Compare commits

...

36 Commits
v1.5.0 ... main

Author SHA1 Message Date
Navid Yaghoobi 66fc1cc64f
Merge pull request #623 from navidys/remote_connections
Fixbug invalid error when default remote connection is not defined
2025-06-15 11:52:18 +10:00
Navid Yaghoobi 04b6c47895 Bugfix invalid error when default remote connection is not defined
Signed-off-by: Navid Yaghoobi <navidys@fedoraproject.org>
2025-06-15 11:49:48 +10:00
Navid Yaghoobi fe095fd4b3
Merge pull request #622 from navidys/podman_remote_connections
Add feature to use podman remote connections config if exist
2025-06-14 20:36:28 +10:00
Navid Yaghoobi a2eb9b4823 Add feature to use podman remote connections config if exist
Signed-off-by: Navid Yaghoobi <navidys@fedoraproject.org>
2025-06-14 20:26:21 +10:00
Navid Yaghoobi d6ba7488b2
Merge pull request #621 from navidys/podman_remote_connections
Using json format for app config file
2025-06-14 10:34:51 +10:00
Navid Yaghoobi 58b4fbdc03 Using json format for app config file
Signed-off-by: Navid Yaghoobi <navidys@fedoraproject.org>
2025-06-14 10:28:38 +10:00
Navid Yaghoobi 7f9559b3ac
Merge pull request #620 from navidys/packit
packit - set rpm build for fedora-all
2025-06-11 19:27:33 +10:00
Navid Yaghoobi 9ed811d204 packit - set rpm build for fedora-all
Signed-off-by: Navid Yaghoobi <navidys@fedoraproject.org>
2025-06-11 19:19:24 +10:00
Navid Yaghoobi 31dce751c8
Merge pull request #619 from navidys/pod_create_namespace
Pod create namespace
2025-06-11 19:10:56 +10:00
Navid Yaghoobi 6e59fa69eb Added pod create namespace category
Signed-off-by: Navid Yaghoobi <navidys@fedoraproject.org>
2025-06-11 18:47:59 +10:00
Navid Yaghoobi cd74eeb550 Added pod create namespace category
Signed-off-by: Navid Yaghoobi <navidys@fedoraproject.org>
2025-06-09 20:19:27 +10:00
Navid Yaghoobi 5e89744b23
Merge pull request #618 from navidys/container_create_namespace
Added container create namespace (ipc, userns, uts, ...) category
2025-06-09 19:36:08 +10:00
Navid Yaghoobi 196004fe46 Added container create namespace (ipc, userns, uts, ...) category
Signed-off-by: Navid Yaghoobi <navidys@fedoraproject.org>
2025-06-09 19:30:43 +10:00
Navid Yaghoobi b7e0b93adb
Merge pull request #617 from navidys/bugfix_screenlock
Bugfix - connection list lock
2025-06-09 18:25:19 +10:00
Navid Yaghoobi e3242f37c7 Bugfix - connection list lock
Signed-off-by: Navid Yaghoobi <navidys@fedoraproject.org>
2025-06-09 18:17:35 +10:00
Navid Yaghoobi 897f8ddae1
Merge pull request #615 from navidys/develop
UI code cleanup + set max width for container view image name
2025-06-08 09:02:52 +10:00
Navid Yaghoobi 92b9653353 UI code cleanup + set max with for container view image name
Signed-off-by: Navid Yaghoobi <navidys@fedoraproject.org>
2025-06-08 08:54:39 +10:00
Navid Yaghoobi a89d6d6ef0
Merge pull request #613 from containers/dependabot/go_modules/golang.org/x/crypto-0.39.0
Bump golang.org/x/crypto from 0.38.0 to 0.39.0
2025-06-07 18:42:35 +10:00
Navid Yaghoobi 6cb9178d24
Merge pull request #614 from containers/dependabot/go_modules/github.com/containers/podman/v5-5.5.1
Bump github.com/containers/podman/v5 from 5.5.0 to 5.5.1
2025-06-07 16:46:53 +10:00
dependabot[bot] 5fc3515715
Bump github.com/containers/podman/v5 from 5.5.0 to 5.5.1
Bumps [github.com/containers/podman/v5](https://github.com/containers/podman) from 5.5.0 to 5.5.1.
- [Release notes](https://github.com/containers/podman/releases)
- [Changelog](https://github.com/containers/podman/blob/main/RELEASE_NOTES.md)
- [Commits](https://github.com/containers/podman/compare/v5.5.0...v5.5.1)

---
updated-dependencies:
- dependency-name: github.com/containers/podman/v5
  dependency-version: 5.5.1
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
2025-06-06 20:07:05 +00:00
dependabot[bot] 5a9541f97f
Bump golang.org/x/crypto from 0.38.0 to 0.39.0
Bumps [golang.org/x/crypto](https://github.com/golang/crypto) from 0.38.0 to 0.39.0.
- [Commits](https://github.com/golang/crypto/compare/v0.38.0...v0.39.0)

---
updated-dependencies:
- dependency-name: golang.org/x/crypto
  dependency-version: 0.39.0
  dependency-type: direct:production
  update-type: version-update:semver-minor
...

Signed-off-by: dependabot[bot] <support@github.com>
2025-06-05 20:52:59 +00:00
Navid Yaghoobi 785b690989
Merge pull request #610 from navidys/bump-1.6.0
Bump 1.6.0
2025-05-28 17:45:53 +10:00
Navid Yaghoobi 5ab12464fb Bump to v1.7.0-dev
Signed-off-by: Navid Yaghoobi <navidys@fedoraproject.org>
2025-05-28 17:37:56 +10:00
Navid Yaghoobi 0b65653d80 Bump to v1.6.0
Signed-off-by: Navid Yaghoobi <navidys@fedoraproject.org>
2025-05-28 17:36:53 +10:00
Navid Yaghoobi 6294d7d1ee
Merge pull request #609 from navidys/podman-v5.5.0
Podman v5.5.0
2025-05-27 22:23:04 +10:00
Navid Yaghoobi 136c5f75df Fix golint issue
Signed-off-by: Navid Yaghoobi <navidys@fedoraproject.org>
2025-05-27 20:59:49 +10:00
Navid Yaghoobi b02ec6036f Added container create health log dest, max count and size options
Signed-off-by: Navid Yaghoobi <navidys@fedoraproject.org>
2025-05-27 20:28:22 +10:00
Navid Yaghoobi ef10f184b1 Fix bats test for network connect
Signed-off-by: Navid Yaghoobi <navidys@fedoraproject.org>
2025-05-25 12:19:16 +10:00
Navid Yaghoobi 3eabf12196 Bump github.com/containers/podman/v5 from 5.4.2 to 5.5.0
Signed-off-by: Navid Yaghoobi <navidys@fedoraproject.org>
2025-05-24 11:29:40 +10:00
Navid Yaghoobi 7f02a36e85
Merge pull request #604 from containers/dependabot/go_modules/golang.org/x/net-0.38.0
Bump golang.org/x/net from 0.36.0 to 0.38.0
2025-05-18 17:45:40 +10:00
dependabot[bot] 164ca27d7f
Bump golang.org/x/net from 0.36.0 to 0.38.0
Bumps [golang.org/x/net](https://github.com/golang/net) from 0.36.0 to 0.38.0.
- [Commits](https://github.com/golang/net/compare/v0.36.0...v0.38.0)

---
updated-dependencies:
- dependency-name: golang.org/x/net
  dependency-version: 0.38.0
  dependency-type: indirect
...

Signed-off-by: dependabot[bot] <support@github.com>
2025-04-16 23:18:22 +00:00
Navid Yaghoobi 2a7c6195b4
Merge pull request #602 from navidys/packit_epel10
packit epel10 build
2025-04-11 19:01:14 +10:00
Navid Yaghoobi 70fd604f0f packit epel10 build
Signed-off-by: Navid Yaghoobi <navidys@fedoraproject.org>
2025-04-11 18:51:57 +10:00
Navid Yaghoobi c9527473cf
Merge pull request #600 from containers/dependabot/go_modules/golang.org/x/crypto-0.37.0
Bump golang.org/x/crypto from 0.36.0 to 0.37.0
2025-04-10 12:14:59 +10:00
dependabot[bot] 42293a3a71
Bump golang.org/x/crypto from 0.36.0 to 0.37.0
Bumps [golang.org/x/crypto](https://github.com/golang/crypto) from 0.36.0 to 0.37.0.
- [Commits](https://github.com/golang/crypto/compare/v0.36.0...v0.37.0)

---
updated-dependencies:
- dependency-name: golang.org/x/crypto
  dependency-version: 0.37.0
  dependency-type: direct:production
  update-type: version-update:semver-minor
...

Signed-off-by: dependabot[bot] <support@github.com>
2025-04-07 21:49:06 +00:00
Navid Yaghoobi f902cdff07 Bump to v1.6.0-dev
Signed-off-by: Navid Yaghoobi <navidys@fedoraproject.org>
2025-04-06 15:04:42 +10:00
953 changed files with 38183 additions and 16655 deletions

View File

@ -12,14 +12,17 @@ jobs:
- openssl-devel
- rpkg
targets:
- fedora-latest-stable-x86_64
- fedora-latest-stable-aarch64
- fedora-development-x86_64
- fedora-development-aarch64
- fedora-all-x86_64
- fedora-all-aarch64
- epel-9-x86_64
- epel-9-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

@ -39,7 +39,7 @@ type App struct {
currentPage string
needInitUI bool
fastRefreshChan chan bool
config *config.Config
config config.Config
}
// NewApp returns new app.
@ -73,10 +73,10 @@ func NewApp(name string, version string) *App {
app.secrets = secrets.NewSecrets()
app.system = system.NewSystem()
app.system.SetConnectionListFunc(app.config.ServicesConnections)
app.system.SetConnectionListFunc(app.config.RemoteConnections)
app.system.SetConnectionSetDefaultFunc(func(name string) error {
err := app.config.SetDefaultService(name)
app.system.UpdateConnectionsData()
err := app.config.SetDefaultConnection(name)
app.system.UpdateData()
return err
})

View File

@ -13,7 +13,7 @@ func (app *App) initUI() {
app.initInfoBar()
}
app.system.UpdateConnectionsData()
app.system.UpdateData()
}
func (app *App) initInfoBar() {

View File

@ -71,7 +71,7 @@ func (app *App) refreshNotConnOK() {
app.switchToScreen(app.system.GetTitle())
}
app.system.UpdateConnectionsData()
app.system.UpdateData()
app.needInitUI = true
}

View File

@ -114,7 +114,7 @@ func (app *App) updatePageData(page string) {
switch page {
case app.system.GetTitle():
app.system.UpdateConnectionsData()
app.system.UpdateData()
case app.pods.GetTitle():
app.pods.UpdateData()
case app.containers.GetTitle():

View File

@ -7,7 +7,7 @@ import (
)
const (
appVersion = "1.5.0"
appVersion = "1.7.0-dev"
)
// versionCmd represents the version command.

View File

@ -1,133 +0,0 @@
package config
import (
"fmt"
"net"
"net/url"
"os"
"regexp"
"github.com/containers/podman-tui/ui/utils"
"github.com/pkg/errors"
"github.com/rs/zerolog/log"
)
// Add adds new service connection.
func (c *Config) Add(name string, uri string, identity string) error {
log.Debug().Msgf("config: adding new service %s %s %s", name, uri, identity)
newService, err := validateNewService(name, uri, identity)
if err != nil {
return err
}
if err := c.add(name, newService); err != nil {
return err
}
if err := c.Write(); err != nil {
return err
}
return c.reload()
}
func (c *Config) add(name string, newService Service) error {
c.mu.Lock()
defer c.mu.Unlock()
for serviceName := range c.Services {
if serviceName == name {
return ErrDuplicatedServiceName
}
}
c.Services[name] = newService
return nil
}
// most of codes are from:
// https://github.com/containers/podman/blob/main/cmd/podman/system/connection/add.go.
func validateNewService(name string, dest string, identity string) (Service, error) { //nolint:cyclop
var (
service Service
serviceIdentity string
)
if name == "" {
return service, ErrEmptyServiceName
}
if dest == "" {
return service, ErrEmptyURIDestination
}
if match, err := regexp.Match("^[A-Za-z][A-Za-z0-9+.-]*://", []byte(dest)); err != nil { //nolint:mirror
return service, fmt.Errorf("%w invalid destition", err)
} else if !match {
dest = "ssh://" + dest
}
uri, err := url.Parse(dest)
if err != nil {
return service, err
}
switch uri.Scheme {
case "ssh":
if uri.User.Username() == "" {
if uri.User, err = getUserInfo(uri); err != nil {
return service, err
}
}
serviceIdentity, err = utils.ResolveHomeDir(identity)
if err != nil {
return service, err
}
if identity == "" {
return service, ErrEmptySSHIdentity
}
if uri.Port() == "" {
uri.Host = net.JoinHostPort(uri.Hostname(), "22")
}
if uri.Path == "" || uri.Path == "/" {
if uri.Path, err = getUDS(uri, serviceIdentity); err != nil {
return service, err
}
}
case "unix":
if identity != "" {
return service, fmt.Errorf("%w identity", ErrInvalidUnixSchemaOption)
}
info, err := os.Stat(uri.Path)
switch {
case errors.Is(err, os.ErrNotExist):
log.Warn().Msgf("config: %q does not exists", uri.Path)
case errors.Is(err, os.ErrPermission):
log.Warn().Msgf("config: You do not have permission to read %q", uri.Path)
case err != nil:
return service, err
case info.Mode()&os.ModeSocket == 0:
return service, fmt.Errorf("%w %q", ErrFileNotUnixSocket, uri.Path)
}
case "tcp":
if identity != "" {
return service, fmt.Errorf("%w identity", ErrInvalidTCPSchemaOption)
}
default:
return service, fmt.Errorf("%w %q", ErrInvalidURISchemaName, uri.Scheme)
}
service.Identity = serviceIdentity
service.URI = uri.String()
service.Default = false
return service, nil
}

View File

@ -1,124 +1,43 @@
package config
import (
"errors"
"os"
"sort"
"sync"
"github.com/containers/podman-tui/config/pconfig"
"github.com/containers/podman-tui/config/tconfig"
"github.com/containers/podman-tui/pdcs/registry"
"github.com/rs/zerolog/log"
)
const (
// _configPath is the path to the podman-tui/podman-tui.conf
// inside a given config directory.
_configPath = "podman-tui/podman-tui.conf"
// UserAppConfig holds the user podman-tui config path.
UserAppConfig = ".config/" + _configPath
)
var (
ErrRemotePodmanUDSReport = errors.New("remote podman failed to report its UDS socket")
ErrInvalidURISchemaName = errors.New("invalid schema name")
ErrInvalidTCPSchemaOption = errors.New("invalid option for tcp")
ErrInvalidUnixSchemaOption = errors.New("invalid option for unix")
ErrFileNotUnixSocket = errors.New("not a unix domain socket")
ErrEmptySSHIdentity = errors.New("empty identity field for SSH connection")
ErrEmptyURIDestination = errors.New("empty URI destination")
ErrEmptyServiceName = errors.New("empty service name")
ErrDuplicatedServiceName = errors.New("duplicated service name")
)
// Config contains configuration options for container tools.
type Config struct {
mu sync.Mutex
// Services specify the service destination connections
Services map[string]Service `toml:"services,omitempty"`
type Config interface {
RemoteConnections() []registry.Connection
SetDefaultConnection(name string) error
GetDefaultConnection() registry.Connection
Add(name string, uri string, identity string) error
Remove(name string) error
}
// Service represents remote service destination.
type Service struct {
// URI, required. Example: ssh://root@example.com:22/run/podman/podman.sock
URI string `toml:"uri"`
func NewConfig() (Config, error) { //nolint:ireturn
var cfg Config
// Identity file with ssh key, optional
Identity string `toml:"identity,omitempty"`
// Default if its default service, optional
Default bool `toml:"default,omitempty"`
}
// NewConfig returns new config.
func NewConfig() (*Config, error) {
log.Debug().Msgf("config: new")
path, err := configPath()
pconfig, err := pconfig.NewConfig()
if err != nil {
return nil, err
}
newConfig := &Config{}
if _, err := os.Stat(path); err == nil {
if err := newConfig.readConfigFromFile(path); err != nil {
return nil, err
}
premoteConns := pconfig.RemoteConnections()
if len(premoteConns) > 0 {
cfg = pconfig
} else {
if !os.IsNotExist(err) {
tconfig, err := tconfig.NewConfig()
if err != nil {
return nil, err
}
cfg = tconfig
}
newConfig.addLocalHostIfEmptyConfig()
defaultConn := newConfig.getDefault()
if defaultConn.URI != "" {
defaultConn := cfg.GetDefaultConnection()
if defaultConn.URI != "" && defaultConn.Name != "" {
registry.SetConnection(defaultConn)
}
return newConfig, nil
}
func (c *Config) addLocalHostIfEmptyConfig() {
if len(c.Services) > 0 {
return
}
c.Services = make(map[string]Service)
c.Services["localhost"] = Service{
URI: localNodeUnixSocket(),
Default: true,
}
}
// ServicesConnections returns list of available connections.
func (c *Config) ServicesConnections() []registry.Connection {
conn := make([]registry.Connection, 0)
c.mu.Lock()
defer c.mu.Unlock()
for name, service := range c.Services {
conn = append(conn, registry.Connection{
Name: name,
URI: service.URI,
Identity: service.Identity,
Default: service.Default,
})
}
sort.Sort(connectionListSortedName{conn})
return conn
}
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
return cfg, nil
}

View File

@ -1,56 +0,0 @@
package config
import (
"github.com/containers/podman-tui/pdcs/registry"
"github.com/rs/zerolog/log"
)
// SetDefaultService sets default service name.
func (c *Config) SetDefaultService(name string) error {
log.Debug().Msgf("config: set %s as default service", name)
if err := c.setDef(name); err != nil {
return err
}
if err := c.Write(); err != nil {
return err
}
return c.reload()
}
func (c *Config) setDef(name string) error {
c.mu.Lock()
defer c.mu.Unlock()
for key := range c.Services {
dest := c.Services[key]
dest.Default = false
if key == name {
dest.Default = true
}
c.Services[key] = dest
}
return nil
}
func (c *Config) getDefault() registry.Connection {
c.mu.Lock()
defer c.mu.Unlock()
for name, service := range c.Services {
if service.Default {
return registry.Connection{
Name: name,
Identity: service.Identity,
URI: service.URI,
}
}
}
return registry.Connection{}
}

132
config/pconfig/config.go Normal file
View File

@ -0,0 +1,132 @@
package pconfig
import (
"slices"
"sort"
cconfig "github.com/containers/common/pkg/config"
"github.com/containers/podman-tui/config/utils"
"github.com/containers/podman-tui/pdcs/registry"
"github.com/containers/podman/v5/pkg/domain/entities"
"github.com/rs/zerolog/log"
)
type Config struct {
podmanOptions *entities.PodmanConfig
}
func NewConfig() (*Config, error) {
log.Debug().Msg("config: loading podman remote connections")
newConfig := &Config{}
defaultConfig, err := cconfig.New(&cconfig.Options{
SetDefault: true,
Modules: nil,
})
if err != nil {
return nil, err
}
podmanOptions := entities.PodmanConfig{ContainersConf: &cconfig.Config{}, ContainersConfDefaultsRO: defaultConfig}
newConfig.podmanOptions = &podmanOptions
return newConfig, nil
}
func (c *Config) RemoteConnections() []registry.Connection {
rconn := make([]registry.Connection, 0)
conns, err := c.podmanOptions.ContainersConfDefaultsRO.GetAllConnections()
if err != nil {
log.Err(err).Msgf("config: podman remote connection")
return nil
}
log.Debug().Msgf("connections: %v", conns)
for _, conn := range conns {
rconn = append(rconn, registry.Connection{
Name: conn.Name,
URI: conn.URI,
Identity: conn.Identity,
Default: conn.Default,
})
}
sort.Sort(utils.ConnectionListSortedName{rconn}) //nolint:govet
return rconn
}
func (c *Config) Remove(name string) error {
return cconfig.EditConnectionConfig(func(cfg *cconfig.ConnectionsFile) error {
delete(cfg.Connection.Connections, name)
if cfg.Connection.Default == name {
cfg.Connection.Default = ""
}
// If there are existing farm, remove the deleted connection that might be part of a farm
for k, v := range cfg.Farm.List {
index := slices.Index(v, name)
if index > -1 {
cfg.Farm.List[k] = append(v[:index], v[index+1:]...)
}
}
return nil
})
}
func (c *Config) Add(name string, uri string, identity string) error {
connURI, err := utils.ValidateNewConnection(name, uri, identity)
if err != nil {
return err
}
dst := cconfig.Destination{
URI: connURI,
Identity: identity,
}
return cconfig.EditConnectionConfig(func(cfg *cconfig.ConnectionsFile) error {
if cfg.Connection.Connections == nil {
cfg.Connection.Connections = map[string]cconfig.Destination{
name: dst,
}
cfg.Connection.Default = name
} else {
cfg.Connection.Connections[name] = dst
}
return nil
})
}
func (c *Config) SetDefaultConnection(name string) error {
return cconfig.EditConnectionConfig(func(cfg *cconfig.ConnectionsFile) error {
if _, found := cfg.Connection.Connections[name]; !found {
return utils.ErrConnectionNotFound
}
cfg.Connection.Default = name
return nil
})
}
func (c *Config) GetDefaultConnection() registry.Connection {
for _, conn := range c.RemoteConnections() {
if conn.Default {
return registry.Connection{
Name: conn.Name,
Identity: conn.Identity,
URI: conn.URI,
}
}
}
return registry.Connection{}
}

View File

@ -1,27 +0,0 @@
package config
import "github.com/rs/zerolog/log"
// Remove removes a service from config.
func (c *Config) Remove(name string) error {
log.Debug().Msgf("config: remove service %q", name)
c.remove(name)
if err := c.Write(); err != nil {
return err
}
return c.reload()
}
func (c *Config) remove(name string) {
c.mu.Lock()
defer c.mu.Unlock()
for serviceName := range c.Services {
if serviceName == name {
delete(c.Services, name)
}
}
}

47
config/tconfig/add.go Normal file
View File

@ -0,0 +1,47 @@
package tconfig
import (
"github.com/containers/podman-tui/config/utils"
"github.com/rs/zerolog/log"
)
// Add adds a new remote connection.
func (c *Config) Add(name string, uri string, identity string) error {
log.Debug().Msgf("config: adding new remote connection %s %s %s", name, uri, identity)
connURI, err := utils.ValidateNewConnection(name, uri, identity)
if err != nil {
return err
}
conn := RemoteConnection{
URI: connURI,
Identity: identity,
Default: false,
}
if err := c.add(name, conn); err != nil {
return err
}
if err := c.write(); err != nil {
return err
}
return c.reload()
}
func (c *Config) add(name string, conn RemoteConnection) error {
c.mu.Lock()
defer c.mu.Unlock()
for connName := range c.Connection.Connections {
if connName == name {
return ErrDuplicatedConnectionName
}
}
c.Connection.Connections[name] = conn
return nil
}

103
config/tconfig/config.go Normal file
View File

@ -0,0 +1,103 @@
package tconfig
import (
"errors"
"os"
"sort"
"sync"
"github.com/containers/podman-tui/config/utils"
"github.com/containers/podman-tui/pdcs/registry"
"github.com/rs/zerolog/log"
)
const (
// _configPath is the path to the podman-tui/podman-tui.json
// inside a given config directory.
_configPath = "podman-tui/podman-tui.json"
// UserAppConfig holds the user podman-tui config path.
UserAppConfig = ".config/" + _configPath
)
var ErrDuplicatedConnectionName = errors.New("duplicated connection name")
// Config contains configuration options for container tools.
type Config struct {
mu sync.Mutex
Connection RemoteConnections
}
type RemoteConnections struct {
Connections map[string]RemoteConnection `json:"connections"`
}
type RemoteConnection struct {
// URI, required. Example: ssh://root@example.com:22/run/podman/podman.sock
URI string `json:"uri"`
// Identity file with ssh key, optional
Identity string `json:"identity,omitempty"`
// Default if its default connection, optional
Default bool `json:"default,omitempty"`
}
// NewConfig returns new config.
func NewConfig() (*Config, error) {
path, err := utils.ConfigPath()
if err != nil {
return nil, err
}
log.Debug().Msgf("config: loading from %q", path)
newConfig := &Config{}
newConfig.Connection.Connections = make(map[string]RemoteConnection)
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
}
}
newConfig.addLocalHostIfEmptyConfig()
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)
c.mu.Lock()
defer c.mu.Unlock()
for name, conn := range c.Connection.Connections {
rconn = append(rconn, registry.Connection{
Name: name,
URI: conn.URI,
Identity: conn.Identity,
Default: conn.Default,
})
}
sort.Sort(utils.ConnectionListSortedName{rconn}) //nolint:govet
return rconn
}

56
config/tconfig/default.go Normal file
View File

@ -0,0 +1,56 @@
package tconfig
import (
"github.com/containers/podman-tui/config/utils"
"github.com/containers/podman-tui/pdcs/registry"
"github.com/rs/zerolog/log"
)
// SetDefaultConnection sets default connection.
func (c *Config) SetDefaultConnection(name string) error {
log.Debug().Msgf("config: set %s as default connection", name)
if err := c.setDef(name); err != nil {
return err
}
if err := c.write(); err != nil {
return err
}
return c.reload()
}
func (c *Config) setDef(name string) error {
c.mu.Lock()
defer c.mu.Unlock()
for connName := range c.Connection.Connections {
if connName == name {
dest := c.Connection.Connections[connName]
dest.Default = true
c.Connection.Connections[connName] = dest
return nil
}
}
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

@ -1,10 +1,12 @@
package config
package tconfig
import (
"encoding/json"
"fmt"
"io"
"os"
"github.com/BurntSushi/toml"
"github.com/containers/podman-tui/config/utils"
"github.com/rs/zerolog/log"
)
@ -14,30 +16,23 @@ func (c *Config) readConfigFromFile(path string) error {
c.mu.Lock()
defer c.mu.Unlock()
rawConfig, err := os.ReadFile(path)
cfgFile, err := os.Open(path)
if err != nil {
return fmt.Errorf("config: %w open configuration %q", err, path)
}
cfgData, err := io.ReadAll(cfgFile)
if err != nil {
return fmt.Errorf("config: %w read configuration %q", err, path)
}
config := os.ExpandEnv(string(rawConfig))
meta, err := toml.Decode(config, c)
if err != nil {
return fmt.Errorf("config: %w decode configuration %q", err, path)
}
keys := meta.Undecoded()
if len(keys) > 0 {
log.Debug().Msgf("config: failed to decode the keys %q from %q.", keys, path)
}
return nil
return json.Unmarshal(cfgData, &c.Connection)
}
func (c *Config) reload() error {
log.Debug().Msgf("config: reload configuration")
path, err := configPath()
path, err := utils.ConfigPath()
if err != nil {
return err
}

27
config/tconfig/remove.go Normal file
View File

@ -0,0 +1,27 @@
package tconfig
import "github.com/rs/zerolog/log"
// Remove removes a connection from config.
func (c *Config) Remove(name string) error {
log.Debug().Msgf("config: remove remote connection %q", name)
c.remove(name)
if err := c.write(); err != nil {
return err
}
return c.reload()
}
func (c *Config) remove(name string) {
c.mu.Lock()
defer c.mu.Unlock()
for connName := range c.Connection.Connections {
if connName != name {
delete(c.Connection.Connections, name)
}
}
}

View File

@ -1,21 +1,23 @@
package config
package tconfig
import (
"encoding/json"
"fmt"
"os"
"path/filepath"
"github.com/BurntSushi/toml"
"github.com/containers/podman-tui/config/utils"
"github.com/rs/zerolog/log"
)
// Write writes config.
func (c *Config) Write() error {
func (c *Config) write() error {
var err error
c.mu.Lock()
defer c.mu.Unlock()
path, err := configPath()
path, err := utils.ConfigPath()
if err != nil {
return err
}
@ -33,7 +35,14 @@ func (c *Config) Write() error {
defer configFile.Close()
enc := toml.NewEncoder(configFile)
jsonData, err := json.Marshal(c.Connection)
if err != nil {
return fmt.Errorf("config: configuration json marshal %w", err)
}
return enc.Encode(c)
if _, err := configFile.Write(jsonData); err != nil {
return fmt.Errorf("config: %w write configuration %q", err, path)
}
return nil
}

100
config/utils/conn.go Normal file
View File

@ -0,0 +1,100 @@
package utils
import (
"errors"
"fmt"
"net"
"net/url"
"os"
"regexp"
"github.com/containers/podman-tui/ui/utils"
"github.com/rs/zerolog/log"
)
var (
ErrInvalidURISchemaName = errors.New("invalid schema name")
ErrInvalidTCPSchemaOption = errors.New("invalid option for tcp")
ErrInvalidUnixSchemaOption = errors.New("invalid option for unix")
ErrFileNotUnixSocket = errors.New("not a unix domain socket")
ErrEmptySSHIdentity = errors.New("empty identity field for SSH connection")
ErrEmptyURIDestination = errors.New("empty URI destination")
ErrEmptyConnectionName = errors.New("empty connection name")
ErrConnectionNotFound = errors.New("connection not found")
)
func ValidateNewConnection(name string, dest string, identity string) (string, error) { //nolint:cyclop
var connIdentity string
if name == "" {
return "", ErrEmptyConnectionName
}
if dest == "" {
return "", ErrEmptyURIDestination
}
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
}
uri, err := url.Parse(dest)
if err != nil {
return "", err
}
switch uri.Scheme {
case "ssh":
if uri.User.Username() == "" {
if uri.User, err = getUserInfo(uri); err != nil {
return "", err
}
}
connIdentity, err = utils.ResolveHomeDir(identity)
if err != nil {
return "", err
}
if identity == "" {
return "", ErrEmptySSHIdentity
}
if uri.Port() == "" {
uri.Host = net.JoinHostPort(uri.Hostname(), "22")
}
if uri.Path == "" || uri.Path == "/" {
if uri.Path, err = getUDS(uri, connIdentity); err != nil {
return "", err
}
}
case "unix":
if identity != "" {
return "", fmt.Errorf("%w identity", ErrInvalidUnixSchemaOption)
}
info, err := os.Stat(uri.Path)
switch {
case errors.Is(err, os.ErrNotExist):
log.Warn().Msgf("config: %q does not exists", uri.Path)
case errors.Is(err, os.ErrPermission):
log.Warn().Msgf("config: You do not have permission to read %q", uri.Path)
case err != nil:
return "", err
case info.Mode()&os.ModeSocket == 0:
return "", fmt.Errorf("%w %q", ErrFileNotUnixSocket, uri.Path)
}
case "tcp":
if identity != "" {
return "", fmt.Errorf("%w identity", ErrInvalidTCPSchemaOption)
}
default:
return "", fmt.Errorf("%w %q", ErrInvalidURISchemaName, uri.Scheme)
}
return uri.String(), nil
}

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

@ -1,8 +1,9 @@
package config
package utils
import (
"bytes"
"encoding/json"
"errors"
"fmt"
"net"
"net/url"
@ -19,7 +20,17 @@ import (
"golang.org/x/crypto/ssh/agent"
)
func configPath() (string, error) {
const (
// _configPath is the path to the podman-tui/podman-tui.json
// inside a given config directory.
_configPath = "podman-tui/podman-tui.json"
// UserAppConfig holds the user podman-tui config path.
UserAppConfig = ".config/" + _configPath
)
var ErrRemotePodmanUDSReport = errors.New("remote podman failed to report its UDS socket")
func ConfigPath() (string, error) {
if configHome := os.Getenv("XDG_CONFIG_HOME"); configHome != "" {
return filepath.Join(configHome, _configPath), nil
}
@ -32,8 +43,8 @@ func configPath() (string, error) {
return filepath.Join(home, UserAppConfig), nil
}
// localNodeUnixSocket return local node unix socket file.
func localNodeUnixSocket() string {
// LocalNodeUnixSocket return local node unix socket file.
func LocalNodeUnixSocket() string {
var (
sockDir string
socket string
@ -48,7 +59,7 @@ func localNodeUnixSocket() string {
sockDir = os.Getenv("XDG_RUNTIME_DIR")
}
socket = "unix:" + sockDir + "/podman/podman.sock"
socket = "unix:/" + sockDir + "/podman/podman.sock"
return socket
}

View File

@ -112,22 +112,27 @@ $ podman run -it --name podman-tui-app \
## Configuration Files
### podman-tui.conf
### podman-tui.json
~/.config/podman-tui/podman-tui.conf
~/.config/podman-tui/podman-tui.json
podman-tui.conf is the configuration file which specifies local and remotes podman systems connections details.
podman-tui.json is the configuration file which specifies local and remotes podman systems connections details.
```shell
[services]
[services.fc36node01]
uri = "ssh://navid@fc36node01:22/run/user/1000/podman/podman.sock"
identity = "/home/navid/.ssh/id_ed25519"
[services.fc36node02]
uri = "ssh://navid@fc36node02:22/run/user/1000/podman/podman.sock"
identity = "/home/navid/.ssh/id_ed25519"
default = true
[services.localhost]
uri = "unix://run/user/1000/podman/podman.sock"
{
"connections": {
"f42node01": {
"uri": "ssh://navid@f42node01:22/run/user/1000/podman/podman.sock",
"identity": "/home/navid/.ssh/id_ed25519"
},
"fc42node02": {
"uri": "ssh://navid@f42node02:22/run/user/1000/podman/podman.sock",
"identity": "/home/navid/.ssh/id_ed25519"
},
"localhost": {
"uri": "unix://run/user/1000/podman/podman.sock",
"default": true
}
}
}
```

118
go.mod
View File

@ -1,32 +1,32 @@
module github.com/containers/podman-tui
go 1.23.0
go 1.23.3
require (
github.com/BurntSushi/toml v1.5.0
github.com/containers/buildah v1.39.4
github.com/containers/common v0.62.3
github.com/containers/podman/v5 v5.4.2
github.com/containers/storage v1.57.2
github.com/containers/buildah v1.40.1
github.com/containers/common v0.63.1
github.com/containers/podman/v5 v5.5.1
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.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.22.2
github.com/onsi/gomega v1.36.2
github.com/onsi/ginkgo/v2 v2.23.4
github.com/onsi/gomega v1.37.0
github.com/pkg/errors v0.9.1
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.36.0
golang.org/x/crypto v0.39.0
)
require (
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.12.9 // indirect
github.com/VividCortex/ewma v1.2.0 // indirect
@ -34,50 +34,50 @@ require (
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.3 // indirect
github.com/containerd/cgroups/v3 v3.0.5 // indirect
github.com/containerd/errdefs v1.0.0 // indirect
github.com/containerd/errdefs/pkg v0.3.0 // indirect
github.com/containerd/log v0.1.0 // indirect
github.com/containerd/platforms v1.0.0-rc.1 // indirect
github.com/containerd/stargz-snapshotter/estargz v0.16.3 // indirect
github.com/containerd/typeurl/v2 v2.2.3 // indirect
github.com/containers/image/v5 v5.34.3 // 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
github.com/coreos/go-systemd/v22 v22.5.1-0.20231103132048-7d375ecc2b09 // indirect
github.com/cyberphone/json-canonicalization v0.0.0-20231217050601-ba74d44ecf5f // indirect
github.com/cyphar/filepath-securejoin v0.3.6 // indirect
github.com/cyberphone/json-canonicalization v0.0.0-20241213102144-19d51d7fe467 // indirect
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 v27.5.1+incompatible // indirect
github.com/docker/docker-credential-helpers v0.8.2 // 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.8.0 // 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.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.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.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
github.com/golang/groupcache v0.0.0-20210331224755-41bb18bfe9da // indirect
github.com/golang/groupcache v0.0.0-20241129210726-2c02b8208cf8 // indirect
github.com/golang/protobuf v1.5.4 // indirect
github.com/google/go-cmp v0.6.0 // indirect
github.com/google/go-containerregistry v0.20.2 // indirect
github.com/google/go-cmp v0.7.0 // indirect
github.com/google/go-containerregistry v0.20.3 // indirect
github.com/google/go-intervals v0.0.2 // indirect
github.com/google/pprof v0.0.0-20241210010833-40e02aabc2ad // indirect
github.com/google/pprof v0.0.0-20250403155104-27863c87afa6 // indirect
github.com/google/uuid v1.6.0 // indirect
github.com/gorilla/mux v1.8.1 // indirect
github.com/gorilla/schema v1.4.1 // indirect
@ -87,77 +87,79 @@ require (
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.17.11 // indirect
github.com/klauspost/compress v1.18.0 // indirect
github.com/klauspost/pgzip v1.2.6 // indirect
github.com/kr/fs v0.1.0 // indirect
github.com/letsencrypt/boulder v0.0.0-20240620165639-de9c06129bec // indirect
github.com/lucasb-eyer/go-colorful v1.2.0 // indirect
github.com/mailru/easyjson v0.7.7 // 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
github.com/mattn/go-runewidth v0.0.16 // indirect
github.com/mattn/go-sqlite3 v1.14.24 // indirect
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
github.com/moby/sys/user v0.3.0 // indirect
github.com/moby/sys/user v0.4.0 // indirect
github.com/moby/sys/userns v0.1.0 // indirect
github.com/moby/term v0.5.2 // indirect
github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd // indirect
github.com/modern-go/reflect2 v1.0.2 // indirect
github.com/morikuni/aec v1.0.0 // indirect
github.com/nxadm/tail v1.4.11 // indirect
github.com/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.0 // indirect
github.com/opencontainers/runc v1.2.4 // indirect
github.com/opencontainers/runtime-spec v1.2.0 // indirect
github.com/opencontainers/runtime-tools v0.9.1-0.20241108202711-f7e3563b0271 // indirect
github.com/opencontainers/selinux v1.11.1 // indirect
github.com/opencontainers/image-spec v1.1.1 // 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.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.7 // 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.4 // indirect
github.com/sigstore/rekor v1.3.8 // indirect
github.com/sigstore/sigstore v1.8.12 // indirect
github.com/skeema/knownhosts v1.3.0 // indirect
github.com/sigstore/fulcio v1.6.6 // indirect
github.com/sigstore/protobuf-specs v0.4.1 // 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.20.2 // indirect
github.com/sylabs/sif/v2 v2.21.1 // 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.11.7 // indirect
github.com/vbauerster/mpb/v8 v8.9.1 // indirect
github.com/vbatts/tar-split v0.12.1 // 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/contrib/instrumentation/net/http/otelhttp v0.56.0 // indirect
go.opentelemetry.io/otel v1.31.0 // indirect
go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracehttp v1.31.0 // indirect
go.opentelemetry.io/otel/metric v1.31.0 // indirect
go.opentelemetry.io/otel/trace v1.31.0 // indirect
golang.org/x/exp v0.0.0-20250103183323-7d7fa50e5329 // indirect
golang.org/x/net v0.36.0 // indirect
golang.org/x/sync v0.12.0 // indirect
golang.org/x/sys v0.31.0 // indirect
golang.org/x/term v0.30.0 // indirect
golang.org/x/text v0.23.0 // indirect
golang.org/x/time v0.9.0 // indirect
golang.org/x/tools v0.28.0 // indirect
google.golang.org/genproto/googleapis/rpc v0.0.0-20250102185135-69823020774d // indirect
google.golang.org/grpc v1.69.4 // indirect
google.golang.org/protobuf v1.36.3 // indirect
go.opentelemetry.io/auto/sdk v1.1.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
golang.org/x/net v0.40.0 // indirect
golang.org/x/sync v0.15.0 // indirect
golang.org/x/sys v0.33.0 // indirect
golang.org/x/term v0.32.0 // indirect
golang.org/x/text v0.26.0 // indirect
golang.org/x/time v0.11.0 // indirect
golang.org/x/tools v0.33.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.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.4.0 // indirect
tags.cncf.io/container-device-interface v0.8.0 // indirect
tags.cncf.io/container-device-interface v1.0.1 // indirect
)
replace github.com/opencontainers/runc => github.com/opencontainers/runc v1.1.1-0.20240131200429-02120488a4c0

282
go.sum
View File

@ -38,8 +38,8 @@ github.com/chzyer/test v1.0.0 h1:p3BQDXSxOhOG0P9z6/hGnII4LGiEPOYBhs8asl/fC04=
github.com/chzyer/test v1.0.0/go.mod h1:2JlltgoNkt4TW/z9V/IzDdFaMTM2JPIi26O1pF38GC8=
github.com/client9/misspell v0.3.4/go.mod h1:qj6jICC3Q7zFZvVWo7KLAzC3yx5G7kyvSDkc90ppPyw=
github.com/cncf/udpa/go v0.0.0-20191209042840-269d4d468f6f/go.mod h1:M8M6+tZqaGXZJjfX53e64911xZQV5JYwmTeXPW+k8Sc=
github.com/containerd/cgroups/v3 v3.0.3 h1:S5ByHZ/h9PMe5IOQoN7E+nMc2UcLEM/V48DGDJ9kip0=
github.com/containerd/cgroups/v3 v3.0.3/go.mod h1:8HBe7V3aWGLFPd/k03swSIsGjZhHI2WzJmticMgVuz0=
github.com/containerd/cgroups/v3 v3.0.5 h1:44na7Ud+VwyE7LIoJ8JTNQOa549a8543BmzaJHo6Bzo=
github.com/containerd/cgroups/v3 v3.0.5/go.mod h1:SA5DLYnXO8pTGYiAHXz94qvLQTKfVM5GEVisn4jpins=
github.com/containerd/errdefs v1.0.0 h1:tg5yIfIlQIrxYtu9ajqY42W3lpS19XqdxRQeEwYG8PI=
github.com/containerd/errdefs v1.0.0/go.mod h1:+YBYIdtsnF4Iw6nWZhJcqGSg/dwvV7tyJ/kCkyJ2k+M=
github.com/containerd/errdefs/pkg v0.3.0 h1:9IKJ06FvyNlexW690DXuQNx2KA2cUJXx151Xdx3ZPPE=
@ -52,32 +52,32 @@ 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.39.4 h1:XTL1+N9wJcSAqXUl4ReFK286QWLTIGp44jBqs9Qd2y0=
github.com/containers/buildah v1.39.4/go.mod h1:EPFAYD/27eXceT8shzWxKg+asgorc8nzrjiG9qFCqTk=
github.com/containers/common v0.62.3 h1:aOGryqXfW6aKBbHbqOveH7zB+ihavUN03X/2pUSvWFI=
github.com/containers/common v0.62.3/go.mod h1:3R8kDox2prC9uj/a2hmXj/YjZz5sBEUNrcDiw51S0Lo=
github.com/containers/image/v5 v5.34.3 h1:/cMgfyA4Y7ILH7nzWP/kqpkE5Df35Ek4bp5ZPvJOVmI=
github.com/containers/image/v5 v5.34.3/go.mod h1:MG++slvQSZVq5ejAcLdu4APGsKGMb0YHHnAo7X28fdE=
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.4.2 h1:r1Rv6GiH0YCFS91B+f0DEgEZK8yfzzkjiECVLjLjcuQ=
github.com/containers/podman/v5 v5.4.2/go.mod h1:LpcnC+53Ln+wVP7XlGR54RrK6wDlgbuknb5jS6G2+fE=
github.com/containers/podman/v5 v5.5.1 h1:nFqybTuKX6kLvnQ67hScE0/ihba5k+qHFArXdopnqlI=
github.com/containers/podman/v5 v5.5.1/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.57.2 h1:2roCtTyE9pzIaBDHibK72DTnYkPmwWaq5uXxZdaWK4U=
github.com/containers/storage v1.57.2/go.mod h1:i/Hb4lu7YgFr9G0K6BMjqW0BLJO1sFsnWQwj2UoWCUM=
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=
github.com/cpuguy83/go-md2man/v2 v2.0.6/go.mod h1:oOW0eioCTA6cOiMLiUPZOpcVxMig6NIQQ7OS05n1F4g=
github.com/creack/pty v1.1.18 h1:n56/Zwd5o6whRC5PMGretI4IdRLlmBXYNjScPaBgsbY=
github.com/creack/pty v1.1.18/go.mod h1:MOBLtS5ELjhRRrroQr9kyvTxUAFNvYEK993ew/Vr4O4=
github.com/cyberphone/json-canonicalization v0.0.0-20231217050601-ba74d44ecf5f h1:eHnXnuK47UlSTOQexbzxAZfekVz6i+LKRdj1CU5DPaM=
github.com/cyberphone/json-canonicalization v0.0.0-20231217050601-ba74d44ecf5f/go.mod h1:uzvlm1mxhHkdfqitSA92i7Se+S9ksOn3a3qmv/kyOCw=
github.com/cyphar/filepath-securejoin v0.3.6 h1:4d9N5ykBnSp5Xn2JkhocYDkOpURL/18CYMpo6xB9uWM=
github.com/cyphar/filepath-securejoin v0.3.6/go.mod h1:Sdj7gXlvMcPZsbhwhQ33GguGLDGQL7h7bg04C/+u9jI=
github.com/cyberphone/json-canonicalization v0.0.0-20241213102144-19d51d7fe467 h1:uX1JmpONuD549D73r6cgnxyUu18Zb7yHAy5AYU0Pm4Q=
github.com/cyberphone/json-canonicalization v0.0.0-20241213102144-19d51d7fe467/go.mod h1:uzvlm1mxhHkdfqitSA92i7Se+S9ksOn3a3qmv/kyOCw=
github.com/cyphar/filepath-securejoin v0.4.1 h1:JyxxyPEaktOD+GAnqIqTf9A8tHyAG22rowi7HkoSU1s=
github.com/cyphar/filepath-securejoin v0.4.1/go.mod h1:Sdj7gXlvMcPZsbhwhQ33GguGLDGQL7h7bg04C/+u9jI=
github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
github.com/davecgh/go-spew v1.1.2-0.20180830191138-d8f796af33cc h1:U9qPSI2PIWSS1VwoXQT9A3Wy9MM3WgvqSxFWenqJduM=
@ -86,14 +86,14 @@ 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 v27.5.1+incompatible h1:JB9cieUT9YNiMITtIsguaN55PLOHhBSz3LKVc6cqWaY=
github.com/docker/cli v27.5.1+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 v27.5.1+incompatible h1:4PYU5dnBYqRQi0294d1FBECqT9ECWeQAIfE8q4YnPY8=
github.com/docker/docker v27.5.1+incompatible/go.mod h1:eEKB0N0r5NX/I1kEveEz05bcu8tLC/8azJZsviup8Sk=
github.com/docker/docker-credential-helpers v0.8.2 h1:bX3YxiGzFP5sOXWc3bTPEXdEaZSeVMrFgOr3T+zrFAo=
github.com/docker/docker-credential-helpers v0.8.2/go.mod h1:P3ci7E3lwkZg6XiHdRKft1KckHiO9a2rNtyFbZ/ry9M=
github.com/docker/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=
github.com/docker/go-connections v0.5.0/go.mod h1:ov60Kzw0kKElRwhNs9UlUHAE/F9Fe6GLaXnqyDdmEXc=
github.com/docker/go-metrics v0.0.1 h1:AgB/0SvBxihN0X8OR4SjsblXkbMvalQ8cjmtKQ2rQV8=
@ -107,8 +107,8 @@ github.com/envoyproxy/protoc-gen-validate v0.1.0/go.mod h1:iSmxcyjqTsJpI2R4NaDN7
github.com/felixge/httpsnoop v1.0.4 h1:NFTV2Zj1bL4mc9sqWACXbQFVBBg2W3GPvqp8/ESS2Wg=
github.com/felixge/httpsnoop v1.0.4/go.mod h1:m8KPJKqk1gH5J9DgRY2ASl2lWCfGKXixSwevea8zH2U=
github.com/fsnotify/fsnotify v1.6.0/go.mod h1:sl3t1tCWJFWoRz9R8WJCbQihKKwmorjAbSClcnxKAGw=
github.com/fsnotify/fsnotify v1.8.0 h1:dAwr6QBTBZIkG8roQaJjGof0pp0EeF+tNV7YBP3F/8M=
github.com/fsnotify/fsnotify v1.8.0/go.mod h1:8jBTzvmWwFyi3Pb8djgCCO5IBqzKJ/Jwo8TRcHyHii0=
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=
@ -124,8 +124,8 @@ 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.0 h1:c4xY/OLxUBSTiepAg3j/MHuAv5mJhnf53LLMWFB+u/w=
github.com/go-openapi/errors v0.22.0/go.mod h1:J3DmZScxCDufmIMsdOuDHxJbdOGC0xtUynjIx092vXE=
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=
@ -138,8 +138,8 @@ github.com/go-openapi/spec v0.21.0 h1:LTVzPc3p/RzRnkQqLRndbAzjY0d0BCL72A6j3CdL9Z
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.0 h1:vsEVJDUo2hPJ2tu0/Xc+4noaxyEffXNIs3cOULZ+GrE=
github.com/go-openapi/swag v0.23.0/go.mod h1:esZ8ITTYEsH1V2trKHjAN8Ai7xHb8RV+YSZ577vPjgQ=
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=
@ -154,8 +154,8 @@ github.com/gogo/protobuf v1.3.2 h1:Ov1cvc58UF3b5XjBnZv7+opcTcQFZebYjWzi34vdm4Q=
github.com/gogo/protobuf v1.3.2/go.mod h1:P1XiOD3dCwIKUDQYPy72D8LYyHL2YPYrpS2s69NZV8Q=
github.com/golang/glog v0.0.0-20160126235308-23def4e6c14b/go.mod h1:SBH7ygxi8pfUlaOkMMuAQtPIUF8ecWP5IEl/CR7VP2Q=
github.com/golang/groupcache v0.0.0-20200121045136-8c9f03a8e57e/go.mod h1:cIg4eruTrX1D+g88fzRXU5OdNfaM+9IcxsU14FzY7Hc=
github.com/golang/groupcache v0.0.0-20210331224755-41bb18bfe9da h1:oI5xCqsCo564l8iNU+DwB5epxmsaqB+rhGL0m5jtYqE=
github.com/golang/groupcache v0.0.0-20210331224755-41bb18bfe9da/go.mod h1:cIg4eruTrX1D+g88fzRXU5OdNfaM+9IcxsU14FzY7Hc=
github.com/golang/groupcache v0.0.0-20241129210726-2c02b8208cf8 h1:f+oWsMOmNPc8JmEHVZIycC7hBoQxHH9pNKQORJNozsQ=
github.com/golang/groupcache v0.0.0-20241129210726-2c02b8208cf8/go.mod h1:wcDNUvekVysuuOpQKo3191zZyTpiI6se1N1ULghS0sw=
github.com/golang/mock v1.1.1/go.mod h1:oTYuIxOrZwtPieC+H1uAHpcLFnEyAGVDL/k47Jfbm0A=
github.com/golang/protobuf v1.2.0/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U=
github.com/golang/protobuf v1.3.2/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U=
@ -175,15 +175,16 @@ github.com/google/go-cmp v0.4.0/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/
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 h1:ofyhxvXcZhMsU5ulbFiLKl/XBFqE1GSq7atu8tAmTRI=
github.com/google/go-cmp v0.6.0/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeNGIjoY=
github.com/google/go-containerregistry v0.20.2 h1:B1wPJ1SN/S7pB+ZAimcciVD+r+yV/l/DSArMxlbwseo=
github.com/google/go-containerregistry v0.20.2/go.mod h1:z38EKdKh4h7IP2gSfUUqEvalZBqs6AoLeWfUy34nQC8=
github.com/google/go-cmp v0.7.0 h1:wk8382ETsv4JYUZwIsn6YpYiWiBsYLSJiTsyBybVuN8=
github.com/google/go-cmp v0.7.0/go.mod h1:pXiqmnSA92OHEEa9HXL2W4E7lf9JzCmGVUdgjX3N/iU=
github.com/google/go-containerregistry v0.20.3 h1:oNx7IdTI936V8CQRveCjaxOiegWwvM7kqkbXTpyiovI=
github.com/google/go-containerregistry v0.20.3/go.mod h1:w00pIgBRDVUDFM6bq+Qx8lwNWK+cxgCuX1vd3PIBDNI=
github.com/google/go-intervals v0.0.2 h1:FGrVEiUnTRKR8yE04qzXYaJMtnIYqobR5QbblK3ixcM=
github.com/google/go-intervals v0.0.2/go.mod h1:MkaR3LNRfeKLPmqgJYs4E66z5InYjmCjbbr4TQlcT6Y=
github.com/google/gofuzz v1.0.0/go.mod h1:dBl0BpW6vV/+mYPU4Po3pmUjxk6FQPldtuIdl/M65Eg=
github.com/google/pprof v0.0.0-20241210010833-40e02aabc2ad h1:a6HEuzUHeKH6hwfN/ZoQgRgVIWFJljSWa/zetS2WTvg=
github.com/google/pprof v0.0.0-20241210010833-40e02aabc2ad/go.mod h1:vavhavw2zAxS5dIdcRluK6cSGGPlZynqzFM8NdvU144=
github.com/google/pprof v0.0.0-20250403155104-27863c87afa6 h1:BHT72Gu3keYf3ZEu2J0b1vyeLSOYI8bm5wbJM/8yDe8=
github.com/google/pprof v0.0.0-20250403155104-27863c87afa6/go.mod h1:boTsfXsheKC2y+lKOCMpSfarhxDeIzfZG1jqGcPl3cA=
github.com/google/uuid v1.1.2/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo=
github.com/google/uuid v1.2.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo=
github.com/google/uuid v1.6.0 h1:NIvaJDMOsjHA8n1jAhLSgzrAzy1Hgr+hNrb57e+94F0=
@ -192,8 +193,8 @@ github.com/gorilla/mux v1.8.1 h1:TuBL49tXwgrFYWhqrNgrUNEY92u81SPhu7sTdzQEiWY=
github.com/gorilla/mux v1.8.1/go.mod h1:AKf9I4AEqPTmMytcMc0KkNouC66V3BtZ4qD5fmWSiMQ=
github.com/gorilla/schema v1.4.1 h1:jUg5hUjCSDZpNGLuXQOgIWGdlgrIdYvgQ0wZtdK1M3E=
github.com/gorilla/schema v1.4.1/go.mod h1:Dg5SSm5PV60mhF2NFaTV1xuYYj8tV8NOPRo4FggUMnM=
github.com/grpc-ecosystem/grpc-gateway/v2 v2.22.0 h1:asbCHRVmodnJTuQ3qamDwqVOIjwqUPTYmYuemVOx+Ys=
github.com/grpc-ecosystem/grpc-gateway/v2 v2.22.0/go.mod h1:ggCgvZ2r7uOoQjOyu2Y1NhHmEPPzzuhWgcza5M1Ji1I=
github.com/grpc-ecosystem/grpc-gateway/v2 v2.25.1 h1:VNqngBF40hVlDloBruUehVYC3ArSgIyScOAyMRqBxRg=
github.com/grpc-ecosystem/grpc-gateway/v2 v2.25.1/go.mod h1:RBRO7fro65R6tjKzYgLAFo0t1QEXY1Dp+i/bvpRiqiQ=
github.com/hashicorp/errwrap v1.0.0/go.mod h1:YH+1FKiLXxHSkmPseP+kNlulaMuP3n2brvKWEqk/Jc4=
github.com/hashicorp/errwrap v1.1.0 h1:OxrOeh75EUXMY8TBjag2fzXGZ40LB6IKw45YeGUDY2I=
github.com/hashicorp/errwrap v1.1.0/go.mod h1:YH+1FKiLXxHSkmPseP+kNlulaMuP3n2brvKWEqk/Jc4=
@ -215,8 +216,8 @@ github.com/kevinburke/ssh_config v1.2.0 h1:x584FjTGwHzMwvHx18PXxbBVzfnxogHaAReU4
github.com/kevinburke/ssh_config v1.2.0/go.mod h1:CT57kijsi8u/K/BOFA39wgDQJ9CxiF4nAY/ojJ6r6mM=
github.com/kisielk/errcheck v1.5.0/go.mod h1:pFxgyoBC7bSaBwPgfKdkLd5X25qrDl4LWUI2bnpBCr8=
github.com/kisielk/gotool v1.0.0/go.mod h1:XhKaO+MFFWcvkIS/tQcRk01m1F5IRFswLeQ+oQHNcck=
github.com/klauspost/compress v1.17.11 h1:In6xLpyWOi1+C7tXUUWv2ot1QvBjxevKAaI6IXrJmUc=
github.com/klauspost/compress v1.17.11/go.mod h1:pMDklpSncoRMuLFrf1W9Ss9KT+0rH90U12bZKk7uwG0=
github.com/klauspost/compress v1.18.0 h1:c/Cqfb0r+Yi+JtIEq73FWXVkRonBlf0CRNYc8Zttxdo=
github.com/klauspost/compress v1.18.0/go.mod h1:2Pp+KzxcywXVXMr50+X0Q/Lsb43OQHYWRCY2AiWywWQ=
github.com/klauspost/pgzip v1.2.6 h1:8RXeL5crjEUFnR2/Sn6GJNWtSQ3Dk8pq4CL3jvdDyjU=
github.com/klauspost/pgzip v1.2.6/go.mod h1:Ch1tH69qFZu15pkjo5kYi6mth2Zzwzt50oCQKQE9RUs=
github.com/kr/fs v0.1.0 h1:Jskdu9ieNAYnjxsi0LbQp1ulIKZV1LAFgK1tWhpZgl8=
@ -229,8 +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.7.7 h1:UGYAvKxe3sBsEDzO8ZeWOSlIQfWFlxbzLZe7hwFURr0=
github.com/mailru/easyjson v0.7.7/go.mod h1:xzfreul335JAWq5oZzymOObrkdz5UnU4kGfJJLY9Nlc=
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=
@ -242,8 +243,8 @@ github.com/mattn/go-isatty v0.0.20/go.mod h1:W+V8PltTTMOvKvAeJH7IuucS94S2C6jfK/D
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.24 h1:tpSp2G2KyMnnQu99ngJ47EIkWVmliIizyZBfPrBWDRM=
github.com/mattn/go-sqlite3 v1.14.24/go.mod h1:Uh1q+B4BYcTPb+yiD3kU8Ct7aC0hY9fxUwlHK0RXw+Y=
github.com/mattn/go-sqlite3 v1.14.28 h1:ThEiQrnbtumT+QMknw63Befp/ce/nUPgBPMlRFEum7A=
github.com/mattn/go-sqlite3 v1.14.28/go.mod h1:Uh1q+B4BYcTPb+yiD3kU8Ct7aC0hY9fxUwlHK0RXw+Y=
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=
@ -252,12 +253,18 @@ github.com/mitchellh/mapstructure v1.5.0 h1:jeMsZIYE/09sWLaz43PL7Gy6RuMjD2eJVyua
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=
github.com/moby/sys/atomicwriter v0.1.0/go.mod h1:Ul8oqv2ZMNHOceF643P6FKPXeCmYtlQMvpizfsSoaWs=
github.com/moby/sys/capability v0.4.0 h1:4D4mI6KlNtWMCM1Z/K0i7RV1FkX+DBDHKVJpCndZoHk=
github.com/moby/sys/capability v0.4.0/go.mod h1:4g9IK291rVkms3LKCDOoYlnV8xKwoDTpIrNEE35Wq0I=
github.com/moby/sys/mountinfo v0.7.2 h1:1shs6aH5s4o5H2zQLn796ADW1wMrIwHsyJ2v9KouLrg=
github.com/moby/sys/mountinfo v0.7.2/go.mod h1:1YOa8w8Ih7uW0wALDUgT1dTTSBrZ+HiBLGws92L2RU4=
github.com/moby/sys/user v0.3.0 h1:9ni5DlcW5an3SvRSx4MouotOygvzaXbaSrc/wGDFWPo=
github.com/moby/sys/user v0.3.0/go.mod h1:bG+tYYYJgaMtRKgEmuueC0hJEAZWwtIbZTB+85uoHjs=
github.com/moby/sys/sequential v0.6.0 h1:qrx7XFUd/5DxtqcoH1h438hF5TmOvzC/lspjy7zgvCU=
github.com/moby/sys/sequential v0.6.0/go.mod h1:uyv8EUTrca5PnDsdMGXhZe6CCe8U/UiTWd+lL+7b/Ko=
github.com/moby/sys/user v0.4.0 h1:jhcMKit7SA80hivmFJcbB1vqmw//wU61Zdui2eQXuMs=
github.com/moby/sys/user v0.4.0/go.mod h1:bG+tYYYJgaMtRKgEmuueC0hJEAZWwtIbZTB+85uoHjs=
github.com/moby/sys/userns v0.1.0 h1:tVLXkFOxVu9A64/yh59slHVv9ahO9UIev4JZusOLG/g=
github.com/moby/sys/userns v0.1.0/go.mod h1:IHUYgu/kao6N8YZlp9Cf444ySSvCmDlmzUcYfDHOl28=
github.com/moby/term v0.5.2 h1:6qk3FJAFDs6i/q3W/pQ97SX192qKfZgGjCQqfCJkgzQ=
github.com/moby/term v0.5.2/go.mod h1:d3djjFCrjnB+fl8NJux+EJzu0msscUP+f8it8hPkFLc=
github.com/modern-go/concurrent v0.0.0-20180228061459-e0a39a4cb421/go.mod h1:6dJC0mAP4ikYIbvyc7fijjWJddQyLn8Ig3JB5CqoB9Q=
@ -275,40 +282,44 @@ 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.22.2 h1:/3X8Panh8/WwhU/3Ssa6rCKqPLuAkVY2I0RoyDLySlU=
github.com/onsi/ginkgo/v2 v2.22.2/go.mod h1:oeMosUL+8LtarXBHu/c0bx2D/K9zyQ6uX3cTyztHwsk=
github.com/onsi/gomega v1.36.2 h1:koNYke6TVk6ZmnyHrCXba/T/MoLBXFjeC1PtvYgw0A8=
github.com/onsi/gomega v1.36.2/go.mod h1:DdwyADRjrc825LhMEkD76cHR5+pUnjhUN8GlHlRPHzY=
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.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.0 h1:8SG7/vwALn54lVB/0yZ/MMwhFrPYtpEHQb2IpWsCzug=
github.com/opencontainers/image-spec v1.1.0/go.mod h1:W4s4sFTMaBeK1BQLXbG4AdM2szdn85PY75RI83NrTrM=
github.com/opencontainers/runc v1.1.1-0.20240131200429-02120488a4c0 h1:NwSQ/5rex97Rum/xZOMjlDQbbZ8YJKOTihf9sxqHxtE=
github.com/opencontainers/runc v1.1.1-0.20240131200429-02120488a4c0/go.mod h1:tBsQqk9ETVlXxzXjk2Xh/1VjxC/U3Gaq5ps/rC/cadE=
github.com/opencontainers/runtime-spec v1.2.0 h1:z97+pHb3uELt/yiAWD691HNHQIF07bE7dzrbT927iTk=
github.com/opencontainers/runtime-spec v1.2.0/go.mod h1:jwyrGlmzljRJv/Fgzds9SsS/C5hL+LL3ko9hs6T5lQ0=
github.com/opencontainers/runtime-tools v0.9.1-0.20241108202711-f7e3563b0271 h1:TPj0pMLCTy1CKwmrat3hqTxoZfqOuTy0asG0ccpGk8Q=
github.com/opencontainers/runtime-tools v0.9.1-0.20241108202711-f7e3563b0271/go.mod h1:oIH6VwKkaDOO+SIYZpdwrC/0wKYqrfO6E1sG1j3UVws=
github.com/opencontainers/selinux v1.11.1 h1:nHFvthhM0qY8/m+vfhJylliSshm8G1jJ2jDMcgULaH8=
github.com/opencontainers/selinux v1.11.1/go.mod h1:E5dMC3VPuVvVHDYmi78qvhJp8+M586T4DlDRYpFkyec=
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.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.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.7 h1:uv+I3nNJvlKZIQGSr8JVQLNHFU9YhhNpvC14Y6KgmSM=
github.com/pkg/sftp v1.13.7/go.mod h1:KMKI0t3T6hfA+lTR/ssZdunHo+uwq7ghoN09/FSu3DY=
github.com/pkg/sftp v1.13.9 h1:4NGkvGudBL7GteO3m6qnaQ4pC0Kvf0onSVc9gR3EWBw=
github.com/pkg/sftp v1.13.9/go.mod h1:OBN7bVXdstkFFN/gdnHPUb5TE8eb8G1Rp9wCItqjkkA=
github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
github.com/pmezard/go-difflib v1.0.1-0.20181226105442-5d4384ee4fb2 h1:Jamvg5psRIccs7FGNTlIRMkT8wgtp5eCXdBlqhYGL6U=
github.com/pmezard/go-difflib v1.0.1-0.20181226105442-5d4384ee4fb2/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
github.com/prashantv/gostub v1.1.0 h1:BTyx3RfQjRHnUWaGF9oQos79AlQ5k8WNktv7VGvVH4g=
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.20.5 h1:cxppBPuYhUnsO6yo/aoRol4L7q7UFfdm+bR9r+8l63Y=
github.com/prometheus/client_golang v1.20.5/go.mod h1:PIEt8X02hGcP8JWbeHyeZ53Y/jReSnHgO035n//V5WE=
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.1 h1:ZKSh/rekM+n3CeS952MLRAdFwIKqeY8b62p8ais2e9E=
github.com/prometheus/client_model v0.6.1/go.mod h1:OrxVMOVHjw3lKMa8+x6HeMGkHMQyHDk9E3jmP2AmGiY=
github.com/prometheus/common v0.57.0 h1:Ro/rKjwdq9mZn1K5QPctzh+MA4Lp0BuYk5ZZEVhoNcY=
github.com/prometheus/common v0.57.0/go.mod h1:7uRPFSUTbfZWsJ7MHY56sqt7hLQu3bxXHDnNhl8E9qI=
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.0.0-20220307222120-9994674d60a8 h1:xe+mmCnDN82KhC010l3NfYlA8ZbOuzbXAzSYBa6wbMc=
@ -317,28 +328,32 @@ github.com/rivo/uniseg v0.2.0/go.mod h1:J6wj4VEh+S6ZtnVlnTBMWIodfgj8LQOQFoIToxlJ
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.12.0 h1:exVL4IDcn6na9z1rAb56Vxr+CgyK3nn3O+epU5NdKM8=
github.com/rogpeppe/go-internal v1.12.0/go.mod h1:E+RYuTGaKKdloAfM02xzb0FW3Paa99yedzYV+kq4uf4=
github.com/rogpeppe/go-internal v1.13.1 h1:KvO1DLK/DRN07sQ1LQKScxyZJuNnedQ5/wKSR38lUII=
github.com/rogpeppe/go-internal v1.13.1/go.mod h1:uMEvuHeurkdAXX61udpOXGD/AzZDWNMNyH2VO9fmH0o=
github.com/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.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=
github.com/secure-systems-lab/go-securesystemslib v0.9.0/go.mod h1:DVHKMcZ+V4/woA/peqr+L0joiRXbPpQ042GgJckkFgw=
github.com/sergi/go-diff v1.3.1 h1:xkr+Oxo4BOQKmkn/B9eMK0g5Kg/983T9DqqPHwYqD+8=
github.com/sergi/go-diff v1.3.1/go.mod h1:aMJSSKb2lpPvRNec0+w3fl7LP9IOFzdc9Pa4NFbPK1I=
github.com/sigstore/fulcio v1.6.4 h1:d86obfxUAG3Y6CYwOx1pdwCZwKmROB6w6927pKOVIRY=
github.com/sigstore/fulcio v1.6.4/go.mod h1:Y6bn3i3KGhXpaHsAtYP3Z4Np0+VzCo1fLv8Ci6mbPDs=
github.com/sigstore/rekor v1.3.8 h1:B8kJI8mpSIXova4Jxa6vXdJyysRxFGsEsLKBDl0rRjA=
github.com/sigstore/rekor v1.3.8/go.mod h1:/dHFYKSuxEygfDRnEwyJ+ZD6qoVYNXQdi1mJrKvKWsI=
github.com/sigstore/sigstore v1.8.12 h1:S8xMVZbE2z9ZBuQUEG737pxdLjnbOIcFi5v9UFfkJFc=
github.com/sigstore/sigstore v1.8.12/go.mod h1:+PYQAa8rfw0QdPpBcT+Gl3egKD9c+TUgAlF12H3Nmjo=
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/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.0 h1:AM+y0rI04VksttfwjkSTNQorvGqmwATnvnAHpSgc0LY=
github.com/skeema/knownhosts v1.3.0/go.mod h1:sPINvnADmT/qYH1kfv+ePMmOBTH6Tbl7b5LvTDjFK7M=
github.com/skeema/knownhosts v1.3.1 h1:X2osQ+RAjK76shCbvhHHHVl3ZlgDm8apHEHFqRjnBY8=
github.com/skeema/knownhosts v1.3.1/go.mod h1:r7KTdC8l4uxWRyK2TpQZ/1o5HaSzh06ePQNxPwTcfiY=
github.com/smallstep/pkcs7 v0.1.1 h1:x+rPdt2W088V9Vkjho4KtoggyktZJlMduZAtRHm68LU=
github.com/smallstep/pkcs7 v0.1.1/go.mod h1:dL6j5AIz9GHjVEBTXtW+QliALcgM19RtXaTeyxI+AfA=
github.com/spf13/cobra v1.9.1 h1:CXSaggrXdbHK9CF+8ywj8Amf7PBRmPCOJugH954Nnlo=
@ -357,8 +372,8 @@ github.com/stretchr/testify v1.8.0/go.mod h1:yNjHg4UonilssWZ8iaSj1OCr/vHnekPRkoO
github.com/stretchr/testify v1.8.1/go.mod h1:w2LPCIKwWwSfY2zedu0+kehJoqGctiVI29o6fzry7u4=
github.com/stretchr/testify v1.10.0 h1:Xv5erBjTwe/5IxqUQTdXv5kgmIvbHo3QQyRwhJsOfJA=
github.com/stretchr/testify v1.10.0/go.mod h1:r2ic/lqez/lEtzL7wO/rwa5dbSLXVDPFyf8C91i36aY=
github.com/sylabs/sif/v2 v2.20.2 h1:HGEPzauCHhIosw5o6xmT3jczuKEuaFzSfdjAsH33vYw=
github.com/sylabs/sif/v2 v2.20.2/go.mod h1:WyYryGRaR4Wp21SAymm5pK0p45qzZCSRiZMFvUZiuhc=
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/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=
@ -367,10 +382,10 @@ github.com/titanous/rocacheck v0.0.0-20171023193734-afe73141d399 h1:e/5i7d4oYZ+C
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.11.7 h1:ixZ93pO/GmvaZw4Vq9OwmfZK/kc2zKdPfu0B+gYqs3U=
github.com/vbatts/tar-split v0.11.7/go.mod h1:eF6B6i6ftWQcDqEn3/iGFRFRo8cBIMSJVOpnNdfTMFA=
github.com/vbauerster/mpb/v8 v8.9.1 h1:LH5R3lXPfE2e3lIGxN7WNWv3Hl5nWO6LRi2B0L0ERHw=
github.com/vbauerster/mpb/v8 v8.9.1/go.mod h1:4XMvznPh8nfe2NpnDo1QTPvW9MVkUhbG90mPWvmOzcQ=
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.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=
@ -384,38 +399,40 @@ go.mongodb.org/mongo-driver v1.14.0 h1:P98w8egYRjYe3XDjxhYJagTokP/H6HzlsnojRgZRd
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/contrib/instrumentation/net/http/otelhttp v0.56.0 h1:UP6IpuHFkUgOQL9FFQFrZ+5LiwhhYRbi7VZSIx6Nj5s=
go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp v0.56.0/go.mod h1:qxuZLtbq5QDtdeSHsS7bcf6EH6uO6jUAgk764zd3rhM=
go.opentelemetry.io/otel v1.31.0 h1:NsJcKPIW0D0H3NgzPDHmo0WW6SptzPdqg/L1zsIm2hY=
go.opentelemetry.io/otel v1.31.0/go.mod h1:O0C14Yl9FgkjqcCZAsE053C13OaddMYr/hz6clDkEJE=
go.opentelemetry.io/otel/exporters/otlp/otlptrace v1.31.0 h1:K0XaT3DwHAcV4nKLzcQvwAgSyisUghWoY20I7huthMk=
go.opentelemetry.io/otel/exporters/otlp/otlptrace v1.31.0/go.mod h1:B5Ki776z/MBnVha1Nzwp5arlzBbE3+1jk+pGmaP5HME=
go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracehttp v1.31.0 h1:lUsI2TYsQw2r1IASwoROaCnjdj2cvC2+Jbxvk6nHnWU=
go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracehttp v1.31.0/go.mod h1:2HpZxxQurfGxJlJDblybejHB6RX6pmExPNe517hREw4=
go.opentelemetry.io/otel/metric v1.31.0 h1:FSErL0ATQAmYHUIzSezZibnyVlft1ybhy4ozRPcF2fE=
go.opentelemetry.io/otel/metric v1.31.0/go.mod h1:C3dEloVbLuYoX41KpmAhOqNriGbA+qqH6PQ5E5mUfnY=
go.opentelemetry.io/otel/sdk v1.31.0 h1:xLY3abVHYZ5HSfOg3l2E5LUj2Cwva5Y7yGxnSW9H5Gk=
go.opentelemetry.io/otel/sdk v1.31.0/go.mod h1:TfRbMdhvxIIr/B2N2LQW2S5v9m3gOQ/08KsbbO5BPT0=
go.opentelemetry.io/otel/sdk/metric v1.31.0 h1:i9hxxLJF/9kkvfHppyLL55aW7iIJz4JjxTeYusH7zMc=
go.opentelemetry.io/otel/sdk/metric v1.31.0/go.mod h1:CRInTMVvNhUKgSAMbKyTMxqOBC0zgyxzW55lZzX43Y8=
go.opentelemetry.io/otel/trace v1.31.0 h1:ffjsj1aRouKewfr85U2aGagJ46+MvodynlQ1HYdmJys=
go.opentelemetry.io/otel/trace v1.31.0/go.mod h1:TXZkRk7SM2ZQLtR6eoAWQFIHPvzQ06FJAsO1tJg480A=
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/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.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=
golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w=
golang.org/x/crypto v0.0.0-20191011191535-87dc89f01550/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI=
golang.org/x/crypto v0.0.0-20200622213623-75b288015ac9/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto=
golang.org/x/crypto v0.0.0-20210921155107-089bfa567519/go.mod h1:GvvjBRRGRdwPK5ydBHafDWAxML/pGHZbMvKqRZ5+Abc=
golang.org/x/crypto v0.13.0/go.mod h1:y6Z2r+Rw4iayiXXAIxJIDAJ1zMW4yaTpebo8fPOliYc=
golang.org/x/crypto v0.17.0/go.mod h1:gCAAfMLgwOJRpTjQ2zCCt2OcSfYMTeZVSRtQlPC7Nq4=
golang.org/x/crypto v0.19.0/go.mod h1:Iy9bg/ha4yyC70EfRS8jz+B6ybOBKMaSxLj6P6oBDfU=
golang.org/x/crypto v0.23.0/go.mod h1:CKFgDieR+mRhux2Lsu27y0fO304Db0wZe70UKqHu0v8=
golang.org/x/crypto v0.30.0/go.mod h1:kDsLvtWBEx7MV9tJOj9bnXsPbxwJQ6csT/x4KIN4Ssk=
golang.org/x/crypto v0.36.0 h1:AnAEvhDddvBdpY+uR+MyHmuZzzNqXSe/GvuDeob5L34=
golang.org/x/crypto v0.36.0/go.mod h1:Y4J0ReaxCR1IMaabaSMugxJES1EpwhBHhv2bDHklZvc=
golang.org/x/crypto v0.31.0/go.mod h1:kDsLvtWBEx7MV9tJOj9bnXsPbxwJQ6csT/x4KIN4Ssk=
golang.org/x/crypto v0.39.0 h1:SHs+kF4LP+f+p14esP5jAoDpHU8Gu/v9lFRK6IT5imM=
golang.org/x/crypto v0.39.0/go.mod h1:L+Xg3Wf6HoL4Bn4238Z6ft6KfEpN0tJGo53AAPC632U=
golang.org/x/exp v0.0.0-20190121172915-509febef88a4/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA=
golang.org/x/exp v0.0.0-20250103183323-7d7fa50e5329 h1:9kj3STMvgqy3YA4VQXBrN7925ICMxD5wzMRcgA30588=
golang.org/x/exp v0.0.0-20250103183323-7d7fa50e5329/go.mod h1:qj5a5QZpwLU2NLQudwIN5koi3beDhSAlJwa67PuM98c=
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=
golang.org/x/lint v0.0.0-20190313153728-d0100b6bd8b3/go.mod h1:6SW0HCj/g11FgYtHlgUYUwCkIfeOF89ocIRzGO/8vkc=
@ -442,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.36.0 h1:vWF2fRbw4qslQsQzgFqZff+BItCvGFQqKzKIzx1rmoA=
golang.org/x/net v0.36.0/go.mod h1:bFmbeoIPfrw4sMHNhb4J9f6+tPziuGjq7Jk/38fxi1I=
golang.org/x/net v0.40.0 h1:79Xs7wF06Gbdcg4kdCCIQArK11Z1hr5POQ6+fIYHNuY=
golang.org/x/net v0.40.0/go.mod h1:y0hY0exeL2Pku80/zKK7tpntoX23cqL3Oa6njdgRtds=
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=
@ -456,8 +473,8 @@ golang.org/x/sync v0.3.0/go.mod h1:FU7BRWz2tNW+3quACPkgCx/L+uEAv1htQ0V83Z9Rj+Y=
golang.org/x/sync v0.6.0/go.mod h1:Czt+wKu1gCyEFDUtn0jG5QVvpJ6rzVqr5aXyt9drQfk=
golang.org/x/sync v0.7.0/go.mod h1:Czt+wKu1gCyEFDUtn0jG5QVvpJ6rzVqr5aXyt9drQfk=
golang.org/x/sync v0.10.0/go.mod h1:Czt+wKu1gCyEFDUtn0jG5QVvpJ6rzVqr5aXyt9drQfk=
golang.org/x/sync v0.12.0 h1:MHc5BpPuC30uJk597Ri8TV3CNZcTLu6B6z4lJy+g6Jw=
golang.org/x/sync v0.12.0/go.mod h1:1dzgHSNfp02xaA81J2MS99Qcpr2w7fw1gpm99rleRqA=
golang.org/x/sync v0.15.0 h1:KWH3jNZsfyT6xfAfKiz6MRNmd46ByHDYaZ7KSkCtdW8=
golang.org/x/sync v0.15.0/go.mod h1:1dzgHSNfp02xaA81J2MS99Qcpr2w7fw1gpm99rleRqA=
golang.org/x/sys v0.0.0-20180830151530-49385e6e1522/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
golang.org/x/sys v0.0.0-20181122145206-62eef0e2fa9b/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
@ -477,13 +494,12 @@ golang.org/x/sys v0.5.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.6.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.8.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.12.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.15.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA=
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.29.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA=
golang.org/x/sys v0.31.0 h1:ioabZlmFYtWhL+TRYpcnNlLwhyxaM9kWTDEmfnprqik=
golang.org/x/sys v0.31.0/go.mod h1:BJP2sWEmIv4KK5OTEluFJCKSidICx8ciO85XgH3Ak8k=
golang.org/x/sys v0.33.0 h1:q3i8TbbEz+JRD9ywIRlyRAQbM0qF7hu24q3teo2hbuw=
golang.org/x/sys v0.33.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=
@ -492,13 +508,12 @@ golang.org/x/term v0.0.0-20210927222741-03fcf44c2211/go.mod h1:jbD1KX2456YbFQfuX
golang.org/x/term v0.5.0/go.mod h1:jMB1sMXY+tzblOD4FWmEbocvup2/aLOaQEp7JmGp78k=
golang.org/x/term v0.8.0/go.mod h1:xPskH00ivmX89bAKVGSKKtLOWNx2+17Eiy94tnKShWo=
golang.org/x/term v0.12.0/go.mod h1:owVbMEjm3cBLCHdkQu9b1opXd4ETQWc3BhuQGKgXgvU=
golang.org/x/term v0.15.0/go.mod h1:BDl952bC7+uMoWR75FIrCDx79TPU9oHkTZ9yRbYOrX0=
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.28.0/go.mod h1:Sw/lC2IAUZ92udQNf3WodGtn4k/XoLyZoh8v/8uiwek=
golang.org/x/term v0.30.0 h1:PQ39fJZ+mfadBm0y5WlL4vlM7Sx1Hgf13sMIY2+QS9Y=
golang.org/x/term v0.30.0/go.mod h1:NYYFdzHoI5wRh/h5tDMdMqCqPJZEuNqVR5xJLd/n67g=
golang.org/x/term v0.32.0 h1:DR4lr0TjUs3epypdhTOkMmuF5CDFJ/8pOnbzMZPQ7bg=
golang.org/x/term v0.32.0/go.mod h1:uZG1FhGx848Sqfsq4/DlJr3xGGsYMu/L5GW4abiaEPQ=
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=
@ -509,10 +524,10 @@ 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.23.0 h1:D71I7dUrlY+VX0gQShAThNGHFxZ13dGLBHQLVl1mJlY=
golang.org/x/text v0.23.0/go.mod h1:/BLNzu4aZCJ1+kcD0DNRotWKage4q2rGVAg4o22unh4=
golang.org/x/time v0.9.0 h1:EsRrnYcQiGH+5FfbgvV4AP7qEZstoyrHB0DzarOQ4ZY=
golang.org/x/time v0.9.0/go.mod h1:3BpzKBy/shNhVucY/MWOyx10tF3SFh9QdLuxbVysPQM=
golang.org/x/text v0.26.0 h1:P42AVeLghgTYr4+xUnTRKDMqpar+PtX7KWuNQL21L8M=
golang.org/x/text v0.26.0/go.mod h1:QK15LZJUUQVJxhz7wXgxSy/CJaTFjd0G+YLonydOVQA=
golang.org/x/time v0.11.0 h1:/bpjEDfN9tkoN/ryeYHnv5hcMlc8ncjMcM4XBk5NWV0=
golang.org/x/time v0.11.0/go.mod h1:CDIdPxbZBQxdj6cxyCIdrNogrJKMJ7pr37NYpMcMDSg=
golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
golang.org/x/tools v0.0.0-20190114222345-bf090417da8b/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
golang.org/x/tools v0.0.0-20190226205152-f727befe758c/go.mod h1:9Yl7xja0Znq3iFh3HoIrodX9oNMXvdceNzlUR8zjMvY=
@ -525,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.28.0 h1:WuB6qZ4RPCQo5aP3WdKZS7i595EdWqWR8vqJTlwTVK8=
golang.org/x/tools v0.28.0/go.mod h1:dcIOrVd3mfQKTgrDVQHqCPMWy6lnhfhtX3hLXYVLfRw=
golang.org/x/tools v0.33.0 h1:4qz2S3zmRxbGIhDIAgjxvFutSvH5EfnsYrRBj0UI0bc=
golang.org/x/tools v0.33.0/go.mod h1:CIJMaWEY88juyUfo7UbgPqbC8rU2OqfAV1h2Qp0oMYI=
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=
@ -536,18 +551,17 @@ google.golang.org/appengine v1.4.0/go.mod h1:xpcJRLb0r/rnEns0DIKYYv+WjYCduHsrkT7
google.golang.org/genproto v0.0.0-20180817151627-c66870c02cf8/go.mod h1:JiN7NxoALGmiZfu7CAH4rXhgtRTLTxftemlI0sWmxmc=
google.golang.org/genproto v0.0.0-20190819201941-24fa4b261c55/go.mod h1:DMBHOl98Agz4BDEuKkezgsaosCRResVns1a3J2ZsMNc=
google.golang.org/genproto v0.0.0-20200526211855-cb27e3aa2013/go.mod h1:NbSheEEYHJ7i3ixzK3sjbqSGDJWnxyFXZblF3eUsNvo=
google.golang.org/genproto v0.0.0-20241118233622-e639e219e697 h1:ToEetK57OidYuqD4Q5w+vfEnPvPpuTwedCNVohYJfNk=
google.golang.org/genproto/googleapis/api v0.0.0-20241209162323-e6fa225c2576 h1:CkkIfIt50+lT6NHAVoRYEyAvQGFM7xEwXUUywFvEb3Q=
google.golang.org/genproto/googleapis/api v0.0.0-20241209162323-e6fa225c2576/go.mod h1:1R3kvZ1dtP3+4p4d3G8uJ8rFk/fWlScl38vanWACI08=
google.golang.org/genproto/googleapis/rpc v0.0.0-20250102185135-69823020774d h1:xJJRGY7TJcvIlpSrN3K6LAWgNFUILlO+OMAqtg9aqnw=
google.golang.org/genproto/googleapis/rpc v0.0.0-20250102185135-69823020774d/go.mod h1:3ENsm/5D1mzDyhpzeRi1NR784I0BcofWBoSc5QqqMK4=
google.golang.org/genproto/googleapis/api v0.0.0-20250303144028-a0af3efb3deb h1:p31xT4yrYrSM/G4Sn2+TNUkVhFCbG9y8itM2S6Th950=
google.golang.org/genproto/googleapis/api v0.0.0-20250303144028-a0af3efb3deb/go.mod h1:jbe3Bkdp+Dh2IrslsFCklNhweNTBgSYanP1UXhJDhKg=
google.golang.org/genproto/googleapis/rpc v0.0.0-20250313205543-e70fdf4c4cb4 h1:iK2jbkWL86DXjEx0qiHcRE9dE4/Ahua5k6V8OWFb//c=
google.golang.org/genproto/googleapis/rpc v0.0.0-20250313205543-e70fdf4c4cb4/go.mod h1:LuRYeWDFV6WOn90g357N17oMCaxpgCnbi/44qJvDn2I=
google.golang.org/grpc v1.19.0/go.mod h1:mqu4LbDTu4XGKhr4mRzUsmM4RtVoemTSY81AxZiDr8c=
google.golang.org/grpc v1.23.0/go.mod h1:Y5yQAOtifL1yxbo5wqy6BxZv8vAUGQwXBOALyacEbxg=
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.69.4 h1:MF5TftSMkd8GLw/m0KM6V8CMOCY6NZ1NQDPGFgbTt4A=
google.golang.org/grpc v1.69.4/go.mod h1:vyjdE6jLBI76dgpDojsFGNaHlxdjXN9ghpnd2o7JGZ4=
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=
@ -557,8 +571,8 @@ google.golang.org/protobuf v1.22.0/go.mod h1:EGpADcykh3NcUnDUJcl1+ZksZNG86OlYog2
google.golang.org/protobuf v1.23.0/go.mod h1:EGpADcykh3NcUnDUJcl1+ZksZNG86OlYog2l/sGQquU=
google.golang.org/protobuf v1.23.1-0.20200526195155-81db48ad09cc/go.mod h1:EGpADcykh3NcUnDUJcl1+ZksZNG86OlYog2l/sGQquU=
google.golang.org/protobuf v1.25.0/go.mod h1:9JNX74DMeImyA3h4bdi1ymwjUzf21/xIlbajtzgsN7c=
google.golang.org/protobuf v1.36.3 h1:82DV7MYdb8anAVi3qge1wSnMDrnKK7ebr+I0hHRN1BU=
google.golang.org/protobuf v1.36.3/go.mod h1:9fA7Ob0pmnwhb644+1+CVWFRbNajQ6iRojtC/QF5bRE=
google.golang.org/protobuf v1.36.6 h1:z1NpPI8ku2WgiWnf+t9wTPsn6eP1L7ksHUlkfLvd9xY=
google.golang.org/protobuf v1.36.6/go.mod h1:jduwjTPXsFjZGTmRluh+L6NjiWu7pchiJ2/5YcXBHnY=
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c h1:Hei/4ADfdWqJk1ZMxUNpqntNwaWcugrBjAiHlqqRiVk=
gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c/go.mod h1:JHkPIbrfpd72SG/EVd6muEfDQjcINNoR0C8j2r3qZ4Q=
@ -567,13 +581,11 @@ gopkg.in/tomb.v1 v1.0.0-20141024135613-dd632973f1e7/go.mod h1:dt/ZhP58zS4L8KSrWD
gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA=
gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
gotest.tools v2.2.0+incompatible h1:VsBPFP1AI068pPrMxtb/S8Zkgf9xEmTLJjfM+P5UIEo=
gotest.tools v2.2.0+incompatible/go.mod h1:DsYFclhRJ6vuDpmuTbkuFWG+y2sxOXAzmJt81HFBacw=
gotest.tools/v3 v3.5.1 h1:EENdUnS3pdur5nybKYIh2Vfgc8IUNBjxDPSjtiJcOzU=
gotest.tools/v3 v3.5.1/go.mod h1:isy3WKz7GK6uNw/sbHzfKBLvlvXwUyV06n6brMxxopU=
gotest.tools/v3 v3.5.2 h1:7koQfIKdy+I8UTetycgUqXWSDwpgv193Ka+qRsmBY8Q=
gotest.tools/v3 v3.5.2/go.mod h1:LtdLGcnqToBH83WByAAi/wiwSFCArdFIUV/xxN4pcjA=
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.4.0 h1:Mk1wCc2gy/F0THH0TAp1QYyJNzRm2KCLy3o5ASXVI5E=
sigs.k8s.io/yaml v1.4.0/go.mod h1:Ejl7/uTz7PSA4eKMyQCUTnhZYNmLIl+5c2lQPGR2BPY=
tags.cncf.io/container-device-interface v0.8.0 h1:8bCFo/g9WODjWx3m6EYl3GfUG31eKJbaggyBDxEldRc=
tags.cncf.io/container-device-interface v0.8.0/go.mod h1:Apb7N4VdILW0EVdEMRYXIDVRZfNJZ+kmEUss2kRRQ6Y=
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

@ -76,6 +76,9 @@ type CreateOptions struct {
HealthStartupRetries string
HealthStartupSuccess string
HealthStartupTimeout string
HealthLogDestination string
HealthMaxLogSize string
HealthMaxLogCount string
Memory string
MemoryReservation string
MemorySwap string
@ -90,6 +93,15 @@ type CreateOptions struct {
CPUSetMems string
SHMSize string
SHMSizeSystemd string
NamespaceCgroup string
NamespacePid string
NamespaceIpc string
NamespaceUser string
NamespaceUts string
NamespaceUidmap string
NamespaceSubuidName string
NamespaceGidmap string
NamespaceSubgidName string
}
// Create creates a new container.
@ -338,6 +350,43 @@ func Create(opts CreateOptions, run bool) ([]string, string, error) { //nolint:c
createOptions.ShmSizeSystemd = opts.SHMSizeSystemd
}
// namespace options
if opts.NamespaceCgroup != "" {
createOptions.CgroupNS = opts.NamespaceCgroup
}
if opts.NamespaceIpc != "" {
createOptions.IPC = opts.NamespaceIpc
}
if opts.NamespacePid != "" {
createOptions.PID = opts.NamespacePid
}
if opts.NamespaceUser != "" {
createOptions.UserNS = opts.NamespaceUser
}
if opts.NamespaceUts != "" {
createOptions.UTS = opts.NamespaceUts
}
if opts.NamespaceUidmap != "" {
createOptions.UIDMap = []string{opts.NamespaceUidmap}
}
if opts.NamespaceSubuidName != "" {
createOptions.SubUIDName = opts.NamespaceSubuidName
}
if opts.NamespaceGidmap != "" {
createOptions.GIDMap = []string{opts.NamespaceGidmap}
}
if opts.NamespaceSubgidName != "" {
createOptions.SubGIDName = opts.NamespaceSubgidName
}
// generate spec
s := specgen.NewSpecGenerator(opts.Name, false)
if err := specgenutil.FillOutSpecGen(s, &createOptions, nil); err != nil {
@ -377,6 +426,7 @@ func containerHealthOptions(createOptions *entities.ContainerCreateOptions, opts
createOptions.HealthTimeout = define.DefaultHealthCheckTimeout
createOptions.StartupHCTimeout = define.DefaultHealthCheckTimeout
createOptions.HealthOnFailure = opts.HealthOnFailure
createOptions.HealthLogDestination = opts.HealthLogDestination
if opts.HealthCmd == "" {
createOptions.HealthCmd = "none"
@ -386,6 +436,26 @@ func containerHealthOptions(createOptions *entities.ContainerCreateOptions, opts
createOptions.HealthCmd = opts.HealthCmd
if opts.HealthMaxLogCount != "" {
logCount, err := strconv.ParseUint(opts.HealthMaxLogCount, 10, 32)
if err != nil {
return err
}
logCountWd := uint(logCount)
createOptions.HealthMaxLogCount = logCountWd
}
if opts.HealthMaxLogSize != "" {
logSize, err := strconv.ParseUint(opts.HealthMaxLogSize, 10, 32)
if err != nil {
return err
}
logSizeWd := uint(logSize)
createOptions.HealthMaxLogSize = logSizeWd
}
if opts.HealthInterval != "" {
createOptions.HealthInterval = opts.HealthInterval
}

View File

@ -20,34 +20,42 @@ import (
// CreateOptions implements pods create spec options.
type CreateOptions struct {
Name string
NoHost bool
Labels map[string]string
DNSServer []string
DNSOptions []string
DNSSearchDomain []string
Infra bool
InfraCommand string
InfraImage string
Hostname string
IPAddress string
MacAddress string
AddHost []string
Network string
Publish []string
SecurityOpts []string
Memory string
MemorySwap string
CPUs string
CPUShares string
CPUSetCPUs string
CPUSetMems string
ShmSize string
ShmSizeSystemd string
Name string
NoHost bool
Labels map[string]string
DNSServer []string
DNSOptions []string
DNSSearchDomain []string
Infra bool
InfraCommand string
InfraImage string
Hostname string
IPAddress string
MacAddress string
AddHost []string
Network string
Publish []string
SecurityOpts []string
Memory string
MemorySwap string
CPUs string
CPUShares string
CPUSetCPUs string
CPUSetMems string
ShmSize string
ShmSizeSystemd string
NamespaceShare []string
NamespacePid string
NamespaceUser string
NamespaceUts string
NamespaceUidmap string
NamespaceSubuidName string
NamespaceGidmap string
NamespaceSubgidName string
}
// Create creates a new pod.
func Create(opts CreateOptions) error { //nolint:cyclop,gocognit
func Create(opts CreateOptions) error { //nolint:cyclop,gocognit,gocyclo
log.Debug().Msgf("pdcs: podman pod create %v", opts)
var createOptions entities.PodCreateOptions
@ -66,6 +74,7 @@ func Create(opts CreateOptions) error { //nolint:cyclop,gocognit
createOptions.Name = opts.Name
createOptions.Labels = opts.Labels
createOptions.Infra = opts.Infra
// resources
if opts.Memory != "" {
@ -110,14 +119,50 @@ func Create(opts CreateOptions) error { //nolint:cyclop,gocognit
infraOptions.ShmSizeSystemd = opts.ShmSizeSystemd
}
// namespace
if len(opts.NamespaceShare) > 0 {
createOptions.Share = opts.NamespaceShare
}
if opts.NamespacePid != "" {
createOptions.Pid = opts.NamespacePid
}
if opts.NamespaceUser != "" {
userns, err := specgen.ParseUserNamespace(opts.NamespaceUser)
if err != nil {
return err
}
createOptions.Userns = userns
}
if opts.NamespaceUts != "" {
createOptions.Uts = opts.NamespaceUts
}
if opts.NamespaceUidmap != "" {
infraOptions.UIDMap = []string{opts.NamespaceUidmap}
}
if opts.NamespaceSubuidName != "" {
infraOptions.SubUIDName = opts.NamespaceSubuidName
}
if opts.NamespaceGidmap != "" {
infraOptions.GIDMap = []string{opts.NamespaceGidmap}
}
if opts.NamespaceSubgidName != "" {
infraOptions.SubGIDName = opts.NamespaceSubgidName
}
// network options
podNetworkOptions, err := podNetworkOptions(opts)
if err != nil {
return err
}
createOptions.Infra = opts.Infra
if createOptions.Infra { //nolint:nestif
if opts.InfraImage != "" {
createOptions.InfraImage = opts.InfraImage

View File

@ -15,8 +15,8 @@
%global git0 https://%{import_path}
Name: podman-tui
Version: 1.5.0
Release: 1%{?dist}
Version: 1.7.0
Release: dev.1%{?dist}
Summary: Podman Terminal User Interface
License: ASL 2.0
URL: %{git0}
@ -60,6 +60,22 @@ install -p ./bin/%{name} %{buildroot}%{_bindir}
%{_bindir}/%{name}
%changelog
* Wed May 28 2025 Navid Yaghoobi <navidys@fedoraproject.org> 1.7.0-dev-1
* Wed May 28 2025 Navid Yaghoobi <navidys@fedoraproject.org> 1.6.0-1
- Added container create health log dest, max count and size options
- Set default values for container create/run health log options
- Fix bats test for network connect
- Running golang-lint
- Bump github.com/containers/podman/v5 from 5.4.2 to 5.5.0
- Bump github.com/containers/buildah from 1.39.4 to 1.40.0
- Bump github.com/containers/common from 0.62.3 to 0.63.0
- Bump github.com/containers/storage from 1.57.2 to 1.58.0
- Bump github.com/onsi/ginkgo/v2 from 2.22.2 to 2.23.4
- Bump github.com/onsi/gomega from 1.36.2 to 1.37.0
- Bump golang.org/x/net from 0.36.0 to 0.38.0
- Bump golang.org/x/crypto from 0.36.0 to 0.38.0
* Sun Apr 06 2025 Navid Yaghoobi <navidys@fedoraproject.org> 1.5.0-1
- Go update to v1.23.0 + Golangci-lint update to v1.64.4
- UI input check trim spaces

View File

@ -19,7 +19,7 @@ load helpers_tui
podman_tui_set_view "networks"
podman_tui_select_network_cmd "connect"
sleep $TEST_TIMEOUT_LOW
podman_tui_send_inputs "Tab" "Tab"
podman_tui_send_inputs "Tab"
podman_tui_send_inputs $TEST_NETWORK_CONNECT_ALIAS
podman_tui_send_inputs "Tab" "Tab" "Tab" "Tab"
podman_tui_send_inputs "Tab" "Enter"
@ -40,7 +40,7 @@ load helpers_tui
podman_tui_set_view "networks"
podman_tui_select_network_cmd "disconnect"
sleep $TEST_TIMEOUT_LOW
podman_tui_send_inputs "Tab" "Tab" "Tab" "Enter"
podman_tui_send_inputs "Tab" "Tab" "Enter"
run_helper podman container inspect $TEST_CONTAINER_NAME --format "{{ .NetworkSettings.Networks.$TEST_NETWORK_CONNECT }}"
assert "$output" == "<no value>" "expected $TEST_NETWORK_CONNECT_ALIAS to be removed from container"

View File

@ -227,15 +227,11 @@ load helpers_tui
# switch to "health check" create view
podman_tui_send_inputs "Down" "Down" "Down" "Down" "Tab"
podman_tui_send_inputs $TEST_CONTAINER_HEALTH_CMD "Tab" "Tab"
podman_tui_send_inputs $TEST_CONTAINER_HEALTH_CMD "Tab" "Tab" "Tab" "Tab" "Tab"
podman_tui_send_inputs "Enter" "Down" "Down" "Enter"
podman_tui_send_inputs "Tab" "Tab" "Tab"
podman_tui_send_inputs $TEST_CONTAINER_HEALTH_INTERVAL
podman_tui_send_inputs "Tab" "Tab"
podman_tui_send_inputs $TEST_CONTAINER_HEALTH_RETRIES
podman_tui_send_inputs "Tab" "Tab"
podman_tui_send_inputs $TEST_CONTAINER_HEALTH_TIMEOUT
podman_tui_send_inputs "Tab" "Tab" "Tab"
podman_tui_send_inputs "Tab" "Tab" "Tab" "Tab"
sleep $TEST_TIMEOUT_LOW
podman_tui_send_inputs "Tab"
sleep $TEST_TIMEOUT_LOW

View File

@ -20,9 +20,8 @@ load helpers_tui
podman_tui_send_inputs "Tab" "Tab" "Tab" "Enter"
sleep $TEST_TIMEOUT_LOW
run_helper tail -2 $PODMAN_TUI_CONFIG_FILE
assert "$output" =~ "[services.${TEST_SYSTEM_CONN_NAME}]" "expected [services.${TEST_SYSTEM_CONN_NAME}] in ${PODMAN_TUI_CONFIG_FILE}"
assert "$output" =~ "uri = \"unix://run/podman/podman.sock\"" "expected ${TEST_SYSTEM_CONN_URI} in ${PODMAN_TUI_CONFIG_FILE}"
run_helper jq ".connections.${TEST_SYSTEM_CONN_NAME}.uri" ${PODMAN_TUI_CONFIG_FILE}
assert "$output" == "\"$TEST_SYSTEM_CONN_URI\"" "expected ${TEST_SYSTEM_CONN_URI} in ${PODMAN_TUI_CONFIG_FILE}"
}
@test "system set default" {
@ -34,10 +33,11 @@ load helpers_tui
podman_tui_select_system_cmd "default"
sleep $TEST_TIMEOUT_LOW
run_helper tail -3 $PODMAN_TUI_CONFIG_FILE
assert "$output" =~ "[services.${TEST_SYSTEM_CONN_NAME}]" "expected [services.${TEST_SYSTEM_CONN_NAME}] in ${PODMAN_TUI_CONFIG_FILE}"
assert "$output" =~ "uri = \"unix://run/podman/podman.sock\"" "expected ${TEST_SYSTEM_CONN_URI} in ${PODMAN_TUI_CONFIG_FILE}"
assert "$output" =~ "default = true" "expected 'default = true' in ${PODMAN_TUI_CONFIG_FILE}"
run_helper jq ".connections.${TEST_SYSTEN_CONN_LOCAL}.uri" ${PODMAN_TUI_CONFIG_FILE}
assert "$output" == "\"$TEST_SYSTEM_CONN_URI\"" "expected ${TEST_SYSTEM_CONN_URI} in ${PODMAN_TUI_CONFIG_FILE}"
run_helper jq ".connections.${TEST_SYSTEN_CONN_LOCAL}.default" ${PODMAN_TUI_CONFIG_FILE}
assert "$output" == "true" "expected 'default = true' in ${PODMAN_TUI_CONFIG_FILE}"
}
@test "system remove" {
@ -51,8 +51,8 @@ load helpers_tui
podman_tui_send_inputs "Enter"
sleep $TEST_TIMEOUT_LOW
run_helper tail -3 $PODMAN_TUI_CONFIG_FILE
assert "$output" !~ "services.${TEST_SYSTEM_CONN_NAME}" "expected [services.${TEST_SYSTEM_CONN_NAME}] not in ${PODMAN_TUI_CONFIG_FILE}"
run_helper jq ".connections.${TEST_SYSTEN_CONN_LOCAL}" ${PODMAN_TUI_CONFIG_FILE}
assert "$output" == "null" "expected ${TEST_SYSTEN_CONN_LOCAL} connection to be removed from in ${PODMAN_TUI_CONFIG_FILE}"
}
@test "system disconnect" {

View File

@ -5,7 +5,7 @@ PODMAN_TUI=${PODMAN_TUI:-./bin/podman-tui}
PODMAN_TUI_DEBUG="$PODMAN_TUI -d"
PODMAN_TUI_LOG="podman-tui.log"
PODMAN_TUI_CONFIG_DIR="/root/.config/podman-tui"
PODMAN_TUI_CONFIG_FILE="${PODMAN_TUI_CONFIG_DIR}/podman-tui.conf"
PODMAN_TUI_CONFIG_FILE="${PODMAN_TUI_CONFIG_DIR}/podman-tui.json"
TMUX_SESSION="podman_tui_test"
@ -13,12 +13,7 @@ function setup() {
# setup config file
[ ! -d "${PODMAN_TUI_CONFIG_DIR}" ] && mkdir -p ${PODMAN_TUI_CONFIG_DIR}
cat > ${PODMAN_TUI_CONFIG_FILE} << EOF
[services]
[services.localhost]
uri = "unix://run/podman/podman.sock"
default = true
[services.localhost_test]
uri = "unix://run/podman/podman.sock"
{"connections":{"localhost":{"uri":"unix://run/podman/podman.sock","default":true},"localhost_test":{"uri":"unix://run/podman/podman.sock"}}}
EOF
# start podman socket

View File

@ -11,9 +11,9 @@ TEST_POD_NETWORK_NAME="${TEST_NAME}_pod01_net"
TEST_CONTAINER_NAME="${TEST_NAME}_cnt01"
TEST_CONTAINER_CHECKPOINT_NAME="${TEST_NAME}_checkpoint"
TEST_CONTAINER_HEALTH_CMD="date"
TEST_CONTAINER_HEALTH_INTERVAL="60s"
TEST_CONTAINER_HEALTH_TIMEOUT="60s"
TEST_CONTAINER_HEALTH_RETRIES="6"
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_NAME}_cnt01_pod"
TEST_CONTAINER_NETWORK_NAME="${TEST_NAME}_cnt01_net"
@ -32,6 +32,7 @@ TEST_CONTAINER_RUN_CMD="/bin/sh"
TEST_LABEL_NAME="test"
TEST_LABEL_VALUE="$TEST_NAME"
TEST_LABEL="${TEST_LABEL_NAME}=${TEST_LABEL_VALUE}"
TEST_SYSTEN_CONN_LOCAL="localhost_test"
TEST_SYSTEM_CONN_NAME="localhost_test_tui"
TEST_SYSTEM_CONN_URI="unix://run/podman/podman.sock"
TEST_IMAGE_BUILD_CONTEXT_DIR="$(realpath .)/test/testdata/"

View File

@ -87,6 +87,9 @@ const (
createContainerHealthStartupRetriesFieldFocus
createContainerHealthStartPeriodFieldFocus
createContainerHealthStartupSuccessFieldFocus
createContainerHealthLogDestFocus
createContainerHealthMaxLogCountFocus
createContainerHealthMaxLogSizeFocus
createContainerMemoryFieldFocus
createContainerMemoryReservatoinFieldFocus
createContainerMemorySwapFieldFocus
@ -101,6 +104,15 @@ const (
createContainerCPUSetMemsFieldFocus
createContainerShmSizeFieldFocus
createContainerShmSizeSystemdFieldFocus
createContainerNamespaceCgroupFieldFocus
createContainerNamespaceIpcFieldFocus
createContainerNamespacePidFieldFocus
createContainerNamespaceUserFieldFocus
createContainerNamespaceUtsFieldFocus
createContainerNamespaceUidmapFieldFocus
createContainerNamespaceGidmapFieldFocus
createContainerNamespaceSubuidNameFieldFocus
createContainerNamespaceSubgidNameFieldFocus
)
const (
@ -114,6 +126,7 @@ const (
createContainerSecurityOptsPageIndex
createContainerVolumePageIndex
createContainerResourcePageIndex
createContainerNamespacePageIndex
)
type ContainerCreateDialogMode int
@ -136,6 +149,7 @@ type ContainerCreateDialog struct {
volumePage *tview.Flex
healthPage *tview.Flex
resourcePage *tview.Flex
namespacePage *tview.Flex
form *tview.Form
display bool
activePageIndex int
@ -193,6 +207,9 @@ type ContainerCreateDialog struct {
containerHealthStartupRetriesField *tview.InputField
containerHealthStartupSuccessField *tview.InputField
containerHealthStartupTimeoutField *tview.InputField
containerHealthLogDestField *tview.InputField
containerHealthMaxLogCountField *tview.InputField
containerHealthMaxLogSizeField *tview.InputField
containerVolumeField *tview.InputField
containerImageVolumeField *tview.DropDown
containerMountField *tview.InputField
@ -210,6 +227,15 @@ type ContainerCreateDialog struct {
containerCPUSetMemsField *tview.InputField
containerShmSizeField *tview.InputField
containerShmSizeSystemdField *tview.InputField
containerNamespaceCgroupField *tview.InputField
containerNamespaceIpcField *tview.InputField
containerNamespacePidField *tview.InputField
containerNamespaceUserField *tview.InputField
containerNamespaceUtsField *tview.InputField
containerNamespaceUidmapField *tview.InputField
containerNamespaceGidmapField *tview.InputField
containerNamespaceSubuidNameField *tview.InputField
containerNamespaceSubgidNameField *tview.InputField
cancelHandler func()
enterHandler func()
}
@ -232,6 +258,7 @@ func NewContainerCreateDialog(mode ContainerCreateDialogMode) *ContainerCreateDi
volumePage: tview.NewFlex(),
healthPage: tview.NewFlex(),
resourcePage: tview.NewFlex(),
namespacePage: tview.NewFlex(),
form: tview.NewForm(),
categoryLabels: []string{
"Container",
@ -244,6 +271,7 @@ func NewContainerCreateDialog(mode ContainerCreateDialogMode) *ContainerCreateDi
"Security Options",
"Volumes Settings",
"Resource Settings",
"Namespace Options",
},
activePageIndex: 0,
display: false,
@ -301,6 +329,9 @@ func NewContainerCreateDialog(mode ContainerCreateDialogMode) *ContainerCreateDi
containerHealthStartupRetriesField: tview.NewInputField(),
containerHealthStartupSuccessField: tview.NewInputField(),
containerHealthStartupTimeoutField: tview.NewInputField(),
containerHealthLogDestField: tview.NewInputField(),
containerHealthMaxLogCountField: tview.NewInputField(),
containerHealthMaxLogSizeField: tview.NewInputField(),
containerMemoryField: tview.NewInputField(),
containerMemoryReservationField: tview.NewInputField(),
containerMemorySwapField: tview.NewInputField(),
@ -315,6 +346,15 @@ func NewContainerCreateDialog(mode ContainerCreateDialogMode) *ContainerCreateDi
containerCPUSetMemsField: tview.NewInputField(),
containerShmSizeField: tview.NewInputField(),
containerShmSizeSystemdField: tview.NewInputField(),
containerNamespaceCgroupField: tview.NewInputField(),
containerNamespacePidField: tview.NewInputField(),
containerNamespaceIpcField: tview.NewInputField(),
containerNamespaceUserField: tview.NewInputField(),
containerNamespaceUtsField: tview.NewInputField(),
containerNamespaceUidmapField: tview.NewInputField(),
containerNamespaceGidmapField: tview.NewInputField(),
containerNamespaceSubuidNameField: tview.NewInputField(),
containerNamespaceSubgidNameField: tview.NewInputField(),
}
containerDialog.setupLayout()
@ -349,6 +389,7 @@ func (d *ContainerCreateDialog) setupLayout() {
d.setupPortsPageUI()
d.setupSecurityPageUI()
d.setupVolumePageUI()
d.setupNamespacePageUI()
// form
d.form.SetBackgroundColor(bgColor)
@ -374,6 +415,7 @@ func (d *ContainerCreateDialog) setupLayout() {
d.categoryPages.AddPage(d.categoryLabels[createContainerSecurityOptsPageIndex], d.securityOptsPage, true, true)
d.categoryPages.AddPage(d.categoryLabels[createContainerVolumePageIndex], d.volumePage, true, true)
d.categoryPages.AddPage(d.categoryLabels[createContainerResourcePageIndex], d.resourcePage, true, true)
d.categoryPages.AddPage(d.categoryLabels[createContainerNamespacePageIndex], d.namespacePage, true, true)
// add it to layout.
d.layout.SetBackgroundColor(bgColor)
@ -728,47 +770,46 @@ func (d *ContainerCreateDialog) setupHealthPageUI() {
d.containerHealthStartupCmdField.SetLabelColor(style.DialogFgColor)
d.containerHealthStartupCmdField.SetFieldBackgroundColor(inputFieldBgColor)
d.containerHealthLogDestField.SetLabel("Log dest:")
d.containerHealthLogDestField.SetLabelWidth(healthPageLabelWidth)
d.containerHealthLogDestField.SetBackgroundColor(bgColor)
d.containerHealthLogDestField.SetLabelColor(style.DialogFgColor)
d.containerHealthLogDestField.SetFieldBackgroundColor(inputFieldBgColor)
// multi primitive row01
// startup success
d.containerHealthStartupSuccessField.SetLabel("Startup success:")
d.containerHealthStartupSuccessField.SetLabelWidth(healthPageSecColLabelWidth)
d.containerHealthStartupSuccessField.SetBackgroundColor(bgColor)
d.containerHealthStartupSuccessField.SetLabelColor(style.DialogFgColor)
d.containerHealthStartupSuccessField.SetFieldBackgroundColor(inputFieldBgColor)
d.containerHealthStartupSuccessField.SetFieldWidth(healthPageMultiRowFieldWidth)
// max log size
d.containerHealthMaxLogSizeField.SetLabel("Max log size:")
d.containerHealthMaxLogSizeField.SetLabelWidth(healthPageLabelWidth)
d.containerHealthMaxLogSizeField.SetBackgroundColor(bgColor)
d.containerHealthMaxLogSizeField.SetLabelColor(style.DialogFgColor)
d.containerHealthMaxLogSizeField.SetFieldBackgroundColor(inputFieldBgColor)
d.containerHealthMaxLogSizeField.SetFieldWidth(healthPageMultiRowFieldWidth)
// max log count
d.containerHealthMaxLogCountField.SetLabel("Max log count:")
d.containerHealthMaxLogCountField.SetLabelWidth(healthPageSecColLabelWidth)
d.containerHealthMaxLogCountField.SetBackgroundColor(bgColor)
d.containerHealthMaxLogCountField.SetLabelColor(style.DialogFgColor)
d.containerHealthMaxLogCountField.SetFieldBackgroundColor(inputFieldBgColor)
d.containerHealthMaxLogCountField.SetFieldWidth(healthPageMultiRowFieldWidth)
// on-failure
onfailureLabel := fmt.Sprintf("%17s: ", "On failure")
d.containerHealthOnFailureField.SetOptions([]string{"none", "kill", "restart", "stop"}, nil)
d.containerHealthOnFailureField.SetLabel("On failure:")
d.containerHealthOnFailureField.SetLabelWidth(healthPageLabelWidth)
d.containerHealthOnFailureField.SetLabel(onfailureLabel)
d.containerHealthOnFailureField.SetBackgroundColor(bgColor)
d.containerHealthOnFailureField.SetLabelColor(style.DialogFgColor)
d.containerHealthOnFailureField.SetListStyles(ddUnselectedStyle, ddselectedStyle)
d.containerHealthOnFailureField.SetFieldBackgroundColor(inputFieldBgColor)
// start period
startPeroidLabel := fmt.Sprintf("%15s: ", "Start period")
d.containerHealthStartPeriodField.SetLabel(startPeroidLabel)
d.containerHealthStartPeriodField.SetBackgroundColor(bgColor)
d.containerHealthStartPeriodField.SetLabelColor(style.DialogFgColor)
d.containerHealthStartPeriodField.SetFieldBackgroundColor(inputFieldBgColor)
d.containerHealthStartPeriodField.SetFieldWidth(healthPageMultiRowFieldWidth)
multiItemRow01 := tview.NewFlex().SetDirection(tview.FlexColumn)
multiItemRow01.AddItem(d.containerHealthMaxLogSizeField, 0, 1, true)
multiItemRow01.AddItem(d.containerHealthMaxLogCountField, 0, 1, true)
multiItemRow01.AddItem(d.containerHealthOnFailureField, 0, 1, true)
multiItemRow01.AddItem(d.containerHealthStartupSuccessField, 0, 1, true)
multiItemRow01.AddItem(d.containerHealthStartPeriodField, 0, 1, true)
multiItemRow01.SetBackgroundColor(bgColor)
// multi primitive row02
// startup interval
d.containerHealthStartupIntervalField.SetLabel("Startup interval:")
d.containerHealthStartupIntervalField.SetLabelWidth(healthPageSecColLabelWidth)
d.containerHealthStartupIntervalField.SetBackgroundColor(bgColor)
d.containerHealthStartupIntervalField.SetLabelColor(style.DialogFgColor)
d.containerHealthStartupIntervalField.SetFieldBackgroundColor(inputFieldBgColor)
d.containerHealthStartupIntervalField.SetFieldWidth(healthPageMultiRowFieldWidth)
// interval
d.containerHealthIntervalField.SetLabel("Interval:")
d.containerHealthIntervalField.SetLabelWidth(healthPageLabelWidth)
@ -777,21 +818,29 @@ func (d *ContainerCreateDialog) setupHealthPageUI() {
d.containerHealthIntervalField.SetFieldBackgroundColor(inputFieldBgColor)
d.containerHealthIntervalField.SetFieldWidth(healthPageMultiRowFieldWidth)
// startup interval
d.containerHealthStartupIntervalField.SetLabel("Startup interval:")
d.containerHealthStartupIntervalField.SetLabelWidth(healthPageSecColLabelWidth)
d.containerHealthStartupIntervalField.SetBackgroundColor(bgColor)
d.containerHealthStartupIntervalField.SetLabelColor(style.DialogFgColor)
d.containerHealthStartupIntervalField.SetFieldBackgroundColor(inputFieldBgColor)
d.containerHealthStartupIntervalField.SetFieldWidth(healthPageMultiRowFieldWidth)
// start period
startPeroidLabel := fmt.Sprintf("%17s: ", "Start period")
d.containerHealthStartPeriodField.SetLabel(startPeroidLabel)
d.containerHealthStartPeriodField.SetBackgroundColor(bgColor)
d.containerHealthStartPeriodField.SetLabelColor(style.DialogFgColor)
d.containerHealthStartPeriodField.SetFieldBackgroundColor(inputFieldBgColor)
d.containerHealthStartPeriodField.SetFieldWidth(healthPageMultiRowFieldWidth)
multiItemRow02 := tview.NewFlex().SetDirection(tview.FlexColumn)
multiItemRow02.AddItem(d.containerHealthIntervalField, 0, 1, true)
multiItemRow02.AddItem(d.containerHealthStartupIntervalField, 0, 1, true)
multiItemRow02.AddItem(utils.EmptyBoxSpace(bgColor), 0, 1, true)
multiItemRow02.AddItem(d.containerHealthStartPeriodField, 0, 1, true)
multiItemRow02.SetBackgroundColor(bgColor)
// multi primitive row03
// startup retries
d.containerHealthStartupRetriesField.SetLabel("Startup retries:")
d.containerHealthStartupRetriesField.SetLabelWidth(healthPageSecColLabelWidth)
d.containerHealthStartupRetriesField.SetBackgroundColor(bgColor)
d.containerHealthStartupRetriesField.SetLabelColor(style.DialogFgColor)
d.containerHealthStartupRetriesField.SetFieldBackgroundColor(inputFieldBgColor)
d.containerHealthStartupRetriesField.SetFieldWidth(healthPageMultiRowFieldWidth)
// retires
d.containerHealthRetriesField.SetLabel("Retries:")
d.containerHealthRetriesField.SetLabelWidth(healthPageLabelWidth)
@ -800,21 +849,29 @@ func (d *ContainerCreateDialog) setupHealthPageUI() {
d.containerHealthRetriesField.SetFieldBackgroundColor(inputFieldBgColor)
d.containerHealthRetriesField.SetFieldWidth(healthPageMultiRowFieldWidth)
// startup retries
d.containerHealthStartupRetriesField.SetLabel("Startup retries:")
d.containerHealthStartupRetriesField.SetLabelWidth(healthPageSecColLabelWidth)
d.containerHealthStartupRetriesField.SetBackgroundColor(bgColor)
d.containerHealthStartupRetriesField.SetLabelColor(style.DialogFgColor)
d.containerHealthStartupRetriesField.SetFieldBackgroundColor(inputFieldBgColor)
d.containerHealthStartupRetriesField.SetFieldWidth(healthPageMultiRowFieldWidth)
// startup success
startupSuccessLabel := fmt.Sprintf("%17s: ", "Startup success")
d.containerHealthStartupSuccessField.SetLabel(startupSuccessLabel)
d.containerHealthStartupSuccessField.SetBackgroundColor(bgColor)
d.containerHealthStartupSuccessField.SetLabelColor(style.DialogFgColor)
d.containerHealthStartupSuccessField.SetFieldBackgroundColor(inputFieldBgColor)
d.containerHealthStartupSuccessField.SetFieldWidth(healthPageMultiRowFieldWidth)
multiItemRow03 := tview.NewFlex().SetDirection(tview.FlexColumn)
multiItemRow03.AddItem(d.containerHealthRetriesField, 0, 1, true)
multiItemRow03.AddItem(d.containerHealthStartupRetriesField, 0, 1, true)
multiItemRow03.AddItem(utils.EmptyBoxSpace(bgColor), 0, 1, true)
multiItemRow03.AddItem(d.containerHealthStartupSuccessField, 0, 1, true)
multiItemRow03.SetBackgroundColor(bgColor)
// multi primitive row04
// startup timeout
d.containerHealthStartupTimeoutField.SetLabel("Startup timeout:")
d.containerHealthStartupTimeoutField.SetLabelWidth(healthPageSecColLabelWidth)
d.containerHealthStartupTimeoutField.SetBackgroundColor(bgColor)
d.containerHealthStartupTimeoutField.SetLabelColor(style.DialogFgColor)
d.containerHealthStartupTimeoutField.SetFieldBackgroundColor(inputFieldBgColor)
d.containerHealthStartupTimeoutField.SetFieldWidth(healthPageMultiRowFieldWidth)
// timeout
d.containerHealthTimeoutField.SetLabel("Timeout:")
d.containerHealthTimeoutField.SetLabelWidth(healthPageLabelWidth)
@ -822,6 +879,13 @@ func (d *ContainerCreateDialog) setupHealthPageUI() {
d.containerHealthTimeoutField.SetLabelColor(style.DialogFgColor)
d.containerHealthTimeoutField.SetFieldBackgroundColor(inputFieldBgColor)
d.containerHealthTimeoutField.SetFieldWidth(healthPageMultiRowFieldWidth)
// startup timeout
d.containerHealthStartupTimeoutField.SetLabel("Startup timeout:")
d.containerHealthStartupTimeoutField.SetLabelWidth(healthPageSecColLabelWidth)
d.containerHealthStartupTimeoutField.SetBackgroundColor(bgColor)
d.containerHealthStartupTimeoutField.SetLabelColor(style.DialogFgColor)
d.containerHealthStartupTimeoutField.SetFieldBackgroundColor(inputFieldBgColor)
d.containerHealthStartupTimeoutField.SetFieldWidth(healthPageMultiRowFieldWidth)
multiItemRow04 := tview.NewFlex().SetDirection(tview.FlexColumn)
multiItemRow04.AddItem(d.containerHealthTimeoutField, 0, 1, true)
@ -835,6 +899,8 @@ func (d *ContainerCreateDialog) setupHealthPageUI() {
d.healthPage.AddItem(utils.EmptyBoxSpace(bgColor), 1, 0, true)
d.healthPage.AddItem(d.containerHealthStartupCmdField, 1, 0, true)
d.healthPage.AddItem(utils.EmptyBoxSpace(bgColor), 1, 0, true)
d.healthPage.AddItem(d.containerHealthLogDestField, 1, 0, true)
d.healthPage.AddItem(utils.EmptyBoxSpace(bgColor), 1, 0, true)
d.healthPage.AddItem(multiItemRow01, 1, 0, true)
d.healthPage.AddItem(utils.EmptyBoxSpace(bgColor), 1, 0, true)
d.healthPage.AddItem(multiItemRow02, 1, 0, true)
@ -1215,6 +1281,105 @@ func (d *ContainerCreateDialog) setupResourcePageUI() {
d.resourcePage.SetBackgroundColor(bgColor)
}
func (d *ContainerCreateDialog) setupNamespacePageUI() {
bgColor := style.DialogBgColor
inputFieldBgColor := style.InputFieldBgColor
namespacePageLabelWidth := 10
// cgroupns
d.containerNamespaceCgroupField.SetLabel("cgroupns:")
d.containerNamespaceCgroupField.SetLabelWidth(namespacePageLabelWidth)
d.containerNamespaceCgroupField.SetBackgroundColor(bgColor)
d.containerNamespaceCgroupField.SetLabelColor(style.DialogFgColor)
d.containerNamespaceCgroupField.SetFieldBackgroundColor(inputFieldBgColor)
// ipc
d.containerNamespaceIpcField.SetLabel("ipc:")
d.containerNamespaceIpcField.SetLabelWidth(namespacePageLabelWidth)
d.containerNamespaceIpcField.SetBackgroundColor(bgColor)
d.containerNamespaceIpcField.SetLabelColor(style.DialogFgColor)
d.containerNamespaceIpcField.SetFieldBackgroundColor(inputFieldBgColor)
// pid
d.containerNamespacePidField.SetLabel("pid:")
d.containerNamespacePidField.SetLabelWidth(namespacePageLabelWidth)
d.containerNamespacePidField.SetBackgroundColor(bgColor)
d.containerNamespacePidField.SetLabelColor(style.DialogFgColor)
d.containerNamespacePidField.SetFieldBackgroundColor(inputFieldBgColor)
// userns
d.containerNamespaceUserField.SetLabel("userns:")
d.containerNamespaceUserField.SetLabelWidth(namespacePageLabelWidth)
d.containerNamespaceUserField.SetBackgroundColor(bgColor)
d.containerNamespaceUserField.SetLabelColor(style.DialogFgColor)
d.containerNamespaceUserField.SetFieldBackgroundColor(inputFieldBgColor)
// uts
d.containerNamespaceUtsField.SetLabel("uts:")
d.containerNamespaceUtsField.SetLabelWidth(namespacePageLabelWidth)
d.containerNamespaceUtsField.SetBackgroundColor(bgColor)
d.containerNamespaceUtsField.SetLabelColor(style.DialogFgColor)
d.containerNamespaceUtsField.SetFieldBackgroundColor(inputFieldBgColor)
// uidmap
d.containerNamespaceUidmapField.SetLabel("uidmap:")
d.containerNamespaceUidmapField.SetLabelWidth(namespacePageLabelWidth)
d.containerNamespaceUidmapField.SetBackgroundColor(bgColor)
d.containerNamespaceUidmapField.SetLabelColor(style.DialogFgColor)
d.containerNamespaceUidmapField.SetFieldBackgroundColor(inputFieldBgColor)
// subuidname
d.containerNamespaceSubuidNameField.SetLabel("subuidname: ")
d.containerNamespaceSubuidNameField.SetBackgroundColor(bgColor)
d.containerNamespaceSubuidNameField.SetLabelColor(style.DialogFgColor)
d.containerNamespaceSubuidNameField.SetFieldBackgroundColor(inputFieldBgColor)
// gidmap
d.containerNamespaceGidmapField.SetLabel("gidmap:")
d.containerNamespaceGidmapField.SetLabelWidth(namespacePageLabelWidth)
d.containerNamespaceGidmapField.SetBackgroundColor(bgColor)
d.containerNamespaceGidmapField.SetLabelColor(style.DialogFgColor)
d.containerNamespaceGidmapField.SetFieldBackgroundColor(inputFieldBgColor)
// subgidname
d.containerNamespaceSubgidNameField.SetLabel("subgidname: ")
d.containerNamespaceSubgidNameField.SetBackgroundColor(bgColor)
d.containerNamespaceSubgidNameField.SetLabelColor(style.DialogFgColor)
d.containerNamespaceSubgidNameField.SetFieldBackgroundColor(inputFieldBgColor)
// mapRow01Layout
mapRow01Layout := tview.NewFlex().SetDirection(tview.FlexColumn)
mapRow01Layout.AddItem(d.containerNamespaceUidmapField, 0, 1, true)
mapRow01Layout.AddItem(utils.EmptyBoxSpace(bgColor), 1, 0, true)
mapRow01Layout.AddItem(d.containerNamespaceSubuidNameField, 0, 1, true)
mapRow01Layout.SetBackgroundColor(bgColor)
// mapRow02Layout
mapRow02Layout := tview.NewFlex().SetDirection(tview.FlexColumn)
mapRow02Layout.AddItem(d.containerNamespaceGidmapField, 0, 1, true)
mapRow02Layout.AddItem(utils.EmptyBoxSpace(bgColor), 1, 0, true)
mapRow02Layout.AddItem(d.containerNamespaceSubgidNameField, 0, 1, true)
mapRow02Layout.SetBackgroundColor(bgColor)
// namespace options page
d.namespacePage.SetDirection(tview.FlexRow)
d.namespacePage.AddItem(d.containerNamespaceCgroupField, 1, 0, true)
d.namespacePage.AddItem(utils.EmptyBoxSpace(bgColor), 1, 0, true)
d.namespacePage.AddItem(d.containerNamespaceIpcField, 1, 0, true)
d.namespacePage.AddItem(utils.EmptyBoxSpace(bgColor), 1, 0, true)
d.namespacePage.AddItem(d.containerNamespacePidField, 1, 0, true)
d.namespacePage.AddItem(utils.EmptyBoxSpace(bgColor), 1, 0, true)
d.namespacePage.AddItem(d.containerNamespaceUserField, 1, 0, true)
d.namespacePage.AddItem(utils.EmptyBoxSpace(bgColor), 1, 0, true)
d.namespacePage.AddItem(d.containerNamespaceUtsField, 1, 0, true)
d.namespacePage.AddItem(utils.EmptyBoxSpace(bgColor), 1, 0, true)
d.namespacePage.AddItem(mapRow01Layout, 1, 0, true)
d.namespacePage.AddItem(utils.EmptyBoxSpace(bgColor), 1, 0, true)
d.namespacePage.AddItem(mapRow02Layout, 1, 0, true)
d.namespacePage.SetBackgroundColor(bgColor)
}
// Display displays this primitive.
func (d *ContainerCreateDialog) Display() {
d.display = true
@ -1421,6 +1586,12 @@ func (d *ContainerCreateDialog) Focus(delegate func(p tview.Primitive)) { //noli
delegate(d.containerHealthStartPeriodField)
case createContainerHealthStartupSuccessFieldFocus:
delegate(d.containerHealthStartupSuccessField)
case createContainerHealthLogDestFocus:
delegate(d.containerHealthLogDestField)
case createContainerHealthMaxLogCountFocus:
delegate(d.containerHealthMaxLogCountField)
case createContainerHealthMaxLogSizeFocus:
delegate(d.containerHealthMaxLogSizeField)
// resource page
case createContainerMemoryFieldFocus:
delegate(d.containerMemoryField)
@ -1450,6 +1621,25 @@ func (d *ContainerCreateDialog) Focus(delegate func(p tview.Primitive)) { //noli
delegate(d.containerShmSizeField)
case createContainerShmSizeSystemdFieldFocus:
delegate(d.containerShmSizeSystemdField)
// namespace page
case createContainerNamespaceCgroupFieldFocus:
delegate(d.containerNamespaceCgroupField)
case createContainerNamespaceIpcFieldFocus:
delegate(d.containerNamespaceIpcField)
case createContainerNamespacePidFieldFocus:
delegate(d.containerNamespacePidField)
case createContainerNamespaceUserFieldFocus:
delegate(d.containerNamespaceUserField)
case createContainerNamespaceUtsFieldFocus:
delegate(d.containerNamespaceUtsField)
case createContainerNamespaceUidmapFieldFocus:
delegate(d.containerNamespaceUidmapField)
case createContainerNamespaceSubuidNameFieldFocus:
delegate(d.containerNamespaceSubuidNameField)
case createContainerNamespaceGidmapFieldFocus:
delegate(d.containerNamespaceGidmapField)
case createContainerNamespaceSubgidNameFieldFocus:
delegate(d.containerNamespaceSubgidNameField)
// category page
case createCategoryPagesFocus:
delegate(d.categoryPages)
@ -1617,6 +1807,18 @@ func (d *ContainerCreateDialog) InputHandler() func(event *tcell.EventKey, setFo
}
}
if d.namespacePage.HasFocus() {
if handler := d.namespacePage.InputHandler(); handler != nil {
if event.Key() == tcell.KeyTab {
d.setNamespaceOptionsPageNextFocus()
}
handler(event, setFocus)
return
}
}
if d.categories.HasFocus() {
if categroryHandler := d.categories.InputHandler(); categroryHandler != nil {
categroryHandler(event, setFocus)
@ -1839,14 +2041,17 @@ func (d *ContainerCreateDialog) initData() {
d.containerHealthCmdField.SetText("")
d.containerHealthStartupCmdField.SetText("")
d.containerHealthOnFailureField.SetCurrentOption(0)
d.containerHealthIntervalField.SetText("")
d.containerHealthStartupIntervalField.SetText("")
d.containerHealthTimeoutField.SetText("")
d.containerHealthStartupTimeoutField.SetText("")
d.containerHealthRetriesField.SetText("")
d.containerHealthIntervalField.SetText("30s")
d.containerHealthStartupIntervalField.SetText("30s")
d.containerHealthTimeoutField.SetText("30s")
d.containerHealthStartupTimeoutField.SetText("30s")
d.containerHealthRetriesField.SetText("3")
d.containerHealthStartupRetriesField.SetText("")
d.containerHealthStartPeriodField.SetText("")
d.containerHealthStartPeriodField.SetText("0s")
d.containerHealthStartupSuccessField.SetText("")
d.containerHealthLogDestField.SetText("local")
d.containerHealthMaxLogCountField.SetText("5")
d.containerHealthMaxLogSizeField.SetText("500")
// network settings category
d.containerHostnameField.SetText("")
@ -1889,6 +2094,17 @@ func (d *ContainerCreateDialog) initData() {
d.containerCPUSetMemsField.SetText("")
d.containerShmSizeField.SetText("")
d.containerShmSizeSystemdField.SetText("")
// namespace options category
d.containerNamespaceCgroupField.SetText("")
d.containerNamespaceIpcField.SetText("")
d.containerNamespacePidField.SetText("")
d.containerNamespaceUserField.SetText("")
d.containerNamespaceUtsField.SetText("")
d.containerNamespaceUidmapField.SetText("")
d.containerNamespaceGidmapField.SetText("")
d.containerNamespaceSubuidNameField.SetText("")
d.containerNamespaceSubgidNameField.SetText("")
}
func (d *ContainerCreateDialog) setPortPageNextFocus() {
@ -1907,6 +2123,58 @@ func (d *ContainerCreateDialog) setPortPageNextFocus() {
d.focusElement = createContainerFormFocus
}
func (d *ContainerCreateDialog) setNamespaceOptionsPageNextFocus() {
if d.containerNamespaceCgroupField.HasFocus() {
d.focusElement = createContainerNamespaceIpcFieldFocus
return
}
if d.containerNamespaceIpcField.HasFocus() {
d.focusElement = createContainerNamespacePidFieldFocus
return
}
if d.containerNamespacePidField.HasFocus() {
d.focusElement = createContainerNamespaceUserFieldFocus
return
}
if d.containerNamespaceUserField.HasFocus() {
d.focusElement = createContainerNamespaceUtsFieldFocus
return
}
if d.containerNamespaceUtsField.HasFocus() {
d.focusElement = createContainerNamespaceUidmapFieldFocus
return
}
if d.containerNamespaceUidmapField.HasFocus() {
d.focusElement = createContainerNamespaceSubuidNameFieldFocus
return
}
if d.containerNamespaceSubuidNameField.HasFocus() {
d.focusElement = createContainerNamespaceGidmapFieldFocus
return
}
if d.containerNamespaceGidmapField.HasFocus() {
d.focusElement = createContainerNamespaceSubgidNameFieldFocus
return
}
d.focusElement = createContainerFormFocus
}
func (d *ContainerCreateDialog) setContainerInfoPageNextFocus() { //nolint:cyclop
if d.containerNameField.HasFocus() {
d.focusElement = createContainerCommandFieldFocus
@ -2123,7 +2391,7 @@ func (d *ContainerCreateDialog) setDNSSettingsPageNextFocus() {
d.focusElement = createContainerFormFocus
}
func (d *ContainerCreateDialog) setResourceSettingsPageNextFocus() { //nolint:cyclop
func (d *ContainerCreateDialog) setResourceSettingsPageNextFocus() { //nolint:cyclop,dupl
if d.containerMemoryField.HasFocus() {
d.focusElement = createContainerMemoryReservatoinFieldFocus
@ -2205,7 +2473,7 @@ func (d *ContainerCreateDialog) setResourceSettingsPageNextFocus() { //nolint:cy
d.focusElement = createContainerFormFocus
}
func (d *ContainerCreateDialog) setHealthSettingsPageNextFocus() { //nolint:cyclop
func (d *ContainerCreateDialog) setHealthSettingsPageNextFocus() { //nolint:cyclop,dupl
if d.containerHealthCmdField.HasFocus() {
d.focusElement = createContainerHealthStartupCmdFieldFocus
@ -2213,24 +2481,30 @@ func (d *ContainerCreateDialog) setHealthSettingsPageNextFocus() { //nolint:cycl
}
if d.containerHealthStartupCmdField.HasFocus() {
d.focusElement = createContainerHealthLogDestFocus
return
}
if d.containerHealthLogDestField.HasFocus() {
d.focusElement = createContainerHealthMaxLogSizeFocus
return
}
if d.containerHealthMaxLogSizeField.HasFocus() {
d.focusElement = createContainerHealthMaxLogCountFocus
return
}
if d.containerHealthMaxLogCountField.HasFocus() {
d.focusElement = createContainerHealthOnFailureFieldFocus
return
}
if d.containerHealthOnFailureField.HasFocus() {
d.focusElement = createContainerHealthStartupSuccessFieldFocus
return
}
if d.containerHealthStartupSuccessField.HasFocus() {
d.focusElement = createContainerHealthStartPeriodFieldFocus
return
}
if d.containerHealthStartPeriodField.HasFocus() {
d.focusElement = createContainerHealthIntervalFieldFocus
return
@ -2243,6 +2517,12 @@ func (d *ContainerCreateDialog) setHealthSettingsPageNextFocus() { //nolint:cycl
}
if d.containerHealthStartupIntervalField.HasFocus() {
d.focusElement = createContainerHealthStartPeriodFieldFocus
return
}
if d.containerHealthStartPeriodField.HasFocus() {
d.focusElement = createContainerHealthRetriesFieldFocus
return
@ -2255,6 +2535,12 @@ func (d *ContainerCreateDialog) setHealthSettingsPageNextFocus() { //nolint:cycl
}
if d.containerHealthStartupRetriesField.HasFocus() {
d.focusElement = createContainerHealthStartupSuccessFieldFocus
return
}
if d.containerHealthStartupSuccessField.HasFocus() {
d.focusElement = createContainerHealthTimeoutFieldFocus
return
@ -2464,6 +2750,9 @@ func (d *ContainerCreateDialog) ContainerCreateOptions() containers.CreateOption
HealthStartupRetries: strings.TrimSpace(d.containerHealthStartupRetriesField.GetText()),
HealthStartupSuccess: strings.TrimSpace(d.containerHealthStartupSuccessField.GetText()),
HealthStartupTimeout: strings.TrimSpace(d.containerHealthStartupTimeoutField.GetText()),
HealthLogDestination: strings.TrimSpace(d.containerHealthLogDestField.GetText()),
HealthMaxLogSize: strings.TrimSpace(d.containerHealthMaxLogSizeField.GetText()),
HealthMaxLogCount: strings.TrimSpace(d.containerHealthMaxLogCountField.GetText()),
Memory: strings.TrimSpace(d.containerMemoryField.GetText()),
MemoryReservation: strings.TrimSpace(d.containerMemoryReservationField.GetText()),
MemorySwap: strings.TrimSpace(d.containerMemorySwapField.GetText()),
@ -2478,6 +2767,15 @@ func (d *ContainerCreateDialog) ContainerCreateOptions() containers.CreateOption
CPUSetMems: strings.TrimSpace(d.containerCPUSetMemsField.GetText()),
SHMSize: strings.TrimSpace(d.containerShmSizeField.GetText()),
SHMSizeSystemd: strings.TrimSpace(d.containerShmSizeSystemdField.GetText()),
NamespaceCgroup: strings.TrimSpace(d.containerNamespaceCgroupField.GetText()),
NamespaceIpc: strings.TrimSpace(d.containerNamespaceIpcField.GetText()),
NamespacePid: strings.TrimSpace(d.containerNamespacePidField.GetText()),
NamespaceUser: strings.TrimSpace(d.containerNamespaceUserField.GetText()),
NamespaceUts: strings.TrimSpace(d.containerNamespaceUtsField.GetText()),
NamespaceUidmap: strings.TrimSpace(d.containerNamespaceUidmapField.GetText()),
NamespaceSubuidName: strings.TrimSpace(d.containerNamespaceSubuidNameField.GetText()),
NamespaceGidmap: strings.TrimSpace(d.containerNamespaceGidmapField.GetText()),
NamespaceSubgidName: strings.TrimSpace(d.containerNamespaceSubgidNameField.GetText()),
}
return opts

View File

@ -26,14 +26,16 @@ func (cnt *Containers) UpdateData() {
}
cnt.containersList.mu.Lock()
defer cnt.containersList.mu.Unlock()
cnt.containersList.report = cntList
cnt.containersList.mu.Unlock()
}
func (cnt *Containers) getData() []entities.ListContainer {
cnt.containersList.mu.Lock()
defer cnt.containersList.mu.Unlock()
data := cnt.containersList.report
cnt.containersList.mu.Unlock()
return data
}
@ -41,8 +43,9 @@ func (cnt *Containers) getData() []entities.ListContainer {
// ClearData clears table data.
func (cnt *Containers) ClearData() {
cnt.containersList.mu.Lock()
defer cnt.containersList.mu.Unlock()
cnt.containersList.report = nil
cnt.containersList.mu.Unlock()
cnt.table.Clear()
expand := 1

View File

@ -6,12 +6,12 @@ import (
// Draw draws this primitive onto the screen.
func (cnt *Containers) Draw(screen tcell.Screen) { //nolint:cyclop
cnt.refresh()
cnt.Box.DrawForSubclass(screen, cnt)
cnt.Box.SetBorder(false)
cntViewX, cntViewY, cntViewW, cntViewH := cnt.GetInnerRect()
cnt.refresh(cntViewW)
cnt.table.SetRect(cntViewX, cntViewY, cntViewW, cntViewH)
cnt.table.SetBorder(true)
cnt.table.Draw(screen)

View File

@ -12,7 +12,9 @@ import (
"github.com/rivo/tview"
)
func (cnt *Containers) refresh() {
func (cnt *Containers) refresh(maxWidth int) {
imageColMaxWidth := maxWidth / 5 //nolint:mnd
cnt.table.Clear()
expand := 1
@ -72,6 +74,7 @@ func (cnt *Containers) refresh() {
// image name column
cnt.table.SetCell(rowIndex, viewContainersImageColIndex,
tview.NewTableCell(cntImage).
SetMaxWidth(imageColMaxWidth).
SetTextColor(cellTextColor).
SetExpansion(expand).
SetAlign(alignment))

View File

@ -22,14 +22,16 @@ func (img *Images) UpdateData() {
}
img.imagesList.mu.Lock()
defer img.imagesList.mu.Unlock()
img.imagesList.report = images
img.imagesList.mu.Unlock()
}
func (img *Images) getData() []images.ImageListReporter {
img.imagesList.mu.Lock()
defer img.imagesList.mu.Unlock()
data := img.imagesList.report
img.imagesList.mu.Unlock()
return data
}
@ -37,8 +39,10 @@ func (img *Images) getData() []images.ImageListReporter {
// ClearData clears table data.
func (img *Images) ClearData() {
img.imagesList.mu.Lock()
defer img.imagesList.mu.Unlock()
img.imagesList.report = nil
img.imagesList.mu.Unlock()
img.table.Clear()
expand := 1

View File

@ -6,12 +6,12 @@ import (
// Draw draws this primitive onto the screen.
func (img *Images) Draw(screen tcell.Screen) { //nolint:cyclop
img.refresh()
img.Box.DrawForSubclass(screen, img)
img.Box.SetBorder(false)
imagewViewX, imagewViewY, imagewViewW, imagewViewH := img.GetInnerRect()
img.refresh(imagewViewW)
img.table.SetRect(imagewViewX, imagewViewY, imagewViewW, imagewViewH)
img.table.SetBorder(true)

View File

@ -10,7 +10,7 @@ import (
"github.com/rivo/tview"
)
func (img *Images) refresh() {
func (img *Images) refresh(_ int) {
img.table.Clear()
expand := 1

61
ui/networks/data.go Normal file
View File

@ -0,0 +1,61 @@
package networks
import (
"fmt"
"strings"
"github.com/containers/podman-tui/pdcs/networks"
"github.com/containers/podman-tui/ui/style"
"github.com/rivo/tview"
"github.com/rs/zerolog/log"
)
// UpdateData retrieves networks list data.
func (nets *Networks) UpdateData() {
netList, err := networks.List()
if err != nil {
log.Error().Msgf("view: networks update %v", err)
nets.errorDialog.SetText(fmt.Sprintf("%v", err))
nets.errorDialog.Display()
}
nets.networkList.mu.Lock()
defer nets.networkList.mu.Unlock()
nets.networkList.report = netList
}
func (nets *Networks) getData() [][]string {
nets.networkList.mu.Lock()
defer nets.networkList.mu.Unlock()
data := nets.networkList.report
return data
}
// ClearData clears table data.
func (nets *Networks) ClearData() {
nets.networkList.mu.Lock()
defer nets.networkList.mu.Unlock()
nets.networkList.report = nil
nets.table.Clear()
expand := 1
fgColor := style.PageHeaderFgColor
bgColor := style.PageHeaderBgColor
for i := range nets.headers {
nets.table.SetCell(0, i,
tview.NewTableCell(fmt.Sprintf("[::b]%s", strings.ToUpper(nets.headers[i]))). //nolint:perfsprint
SetExpansion(expand).
SetBackgroundColor(bgColor).
SetTextColor(fgColor).
SetAlign(tview.AlignLeft).
SetSelectable(false))
}
nets.table.SetTitle(fmt.Sprintf("[::b]%s[0]", strings.ToUpper(nets.title)))
}

View File

@ -12,6 +12,7 @@ func (nets *Networks) Draw(screen tcell.Screen) {
netViewX, netViewY, netViewW, netViewH := nets.GetInnerRect()
nets.table.SetRect(netViewX, netViewY, netViewW, netViewH)
nets.refresh(netViewW)
nets.table.SetBorder(true)
nets.table.Draw(screen)

View File

@ -4,6 +4,7 @@ import (
"errors"
"fmt"
"strings"
"sync"
"github.com/containers/podman-tui/ui/dialogs"
"github.com/containers/podman-tui/ui/networks/netdialogs"
@ -38,11 +39,17 @@ type Networks struct {
createDialog *netdialogs.NetworkCreateDialog
connectDialog *netdialogs.NetworkConnectDialog
disconnectDialog *netdialogs.NetworkDisconnectDialog
networkList networkListReport
selectedID string
confirmData string
appFocusHandler func()
}
type networkListReport struct {
mu sync.Mutex
report [][]string
}
// NewNetworks returns nets page view.
func NewNetworks() *Networks {
nets := &Networks{

View File

@ -4,21 +4,11 @@ import (
"fmt"
"strings"
"github.com/containers/podman-tui/pdcs/networks"
"github.com/containers/podman-tui/ui/style"
"github.com/rivo/tview"
"github.com/rs/zerolog/log"
)
// UpdateData retrieves networks list data.
func (nets *Networks) UpdateData() {
netList, err := networks.List()
if err != nil {
log.Error().Msgf("view: networks update %v", err)
nets.errorDialog.SetText(fmt.Sprintf("%v", err))
nets.errorDialog.Display()
}
func (nets *Networks) refresh(_ int) {
nets.table.Clear()
expand := 1
@ -35,6 +25,7 @@ func (nets *Networks) UpdateData() {
}
rowIndex := 1
netList := nets.getData()
nets.table.SetTitle(fmt.Sprintf("[::b]%s[%d]", strings.ToUpper(nets.title), len(netList)))
@ -64,24 +55,3 @@ func (nets *Networks) UpdateData() {
rowIndex++
}
}
// ClearData clears table data.
func (nets *Networks) ClearData() {
nets.table.Clear()
expand := 1
fgColor := style.PageHeaderFgColor
bgColor := style.PageHeaderBgColor
for i := range nets.headers {
nets.table.SetCell(0, i,
tview.NewTableCell(fmt.Sprintf("[::b]%s", strings.ToUpper(nets.headers[i]))). //nolint:perfsprint
SetExpansion(expand).
SetBackgroundColor(bgColor).
SetTextColor(fgColor).
SetAlign(tview.AlignLeft).
SetSelectable(false))
}
nets.table.SetTitle(fmt.Sprintf("[::b]%s[0]", strings.ToUpper(nets.title)))
}

View File

@ -23,14 +23,16 @@ func (pods *Pods) UpdateData() {
}
pods.podsList.mu.Lock()
defer pods.podsList.mu.Unlock()
pods.podsList.report = podList
pods.podsList.mu.Unlock()
}
func (pods *Pods) getData() []*entities.ListPodsReport {
pods.podsList.mu.Lock()
defer pods.podsList.mu.Unlock()
data := pods.podsList.report
pods.podsList.mu.Unlock()
return data
}
@ -38,8 +40,10 @@ func (pods *Pods) getData() []*entities.ListPodsReport {
// ClearData clears table data.
func (pods *Pods) ClearData() { //nolint:stylecheck
pods.podsList.mu.Lock()
defer pods.podsList.mu.Unlock()
pods.podsList.report = nil
pods.podsList.mu.Unlock()
pods.table.Clear()
expand := 1

View File

@ -6,12 +6,12 @@ import (
// Draw draws this primitive onto the screen.
func (pods *Pods) Draw(screen tcell.Screen) {
pods.refresh()
pods.Box.DrawForSubclass(screen, pods)
pods.Box.SetBorder(false)
podViewX, podViewY, podViewW, podViewH := pods.GetInnerRect()
pods.refresh(podViewW)
pods.table.SetRect(podViewX, podViewY, podViewW, podViewH)
pods.table.SetBorder(true)

View File

@ -52,6 +52,14 @@ const (
createPodCPUSetMemsFieldFocus
createPodShmSizeFieldFocus
createPodShmSizeSystemdFieldFocus
createPodNamespaceShareFieldFocus
createPodNamespacePidFieldFocus
createPodNamespaceUserFieldFocus
createPodNamespaceUtsFieldFocus
createPodNamespaceUidmapFieldFocus
createPodNamespaceSubuidNameFieldFocus
createPodNamespaceGidmapFieldFocus
createPodNamespaceSubgidNameFieldFocus
)
const (
@ -61,56 +69,66 @@ const (
createPodNetworkingPageIndex
createPodSecurityOptsPageIndex
createPodResourceSettingsPageIndex
createPodNamespaceOptionsPageIndex
)
// PodCreateDialog implements pod create dialog.
type PodCreateDialog struct {
*tview.Box
layout *tview.Flex
categoryLabels []string
categories *tview.TextView
categoryPages *tview.Pages
basicInfoPage *tview.Flex
securityOptsPage *tview.Flex
dnsSetupPage *tview.Flex
infraSetupPage *tview.Flex
networkingPage *tview.Flex
resourcePage *tview.Flex
form *tview.Form
display bool
activePageIndex int
focusElement int
podNameField *tview.InputField
podNoHostsCheckBox *tview.Checkbox
podLabelsField *tview.InputField
podSelinuxLabelField *tview.InputField
podApparmorField *tview.InputField
podSeccompField *tview.InputField
podMaskField *tview.InputField
podUnmaskField *tview.InputField
podNoNewPrivField *tview.Checkbox
podDNSServerField *tview.InputField
podDNSOptionsField *tview.InputField
podDNSSearchDomaindField *tview.InputField
podInfraCheckBox *tview.Checkbox
podInfraCommandField *tview.InputField
podInfraImageField *tview.InputField
podHostnameField *tview.InputField
podIPAddressField *tview.InputField
podMacAddressField *tview.InputField
podAddHostField *tview.InputField
podNetworkField *tview.DropDown
podPublishField *tview.InputField
podMemoryField *tview.InputField
podMemorySwapField *tview.InputField
podCPUsField *tview.InputField
podCPUSharesField *tview.InputField
podCPUSetCPUsField *tview.InputField
podCPUSetMemsField *tview.InputField
podShmSizeField *tview.InputField
podShmSizeSystemdField *tview.InputField
cancelHandler func()
createHandler func()
layout *tview.Flex
categoryLabels []string
categories *tview.TextView
categoryPages *tview.Pages
basicInfoPage *tview.Flex
securityOptsPage *tview.Flex
dnsSetupPage *tview.Flex
infraSetupPage *tview.Flex
networkingPage *tview.Flex
resourcePage *tview.Flex
namespacePage *tview.Flex
form *tview.Form
display bool
activePageIndex int
focusElement int
podNameField *tview.InputField
podNoHostsCheckBox *tview.Checkbox
podLabelsField *tview.InputField
podSelinuxLabelField *tview.InputField
podApparmorField *tview.InputField
podSeccompField *tview.InputField
podMaskField *tview.InputField
podUnmaskField *tview.InputField
podNoNewPrivField *tview.Checkbox
podDNSServerField *tview.InputField
podDNSOptionsField *tview.InputField
podDNSSearchDomaindField *tview.InputField
podInfraCheckBox *tview.Checkbox
podInfraCommandField *tview.InputField
podInfraImageField *tview.InputField
podHostnameField *tview.InputField
podIPAddressField *tview.InputField
podMacAddressField *tview.InputField
podAddHostField *tview.InputField
podNetworkField *tview.DropDown
podPublishField *tview.InputField
podMemoryField *tview.InputField
podMemorySwapField *tview.InputField
podCPUsField *tview.InputField
podCPUSharesField *tview.InputField
podCPUSetCPUsField *tview.InputField
podCPUSetMemsField *tview.InputField
podShmSizeField *tview.InputField
podShmSizeSystemdField *tview.InputField
podNamespaceShareField *tview.InputField
podNamespacePidField *tview.InputField
podNamespaceUserField *tview.InputField
podNamespaceUtsField *tview.InputField
podNamespaceUidmapField *tview.InputField
podNamespaceSubuidNameField *tview.InputField
podNamespaceGidmapField *tview.InputField
podNamespaceSubgidNameField *tview.InputField
cancelHandler func()
createHandler func()
}
// NewPodCreateDialog returns new pod create dialog primitive PodCreateDialog.
@ -126,6 +144,7 @@ func NewPodCreateDialog() *PodCreateDialog {
infraSetupPage: tview.NewFlex(),
networkingPage: tview.NewFlex(),
resourcePage: tview.NewFlex(),
namespacePage: tview.NewFlex(),
form: tview.NewForm(),
categoryLabels: []string{
"Basic Information",
@ -134,38 +153,47 @@ func NewPodCreateDialog() *PodCreateDialog {
"Networking",
"Security Options",
"Resource Settings",
"Namespace Options",
},
activePageIndex: 0,
display: false,
podNameField: tview.NewInputField(),
podNoHostsCheckBox: tview.NewCheckbox(),
podLabelsField: tview.NewInputField(),
podSelinuxLabelField: tview.NewInputField(),
podApparmorField: tview.NewInputField(),
podSeccompField: tview.NewInputField(),
podMaskField: tview.NewInputField(),
podUnmaskField: tview.NewInputField(),
podNoNewPrivField: tview.NewCheckbox(),
podDNSServerField: tview.NewInputField(),
podDNSOptionsField: tview.NewInputField(),
podDNSSearchDomaindField: tview.NewInputField(),
podInfraCheckBox: tview.NewCheckbox(),
podInfraCommandField: tview.NewInputField(),
podInfraImageField: tview.NewInputField(),
podHostnameField: tview.NewInputField(),
podIPAddressField: tview.NewInputField(),
podMacAddressField: tview.NewInputField(),
podAddHostField: tview.NewInputField(),
podNetworkField: tview.NewDropDown(),
podPublishField: tview.NewInputField(),
podMemoryField: tview.NewInputField(),
podMemorySwapField: tview.NewInputField(),
podCPUsField: tview.NewInputField(),
podCPUSharesField: tview.NewInputField(),
podCPUSetCPUsField: tview.NewInputField(),
podCPUSetMemsField: tview.NewInputField(),
podShmSizeField: tview.NewInputField(),
podShmSizeSystemdField: tview.NewInputField(),
activePageIndex: 0,
display: false,
podNameField: tview.NewInputField(),
podNoHostsCheckBox: tview.NewCheckbox(),
podLabelsField: tview.NewInputField(),
podSelinuxLabelField: tview.NewInputField(),
podApparmorField: tview.NewInputField(),
podSeccompField: tview.NewInputField(),
podMaskField: tview.NewInputField(),
podUnmaskField: tview.NewInputField(),
podNoNewPrivField: tview.NewCheckbox(),
podDNSServerField: tview.NewInputField(),
podDNSOptionsField: tview.NewInputField(),
podDNSSearchDomaindField: tview.NewInputField(),
podInfraCheckBox: tview.NewCheckbox(),
podInfraCommandField: tview.NewInputField(),
podInfraImageField: tview.NewInputField(),
podHostnameField: tview.NewInputField(),
podIPAddressField: tview.NewInputField(),
podMacAddressField: tview.NewInputField(),
podAddHostField: tview.NewInputField(),
podNetworkField: tview.NewDropDown(),
podPublishField: tview.NewInputField(),
podMemoryField: tview.NewInputField(),
podMemorySwapField: tview.NewInputField(),
podCPUsField: tview.NewInputField(),
podCPUSharesField: tview.NewInputField(),
podCPUSetCPUsField: tview.NewInputField(),
podCPUSetMemsField: tview.NewInputField(),
podShmSizeField: tview.NewInputField(),
podShmSizeSystemdField: tview.NewInputField(),
podNamespaceShareField: tview.NewInputField(),
podNamespacePidField: tview.NewInputField(),
podNamespaceUserField: tview.NewInputField(),
podNamespaceUtsField: tview.NewInputField(),
podNamespaceUidmapField: tview.NewInputField(),
podNamespaceSubuidNameField: tview.NewInputField(),
podNamespaceGidmapField: tview.NewInputField(),
podNamespaceSubgidNameField: tview.NewInputField(),
}
podDialog.categories.SetDynamicColors(true).
@ -208,6 +236,7 @@ func (d *PodCreateDialog) setupLayout() {
d.setupNetworkingUI()
d.setupSecurityOptionsUI()
d.setupResourceSettingsUI()
d.setupNamespaceOptionsUI()
// adding category pages
d.categoryPages.AddPage(d.categoryLabels[createPodBasicInfoPageIndex], d.basicInfoPage, true, true)
@ -216,6 +245,7 @@ func (d *PodCreateDialog) setupLayout() {
d.categoryPages.AddPage(d.categoryLabels[createPodNetworkingPageIndex], d.networkingPage, true, true)
d.categoryPages.AddPage(d.categoryLabels[createPodSecurityOptsPageIndex], d.securityOptsPage, true, true)
d.categoryPages.AddPage(d.categoryLabels[createPodResourceSettingsPageIndex], d.resourcePage, true, true)
d.categoryPages.AddPage(d.categoryLabels[createPodNamespaceOptionsPageIndex], d.namespacePage, true, true)
// add it to layout.
_, layoutWidth := utils.AlignStringListWidth(d.categoryLabels)
@ -557,6 +587,96 @@ func (d *PodCreateDialog) setupResourceSettingsUI() {
d.resourcePage.SetBackgroundColor(style.DialogBgColor)
}
func (d *PodCreateDialog) setupNamespaceOptionsUI() {
bgColor := style.DialogBgColor
inputFieldBgColor := style.InputFieldBgColor
namespacePageLabelWidth := 8
// share
d.podNamespaceShareField.SetLabel("share:")
d.podNamespaceShareField.SetLabelWidth(namespacePageLabelWidth)
d.podNamespaceShareField.SetBackgroundColor(bgColor)
d.podNamespaceShareField.SetLabelColor(style.DialogFgColor)
d.podNamespaceShareField.SetFieldBackgroundColor(inputFieldBgColor)
// pid
d.podNamespacePidField.SetLabel("pid:")
d.podNamespacePidField.SetLabelWidth(namespacePageLabelWidth)
d.podNamespacePidField.SetBackgroundColor(bgColor)
d.podNamespacePidField.SetLabelColor(style.DialogFgColor)
d.podNamespacePidField.SetFieldBackgroundColor(inputFieldBgColor)
// userns
d.podNamespaceUserField.SetLabel("userns:")
d.podNamespaceUserField.SetLabelWidth(namespacePageLabelWidth)
d.podNamespaceUserField.SetBackgroundColor(bgColor)
d.podNamespaceUserField.SetLabelColor(style.DialogFgColor)
d.podNamespaceUserField.SetFieldBackgroundColor(inputFieldBgColor)
// uts
d.podNamespaceUtsField.SetLabel("uts:")
d.podNamespaceUtsField.SetLabelWidth(namespacePageLabelWidth)
d.podNamespaceUtsField.SetBackgroundColor(bgColor)
d.podNamespaceUtsField.SetLabelColor(style.DialogFgColor)
d.podNamespaceUtsField.SetFieldBackgroundColor(inputFieldBgColor)
// uidmap
d.podNamespaceUidmapField.SetLabel("uidmap:")
d.podNamespaceUidmapField.SetLabelWidth(namespacePageLabelWidth)
d.podNamespaceUidmapField.SetBackgroundColor(bgColor)
d.podNamespaceUidmapField.SetLabelColor(style.DialogFgColor)
d.podNamespaceUidmapField.SetFieldBackgroundColor(inputFieldBgColor)
// subuidname
d.podNamespaceSubuidNameField.SetLabel("subuidname: ")
d.podNamespaceSubuidNameField.SetBackgroundColor(bgColor)
d.podNamespaceSubuidNameField.SetLabelColor(style.DialogFgColor)
d.podNamespaceSubuidNameField.SetFieldBackgroundColor(inputFieldBgColor)
// gidmap
d.podNamespaceGidmapField.SetLabel("gidmap:")
d.podNamespaceGidmapField.SetLabelWidth(namespacePageLabelWidth)
d.podNamespaceGidmapField.SetBackgroundColor(bgColor)
d.podNamespaceGidmapField.SetLabelColor(style.DialogFgColor)
d.podNamespaceGidmapField.SetFieldBackgroundColor(inputFieldBgColor)
// subgidname
d.podNamespaceSubgidNameField.SetLabel("subgidname: ")
d.podNamespaceSubgidNameField.SetBackgroundColor(bgColor)
d.podNamespaceSubgidNameField.SetLabelColor(style.DialogFgColor)
d.podNamespaceSubgidNameField.SetFieldBackgroundColor(inputFieldBgColor)
// mapRow01Layout
mapRow01Layout := tview.NewFlex().SetDirection(tview.FlexColumn)
mapRow01Layout.AddItem(d.podNamespaceUidmapField, 0, 1, true)
mapRow01Layout.AddItem(utils.EmptyBoxSpace(bgColor), 1, 0, true)
mapRow01Layout.AddItem(d.podNamespaceSubuidNameField, 0, 1, true)
mapRow01Layout.SetBackgroundColor(bgColor)
// mapRow02Layout
mapRow02Layout := tview.NewFlex().SetDirection(tview.FlexColumn)
mapRow02Layout.AddItem(d.podNamespaceGidmapField, 0, 1, true)
mapRow02Layout.AddItem(utils.EmptyBoxSpace(bgColor), 1, 0, true)
mapRow02Layout.AddItem(d.podNamespaceSubgidNameField, 0, 1, true)
mapRow02Layout.SetBackgroundColor(bgColor)
// namespace options page
d.namespacePage.SetDirection(tview.FlexRow)
d.namespacePage.AddItem(d.podNamespaceShareField, 1, 0, true)
d.namespacePage.AddItem(utils.EmptyBoxSpace(bgColor), 1, 0, true)
d.namespacePage.AddItem(d.podNamespacePidField, 1, 0, true)
d.namespacePage.AddItem(utils.EmptyBoxSpace(bgColor), 1, 0, true)
d.namespacePage.AddItem(d.podNamespaceUserField, 1, 0, true)
d.namespacePage.AddItem(utils.EmptyBoxSpace(bgColor), 1, 0, true)
d.namespacePage.AddItem(d.podNamespaceUtsField, 1, 0, true)
d.namespacePage.AddItem(utils.EmptyBoxSpace(bgColor), 1, 0, true)
d.namespacePage.AddItem(mapRow01Layout, 1, 0, true)
d.namespacePage.AddItem(utils.EmptyBoxSpace(bgColor), 1, 0, true)
d.namespacePage.AddItem(mapRow02Layout, 1, 0, true)
d.namespacePage.SetBackgroundColor(bgColor)
}
// Display displays this primitive.
func (d *PodCreateDialog) Display() {
d.display = true
@ -692,6 +812,23 @@ func (d *PodCreateDialog) Focus(delegate func(p tview.Primitive)) { //nolint:cyc
delegate(d.podShmSizeField)
case createPodShmSizeSystemdFieldFocus:
delegate(d.podShmSizeSystemdField)
// namespace page
case createPodNamespaceShareFieldFocus:
delegate(d.podNamespaceShareField)
case createPodNamespacePidFieldFocus:
delegate(d.podNamespacePidField)
case createPodNamespaceUserFieldFocus:
delegate(d.podNamespaceUserField)
case createPodNamespaceUtsFieldFocus:
delegate(d.podNamespaceUtsField)
case createPodNamespaceUidmapFieldFocus:
delegate(d.podNamespaceUidmapField)
case createPodNamespaceSubuidNameFieldFocus:
delegate(d.podNamespaceSubuidNameField)
case createPodNamespaceGidmapFieldFocus:
delegate(d.podNamespaceGidmapField)
case createPodNamespaceSubgidNameFieldFocus:
delegate(d.podNamespaceSubgidNameField)
// category page
case createPodCategoryPagesFocus:
delegate(d.categoryPages)
@ -790,6 +927,18 @@ func (d *PodCreateDialog) InputHandler() func(event *tcell.EventKey, setFocus fu
}
}
if d.namespacePage.HasFocus() {
if handler := d.namespacePage.InputHandler(); handler != nil {
if event.Key() == tcell.KeyTab {
d.setNamespacePageNextFocus()
}
handler(event, setFocus)
return
}
}
if d.categories.HasFocus() {
if categroryHandler := d.categories.InputHandler(); categroryHandler != nil {
categroryHandler(event, setFocus)
@ -964,6 +1113,16 @@ func (d *PodCreateDialog) initData() {
d.podCPUSetMemsField.SetText("")
d.podShmSizeField.SetText("")
d.podShmSizeSystemdField.SetText("")
// namespace
d.podNamespaceShareField.SetText("")
d.podNamespacePidField.SetText("")
d.podNamespaceUserField.SetText("")
d.podNamespaceUtsField.SetText("")
d.podNamespaceUidmapField.SetText("")
d.podNamespaceSubuidNameField.SetText("")
d.podNamespaceGidmapField.SetText("")
d.podNamespaceSubgidNameField.SetText("")
}
func (d *PodCreateDialog) setResourcePagePageNextFocus() {
@ -1128,6 +1287,52 @@ func (d *PodCreateDialog) setNetworkingPageNextFocus() {
d.focusElement = createPodFormFocus
}
func (d *PodCreateDialog) setNamespacePageNextFocus() {
if d.podNamespaceShareField.HasFocus() {
d.focusElement = createPodNamespacePidFieldFocus
return
}
if d.podNamespacePidField.HasFocus() {
d.focusElement = createPodNamespaceUserFieldFocus
return
}
if d.podNamespaceUserField.HasFocus() {
d.focusElement = createPodNamespaceUtsFieldFocus
return
}
if d.podNamespaceUtsField.HasFocus() {
d.focusElement = createPodNamespaceUidmapFieldFocus
return
}
if d.podNamespaceUidmapField.HasFocus() {
d.focusElement = createPodNamespaceSubuidNameFieldFocus
return
}
if d.podNamespaceSubuidNameField.HasFocus() {
d.focusElement = createPodNamespaceGidmapFieldFocus
return
}
if d.podNamespaceGidmapField.HasFocus() {
d.focusElement = createPodNamespaceSubgidNameFieldFocus
return
}
d.focusElement = createPodFormFocus
}
// GetPodSpec returns pod create option spec.
func (d *PodCreateDialog) GetPodSpec() pods.CreateOptions { //nolint:gocognit,cyclop
var (
@ -1139,8 +1344,15 @@ func (d *PodCreateDialog) GetPodSpec() pods.CreateOptions { //nolint:gocognit,cy
network string
securityOpts []string
publish []string
namespaceShare []string
)
for _, nsshare := range strings.Split(d.podNamespaceShareField.GetText(), " ") {
if nsshare != "" {
namespaceShare = append(namespaceShare, nsshare)
}
}
for _, label := range strings.Split(d.podLabelsField.GetText(), " ") {
if label != "" {
split := strings.Split(label, "=")
@ -1222,30 +1434,38 @@ func (d *PodCreateDialog) GetPodSpec() pods.CreateOptions { //nolint:gocognit,cy
}
opts := pods.CreateOptions{
Name: strings.TrimSpace(d.podNameField.GetText()),
NoHost: d.podNoHostsCheckBox.IsChecked(),
Labels: labels,
DNSServer: dnsServers,
DNSOptions: dnsOptions,
DNSSearchDomain: dnsSearchDomains,
Infra: d.podInfraCheckBox.IsChecked(),
InfraImage: strings.TrimSpace(d.podInfraImageField.GetText()),
InfraCommand: strings.TrimSpace(d.podInfraCommandField.GetText()),
Hostname: strings.TrimSpace(d.podHostnameField.GetText()),
IPAddress: strings.TrimSpace(d.podIPAddressField.GetText()),
MacAddress: strings.TrimSpace(d.podMacAddressField.GetText()),
AddHost: addHost,
Network: network,
SecurityOpts: securityOpts,
Publish: publish,
Memory: strings.TrimSpace(d.podMemoryField.GetText()),
MemorySwap: strings.TrimSpace(d.podMemorySwapField.GetText()),
CPUs: strings.TrimSpace(d.podCPUsField.GetText()),
CPUShares: strings.TrimSpace(d.podCPUSharesField.GetText()),
CPUSetCPUs: strings.TrimSpace(d.podCPUSetCPUsField.GetText()),
CPUSetMems: strings.TrimSpace(d.podCPUSetMemsField.GetText()),
ShmSize: strings.TrimSpace(d.podShmSizeField.GetText()),
ShmSizeSystemd: strings.TrimSpace(d.podShmSizeSystemdField.GetText()),
Name: strings.TrimSpace(d.podNameField.GetText()),
NoHost: d.podNoHostsCheckBox.IsChecked(),
Labels: labels,
DNSServer: dnsServers,
DNSOptions: dnsOptions,
DNSSearchDomain: dnsSearchDomains,
Infra: d.podInfraCheckBox.IsChecked(),
InfraImage: strings.TrimSpace(d.podInfraImageField.GetText()),
InfraCommand: strings.TrimSpace(d.podInfraCommandField.GetText()),
Hostname: strings.TrimSpace(d.podHostnameField.GetText()),
IPAddress: strings.TrimSpace(d.podIPAddressField.GetText()),
MacAddress: strings.TrimSpace(d.podMacAddressField.GetText()),
AddHost: addHost,
Network: network,
SecurityOpts: securityOpts,
Publish: publish,
Memory: strings.TrimSpace(d.podMemoryField.GetText()),
MemorySwap: strings.TrimSpace(d.podMemorySwapField.GetText()),
CPUs: strings.TrimSpace(d.podCPUsField.GetText()),
CPUShares: strings.TrimSpace(d.podCPUSharesField.GetText()),
CPUSetCPUs: strings.TrimSpace(d.podCPUSetCPUsField.GetText()),
CPUSetMems: strings.TrimSpace(d.podCPUSetMemsField.GetText()),
ShmSize: strings.TrimSpace(d.podShmSizeField.GetText()),
ShmSizeSystemd: strings.TrimSpace(d.podShmSizeSystemdField.GetText()),
NamespaceShare: namespaceShare,
NamespacePid: strings.TrimSpace(d.podNamespacePidField.GetText()),
NamespaceUser: strings.TrimSpace(d.podNamespaceUserField.GetText()),
NamespaceUts: strings.TrimSpace(d.podNamespaceUtsField.GetText()),
NamespaceUidmap: strings.TrimSpace(d.podNamespaceUidmapField.GetText()),
NamespaceSubuidName: strings.TrimSpace(d.podNamespaceSubuidNameField.GetText()),
NamespaceGidmap: strings.TrimSpace(d.podNamespaceGidmapField.GetText()),
NamespaceSubgidName: strings.TrimSpace(d.podNamespaceSubgidNameField.GetText()),
}
return opts

View File

@ -12,7 +12,7 @@ import (
"github.com/rivo/tview"
)
func (pods *Pods) refresh() {
func (pods *Pods) refresh(_ int) {
pods.table.Clear()
expand := 1

61
ui/secrets/data.go Normal file
View File

@ -0,0 +1,61 @@
package secrets
import (
"fmt"
"strings"
"github.com/containers/podman-tui/pdcs/secrets"
"github.com/containers/podman-tui/ui/style"
"github.com/containers/podman/v5/pkg/domain/entities/types"
"github.com/rivo/tview"
"github.com/rs/zerolog/log"
)
// UpdateData retrieves secrets list data.
func (s *Secrets) UpdateData() {
secResponse, err := secrets.List()
if err != nil {
log.Error().Msgf("view: secrets update %v", err)
s.errorDialog.SetText(fmt.Sprintf("%v", err))
s.errorDialog.Display()
}
s.secretList.mu.Lock()
defer s.secretList.mu.Unlock()
s.secretList.report = secResponse
}
func (s *Secrets) getData() []*types.SecretInfoReport {
s.secretList.mu.Lock()
defer s.secretList.mu.Unlock()
data := s.secretList.report
return data
}
// ClearData clears table data.
func (s *Secrets) ClearData() {
s.secretList.mu.Lock()
defer s.secretList.mu.Unlock()
s.secretList.report = nil
s.table.Clear()
expand := 1
for i := range s.headers {
s.table.SetCell(0, i,
tview.NewTableCell(fmt.Sprintf("[::b]%s", strings.ToUpper(s.headers[i]))). //nolint:perfsprint
SetExpansion(expand).
SetBackgroundColor(style.PageHeaderBgColor).
SetTextColor(style.PageHeaderFgColor).
SetAlign(tview.AlignLeft).
SetSelectable(false))
}
s.table.SetTitle(fmt.Sprintf("[::b]%s[0]", strings.ToUpper(s.title)))
}

View File

@ -10,6 +10,7 @@ func (s *Secrets) Draw(screen tcell.Screen) {
secretViewX, secretViewY, secretViewW, secretViewH := s.GetInnerRect()
s.table.SetRect(secretViewX, secretViewY, secretViewW, secretViewH)
s.refresh(secretViewW)
s.table.SetBorder(true)
s.table.Draw(screen)

View File

@ -5,23 +5,12 @@ import (
"strings"
"time"
"github.com/containers/podman-tui/pdcs/secrets"
"github.com/containers/podman-tui/ui/style"
"github.com/docker/go-units"
"github.com/rivo/tview"
"github.com/rs/zerolog/log"
)
// UpdateData retrieves secrets list data.
func (s *Secrets) UpdateData() {
secResponse, err := secrets.List()
if err != nil {
log.Error().Msgf("view: secrets update %v", err)
s.errorDialog.SetText(fmt.Sprintf("%v", err))
s.errorDialog.Display()
}
func (s *Secrets) refresh(_ int) {
s.table.Clear()
expand := 1
@ -38,6 +27,7 @@ func (s *Secrets) UpdateData() {
}
rowIndex := 1
secResponse := s.getData()
s.table.SetTitle(fmt.Sprintf("[::b]%s[%d]", strings.ToUpper(s.title), len(secResponse)))
@ -81,22 +71,3 @@ func (s *Secrets) UpdateData() {
rowIndex++
}
}
// ClearData clears table data.
func (s *Secrets) ClearData() {
s.table.Clear()
expand := 1
for i := range s.headers {
s.table.SetCell(0, i,
tview.NewTableCell(fmt.Sprintf("[::b]%s", strings.ToUpper(s.headers[i]))). //nolint:perfsprint
SetExpansion(expand).
SetBackgroundColor(style.PageHeaderBgColor).
SetTextColor(style.PageHeaderFgColor).
SetAlign(tview.AlignLeft).
SetSelectable(false))
}
s.table.SetTitle(fmt.Sprintf("[::b]%s[0]", strings.ToUpper(s.title)))
}

View File

@ -4,10 +4,12 @@ import (
"errors"
"fmt"
"strings"
"sync"
"github.com/containers/podman-tui/ui/dialogs"
"github.com/containers/podman-tui/ui/secrets/secdialogs"
"github.com/containers/podman-tui/ui/style"
"github.com/containers/podman/v5/pkg/domain/entities/types"
"github.com/rivo/tview"
)
@ -38,9 +40,15 @@ type Secrets struct {
progressDialog *dialogs.ProgressDialog
confirmDialog *dialogs.ConfirmDialog
createDialog *secdialogs.SecretCreateDialog
secretList secretListReport
appFocusHandler func()
}
type secretListReport struct {
mu sync.Mutex
report []*types.SecretInfoReport
}
// NewSecrets returns secrets page view.
func NewSecrets() *Secrets {
secrets := &Secrets{

View File

@ -50,7 +50,7 @@ func (sys *System) addConnection() {
go func() {
err := sys.connectionAddFunc(name, uri, identity)
sys.progressDialog.Hide()
sys.UpdateConnectionsData()
sys.UpdateData()
if err != nil {
sys.displayError("ADD NEW CONNECTION ERROR", err)
@ -76,13 +76,13 @@ func (sys *System) connect() {
sys.eventDialog.SetText("")
sys.connectionConnectFunc(dest)
sys.UpdateConnectionsData()
sys.UpdateData()
}
func (sys *System) disconnect() {
sys.connectionDisconnectFunc()
sys.eventDialog.SetText("")
sys.UpdateConnectionsData()
sys.UpdateData()
}
func (sys *System) df() {
@ -229,7 +229,7 @@ func (sys *System) remove() {
return
}
sys.UpdateConnectionsData()
sys.UpdateData()
}()
}

View File

@ -7,12 +7,16 @@ import (
"github.com/containers/podman-tui/ui/style"
)
// UpdateConnectionsData retrieves connections list data.
func (sys *System) UpdateConnectionsData() {
// UpdateData retrieves connections list data.
func (sys *System) UpdateData() {
destinations := sys.connectionListFunc()
sys.connectionList.mu.Lock()
sys.connectionList.report = destinations
sys.connectionList.mu.Unlock()
sys.udpateConnectionDataStatus()
}
@ -34,8 +38,9 @@ func (sys *System) udpateConnectionDataStatus() {
func (sys *System) getConnectionsData() []registry.Connection {
sys.connectionList.mu.Lock()
defer sys.connectionList.mu.Unlock()
destReport := sys.connectionList.report
sys.connectionList.mu.Unlock()
return destReport
}

View File

@ -6,12 +6,12 @@ import (
// Draw draws this primitive onto the screen.
func (sys *System) Draw(screen tcell.Screen) { //nolint:cyclop
sys.refresh()
sys.Box.DrawForSubclass(screen, sys)
sysViewX, sysViewY, sysViewW, sysViewH := sys.GetInnerRect()
sys.connTable.SetRect(sysViewX, sysViewY, sysViewW, sysViewH)
sys.refresh(sysViewW)
sys.connTable.Draw(screen)
x, y, width, height := sys.connTable.GetInnerRect()

View File

@ -8,7 +8,7 @@ import (
"github.com/rivo/tview"
)
func (sys *System) refresh() {
func (sys *System) refresh(_ int) {
connections := sys.getConnectionsData()
sys.connTable.Clear()
sys.updateConnTableTitle(len(connections))

View File

@ -148,7 +148,7 @@ func NewSystem() *System {
sys.connPrgDialog.Hide()
registry.UnsetConnection()
sys.eventDialog.SetText("")
sys.UpdateConnectionsData()
sys.UpdateData()
})
// set connection create dialog functions

View File

@ -23,14 +23,16 @@ func (vols *Volumes) UpdateData() {
}
vols.volumeList.mu.Lock()
defer vols.volumeList.mu.Unlock()
vols.volumeList.report = volList
vols.volumeList.mu.Unlock()
}
func (vols *Volumes) getData() []*entities.VolumeListReport {
vols.volumeList.mu.Lock()
defer vols.volumeList.mu.Unlock()
data := vols.volumeList.report
vols.volumeList.mu.Unlock()
return data
}
@ -38,8 +40,9 @@ func (vols *Volumes) getData() []*entities.VolumeListReport {
// ClearData clears table data.
func (vols *Volumes) ClearData() {
vols.volumeList.mu.Lock()
defer vols.volumeList.mu.Unlock()
vols.volumeList.report = nil
vols.volumeList.mu.Unlock()
vols.table.Clear()

View File

@ -6,12 +6,12 @@ import (
// Draw draws this primitive onto the screen.
func (vols *Volumes) Draw(screen tcell.Screen) {
vols.refresh()
vols.Box.DrawForSubclass(screen, vols)
vols.Box.SetBorder(false)
x, y, width, height := vols.GetInnerRect()
vols.refresh(width)
vols.table.SetRect(x, y, width, height)
vols.table.SetBorder(true)

View File

@ -17,7 +17,7 @@ const (
volsTableMountPointColIndex
)
func (vols *Volumes) refresh() {
func (vols *Volumes) refresh(_ int) {
vols.table.Clear()
expand := 1

View File

@ -187,7 +187,7 @@ type BuildOptions struct {
// Log is a callback that will print a progress message. If no value
// is supplied, the message will be sent to Err (or os.Stderr, if Err
// is nil) by default.
Log func(format string, args ...interface{})
Log func(format string, args ...any)
// In is connected to stdin for RUN instructions.
In io.Reader
// Out is a place where non-error log messages are sent.
@ -236,6 +236,9 @@ type BuildOptions struct {
// ID mapping options to use if we're setting up our own user namespace
// when handling RUN instructions.
IDMappingOptions *IDMappingOptions
// InheritLabels controls whether or not built images will retain the labels
// which were set in their base images
InheritLabels types.OptionalBool
// AddCapabilities is a list of capabilities to add to the default set when
// handling RUN instructions.
AddCapabilities []string

View File

@ -29,7 +29,7 @@ const (
// identify working containers.
Package = "buildah"
// Version for the Package. Also used by .packit.sh for Packit builds.
Version = "1.39.4"
Version = "1.40.1"
// DefaultRuntime if containers.conf fails.
DefaultRuntime = "runc"
@ -260,13 +260,6 @@ func parseGitBuildContext(url string) (string, string, string) {
return gitBranchPart[0], gitSubdir, gitBranch
}
func isGitTag(remote, ref string) bool {
if _, err := exec.Command("git", "ls-remote", "--exit-code", remote, ref).Output(); err != nil {
return true
}
return false
}
func cloneToDirectory(url, dir string) ([]byte, string, error) {
var cmd *exec.Cmd
gitRepo, gitSubdir, gitRef := parseGitBuildContext(url)
@ -274,20 +267,18 @@ func cloneToDirectory(url, dir string) ([]byte, string, error) {
cmd = exec.Command("git", "init", dir)
combinedOutput, err := cmd.CombinedOutput()
if err != nil {
return combinedOutput, gitSubdir, fmt.Errorf("failed while performing `git init`: %w", err)
// Return err.Error() instead of err as we want buildah to override error code with more predictable
// value.
return combinedOutput, gitSubdir, fmt.Errorf("failed while performing `git init`: %s", err.Error())
}
// add origin
cmd = exec.Command("git", "remote", "add", "origin", gitRepo)
cmd.Dir = dir
combinedOutput, err = cmd.CombinedOutput()
if err != nil {
return combinedOutput, gitSubdir, fmt.Errorf("failed while performing `git remote add`: %w", err)
}
if gitRef != "" {
if ok := isGitTag(url, gitRef); ok {
gitRef += ":refs/tags/" + gitRef
}
// Return err.Error() instead of err as we want buildah to override error code with more predictable
// value.
return combinedOutput, gitSubdir, fmt.Errorf("failed while performing `git remote add`: %s", err.Error())
}
logrus.Debugf("fetching repo %q and branch (or commit ID) %q to %q", gitRepo, gitRef, dir)
@ -296,14 +287,18 @@ func cloneToDirectory(url, dir string) ([]byte, string, error) {
cmd.Dir = dir
combinedOutput, err = cmd.CombinedOutput()
if err != nil {
return combinedOutput, gitSubdir, fmt.Errorf("failed while performing `git fetch`: %w", err)
// Return err.Error() instead of err as we want buildah to override error code with more predictable
// value.
return combinedOutput, gitSubdir, fmt.Errorf("failed while performing `git fetch`: %s", err.Error())
}
cmd = exec.Command("git", "checkout", "FETCH_HEAD")
cmd.Dir = dir
combinedOutput, err = cmd.CombinedOutput()
if err != nil {
return combinedOutput, gitSubdir, fmt.Errorf("failed while performing `git checkout`: %w", err)
// Return err.Error() instead of err as we want buildah to override error code with more predictable
// value.
return combinedOutput, gitSubdir, fmt.Errorf("failed while performing `git checkout`: %s", err.Error())
}
return combinedOutput, gitSubdir, nil
}

View File

@ -1,10 +1,10 @@
package internal
import (
"maps"
"slices"
v1 "github.com/opencontainers/image-spec/specs-go/v1"
"golang.org/x/exp/maps"
)
// DeepCopyDescriptor copies a Descriptor, deeply copying its contents

View File

@ -21,8 +21,10 @@ import (
"github.com/containers/image/v5/signature"
"github.com/containers/image/v5/signature/signer"
storageTransport "github.com/containers/image/v5/storage"
"github.com/containers/image/v5/transports"
"github.com/containers/image/v5/types"
encconfig "github.com/containers/ocicrypt/config"
"github.com/containers/storage"
"github.com/sirupsen/logrus"
)
@ -175,8 +177,8 @@ type Copier struct {
// newCopier creates a Copier based on a runtime's system context.
// Note that fields in options *may* overwrite the counterparts of
// the specified system context. Please make sure to call `(*Copier).Close()`.
func (r *Runtime) newCopier(options *CopyOptions, reportResolvedReference *types.ImageReference) (*Copier, error) {
return NewCopier(options, r.SystemContext(), reportResolvedReference)
func (r *Runtime) newCopier(options *CopyOptions) (*Copier, error) {
return NewCopier(options, r.SystemContext())
}
// storageAllowedPolicyScopes overrides the policy for local storage
@ -223,7 +225,7 @@ func getDockerAuthConfig(name, passwd, creds, idToken string) (*types.DockerAuth
// NewCopier creates a Copier based on a provided system context.
// Note that fields in options *may* overwrite the counterparts of
// the specified system context. Please make sure to call `(*Copier).Close()`.
func NewCopier(options *CopyOptions, sc *types.SystemContext, reportResolvedReference *types.ImageReference) (*Copier, error) {
func NewCopier(options *CopyOptions, sc *types.SystemContext) (*Copier, error) {
c := Copier{extendTimeoutSocket: options.extendTimeoutSocket}
sysContextCopy := *sc
c.systemContext = &sysContextCopy
@ -330,7 +332,6 @@ func NewCopier(options *CopyOptions, sc *types.SystemContext, reportResolvedRefe
c.imageCopyOptions.SignBySigstorePrivateKeyFile = options.SignBySigstorePrivateKeyFile
c.imageCopyOptions.SignSigstorePrivateKeyPassphrase = options.SignSigstorePrivateKeyPassphrase
c.imageCopyOptions.ReportWriter = options.Writer
c.imageCopyOptions.ReportResolvedReference = reportResolvedReference
defaultContainerConfig, err := config.Default()
if err != nil {
@ -350,6 +351,12 @@ func (c *Copier) Close() error {
// Copy the source to the destination. Returns the bytes of the copied
// manifest which may be used for digest computation.
func (c *Copier) Copy(ctx context.Context, source, destination types.ImageReference) ([]byte, error) {
return c.copyInternal(ctx, source, destination, nil)
}
// Copy the source to the destination. Returns the bytes of the copied
// manifest which may be used for digest computation.
func (c *Copier) copyInternal(ctx context.Context, source, destination types.ImageReference, reportResolvedReference *types.ImageReference) ([]byte, error) {
logrus.Debugf("Copying source image %s to destination image %s", source.StringWithinTransport(), destination.StringWithinTransport())
// Avoid running out of time when running inside a systemd unit by
@ -454,6 +461,11 @@ func (c *Copier) Copy(ctx context.Context, source, destination types.ImageRefere
var returnManifest []byte
f := func() error {
opts := c.imageCopyOptions
// This is already set when `newCopier` was called but there is an option
// to override it by callers if needed.
if reportResolvedReference != nil {
opts.ReportResolvedReference = reportResolvedReference
}
if sourceInsecure != nil {
value := types.NewOptionalBool(*sourceInsecure)
opts.SourceCtx.DockerInsecureSkipTLSVerify = value
@ -472,6 +484,22 @@ func (c *Copier) Copy(ctx context.Context, source, destination types.ImageRefere
return returnManifest, retry.IfNecessary(ctx, f, &c.retryOptions)
}
func (c *Copier) copyToStorage(ctx context.Context, source, destination types.ImageReference) (*storage.Image, error) {
var resolvedReference types.ImageReference
_, err := c.copyInternal(ctx, source, destination, &resolvedReference)
if err != nil {
return nil, fmt.Errorf("internal error: unable to copy from source %s: %w", transports.ImageName(source), err)
}
if resolvedReference == nil {
return nil, fmt.Errorf("internal error: After attempting to copy %s, resolvedReference is nil", source)
}
_, image, err := storageTransport.ResolveReference(resolvedReference)
if err != nil {
return nil, fmt.Errorf("resolving an already-resolved reference %q to the pulled image: %w", transports.ImageName(resolvedReference), err)
}
return image, nil
}
// checkRegistrySourcesAllows checks the $BUILD_REGISTRY_SOURCES environment
// variable, if it's set. The contents are expected to be a JSON-encoded
// github.com/openshift/api/config/v1.Image, set by an OpenShift build

View File

@ -5,6 +5,9 @@ package libimage
import (
"context"
"time"
"github.com/containers/storage"
"github.com/sirupsen/logrus"
)
// ImageDiskUsage reports the total size of an image. That is the size
@ -36,49 +39,49 @@ func (r *Runtime) DiskUsage(ctx context.Context) ([]ImageDiskUsage, int64, error
return nil, -1, err
}
layerTree, err := r.newLayerTreeFromData(images, layers)
if err != nil {
return nil, -1, err
var totalSize int64
layerMap := make(map[string]*storage.Layer)
for _, layer := range layers {
layerMap[layer.ID] = &layer
if layer.UncompressedSize == -1 {
// size is unknown, we must manually diff the layer size which
// can be quite slow as it might have to walk all files
size, err := r.store.DiffSize("", layer.ID)
if err != nil {
return nil, -1, err
}
// cache the size now
layer.UncompressedSize = size
}
// count the total layer size here so we know we only count each layer once
totalSize += layer.UncompressedSize
}
var totalSize int64
visitedImages := make(map[string]bool)
visistedLayers := make(map[string]bool)
// First walk all images to count how often each layer is used.
// This is done so we know if the size for an image is shared between
// images that use the same layer or unique.
layerCount := make(map[string]int)
for _, image := range images {
walkImageLayers(image, layerMap, func(layer *storage.Layer) {
// Increment the count for each layer visit
layerCount[layer.ID] += 1
})
}
// Now that we actually have all the info walk again to add the sizes.
var allUsages []ImageDiskUsage
for _, image := range images {
usages, err := diskUsageForImage(ctx, image, layerTree)
usages, err := diskUsageForImage(ctx, image, layerMap, layerCount, &totalSize)
if err != nil {
return nil, -1, err
}
allUsages = append(allUsages, usages...)
if _, ok := visitedImages[image.ID()]; ok {
// Do not count an image twice
continue
}
visitedImages[image.ID()] = true
size, err := image.Size()
if err != nil {
return nil, -1, err
}
for _, layer := range layerTree.layersOf(image) {
if _, ok := visistedLayers[layer.ID]; ok {
// Do not count a layer twice, so remove its
// size from the image size.
size -= layer.UncompressedSize
continue
}
visistedLayers[layer.ID] = true
}
totalSize += size
}
return allUsages, totalSize, err
}
// diskUsageForImage returns the disk-usage baseistics for the specified image.
func diskUsageForImage(ctx context.Context, image *Image, tree *layerTree) ([]ImageDiskUsage, error) {
func diskUsageForImage(ctx context.Context, image *Image, layerMap map[string]*storage.Layer, layerCount map[string]int, totalSize *int64) ([]ImageDiskUsage, error) {
if err := image.isCorrupted(ctx, ""); err != nil {
return nil, err
}
@ -90,36 +93,25 @@ func diskUsageForImage(ctx context.Context, image *Image, tree *layerTree) ([]Im
Tag: "<none>",
}
// Shared, unique and total size.
parent, err := tree.parent(ctx, image)
if err != nil {
return nil, err
}
childIDs, err := tree.children(ctx, image, false)
if err != nil {
return nil, err
}
// Optimistically set unique size to the full size of the image.
size, err := image.Size()
if err != nil {
return nil, err
}
base.UniqueSize = size
if len(childIDs) > 0 {
// If we have children, we share everything.
base.SharedSize = base.UniqueSize
base.UniqueSize = 0
} else if parent != nil {
// If we have no children but a parent, remove the parent
// (shared) size from the unique one.
size, err := parent.Size()
if err != nil {
return nil, err
walkImageLayers(image, layerMap, func(layer *storage.Layer) {
// If the layer used by more than one image it shares its size
if layerCount[layer.ID] > 1 {
base.SharedSize += layer.UncompressedSize
} else {
base.UniqueSize += layer.UncompressedSize
}
base.UniqueSize -= size
base.SharedSize = size
})
// Count the image specific big data as well.
// store.BigDataSize() is not used intentionally, it is slower (has to take
// locks) and can fail.
// BigDataSizes is always correctly populated on new stores since c/storage
// commit a7d7fe8c9a (2016). It should be safe to assume that no such old
// store+image exist now so we don't bother. Worst case we report a few
// bytes to little.
for _, size := range image.storageImage.BigDataSizes {
base.UniqueSize += size
*totalSize += size
}
base.Size = base.SharedSize + base.UniqueSize
@ -155,3 +147,33 @@ func diskUsageForImage(ctx context.Context, image *Image, tree *layerTree) ([]Im
return results, nil
}
// walkImageLayers walks all layers in an image and calls the given function for each layer.
func walkImageLayers(image *Image, layerMap map[string]*storage.Layer, f func(layer *storage.Layer)) {
visited := make(map[string]struct{})
// Layers are walked recursively until it has no parent which means we reached the end.
// We must account for the fact that an image might have several top layers when id mappings are used.
layers := append([]string{image.storageImage.TopLayer}, image.storageImage.MappedTopLayers...)
for _, layerID := range layers {
for layerID != "" {
layer := layerMap[layerID]
if layer == nil {
logrus.Errorf("Local Storage is corrupt, layer %q missing from the storage", layerID)
break
}
if _, ok := visited[layerID]; ok {
// We have seen this layer before. Break here to
// a) Do not count the same layer twice that was shared between
// the TopLayer and MappedTopLayers layer chain.
// b) Prevent infinite loops, should not happen per c/storage
// design but it is good to be safer.
break
}
visited[layerID] = struct{}{}
f(layer)
// Set the layer for the next iteration, parent is empty if we reach the end.
layerID = layer.Parent
}
}
}

View File

@ -14,6 +14,7 @@ import (
"github.com/containerd/platforms"
"github.com/containers/common/libimage/platform"
"github.com/containers/image/v5/docker/reference"
"github.com/containers/image/v5/image"
"github.com/containers/image/v5/manifest"
storageTransport "github.com/containers/image/v5/storage"
"github.com/containers/image/v5/types"
@ -190,13 +191,21 @@ func (i *Image) IsReadOnly() bool {
}
// IsDangling returns true if the image is dangling, that is an untagged image
// without children.
// without children and not used in a manifest list.
func (i *Image) IsDangling(ctx context.Context) (bool, error) {
return i.isDangling(ctx, nil)
images, layers, err := i.runtime.getImagesAndLayers()
if err != nil {
return false, err
}
tree, err := i.runtime.newLayerTreeFromData(images, layers, true)
if err != nil {
return false, err
}
return i.isDangling(ctx, tree)
}
// isDangling returns true if the image is dangling, that is an untagged image
// without children. If tree is nil, it will created for this invocation only.
// without children and not used in a manifest list. If tree is nil, it will created for this invocation only.
func (i *Image) isDangling(ctx context.Context, tree *layerTree) (bool, error) {
if len(i.Names()) > 0 {
return false, nil
@ -205,7 +214,8 @@ func (i *Image) isDangling(ctx context.Context, tree *layerTree) (bool, error) {
if err != nil {
return false, err
}
return len(children) == 0, nil
_, usedInManfiestList := tree.manifestListDigests[i.Digest()]
return (len(children) == 0 && !usedInManfiestList), nil
}
// IsIntermediate returns true if the image is an intermediate image, that is
@ -453,13 +463,13 @@ func (i *Image) removeRecursive(ctx context.Context, rmMap map[string]*RemoveIma
skipRemove := false
numNames := len(i.Names())
// NOTE: the `numNames == 1` check is not only a performance
// NOTE: the `numNames != 1` check is not only a performance
// optimization but also preserves existing Podman/Docker behaviour.
// If image "foo" is used by a container and has only this tag/name,
// an `rmi foo` will not untag "foo" but instead attempt to remove the
// entire image. If there's a container using "foo", we should get an
// error.
if !(referencedBy == "" || numNames == 1) {
if referencedBy != "" && numNames != 1 {
byID := strings.HasPrefix(i.ID(), referencedBy)
byDigest := strings.HasPrefix(referencedBy, "sha256:")
if !options.Force {
@ -788,27 +798,25 @@ func (i *Image) Mount(_ context.Context, mountOptions []string, mountLabel strin
// Mountpoint returns the path to image's mount point. The path is empty if
// the image is not mounted.
func (i *Image) Mountpoint() (string, error) {
mountedTimes, err := i.runtime.store.Mounted(i.TopLayer())
if err != nil || mountedTimes == 0 {
if errors.Is(err, storage.ErrLayerUnknown) {
// Can happen, Podman did it, but there's no
// explanation why.
err = nil
for _, layerID := range append([]string{i.TopLayer()}, i.storageImage.MappedTopLayers...) {
mountedTimes, err := i.runtime.store.Mounted(layerID)
if err != nil {
if errors.Is(err, storage.ErrLayerUnknown) {
// Can happen, Podman did it, but there's no
// explanation why.
continue
}
return "", err
}
if mountedTimes > 0 {
layer, err := i.runtime.store.Layer(layerID)
if err != nil {
return "", err
}
return filepath.EvalSymlinks(layer.MountPoint)
}
return "", err
}
layer, err := i.runtime.store.Layer(i.TopLayer())
if err != nil {
return "", err
}
mountPoint, err := filepath.EvalSymlinks(layer.MountPoint)
if err != nil {
return "", err
}
return mountPoint, nil
return "", nil
}
// Unmount the image. Use force to ignore the reference counter and forcefully
@ -1002,7 +1010,7 @@ func (i *Image) Manifest(ctx context.Context) (rawManifest []byte, mimeType stri
if err != nil {
return nil, "", err
}
return src.GetManifest(ctx, nil)
return image.UnparsedInstance(src, nil).Manifest(ctx)
}
// getImageID creates an image object and uses the hex value of the config

View File

@ -3,6 +3,7 @@
package libimage
import (
"context"
"fmt"
"strings"
@ -13,7 +14,7 @@ import (
// Tree generates a tree for the specified image and its layers. Use
// `traverseChildren` to traverse the layers of all children. By default, only
// layers of the image are printed.
func (i *Image) Tree(traverseChildren bool) (string, error) {
func (i *Image) Tree(ctx context.Context, traverseChildren bool) (string, error) {
// NOTE: a string builder prevents us from copying to much data around
// and compile the string when and where needed.
sb := &strings.Builder{}

View File

@ -104,7 +104,7 @@ func (r *Runtime) Import(ctx context.Context, path string, options *ImportOption
return "", err
}
c, err := r.newCopier(&options.CopyOptions, nil)
c, err := r.newCopier(&options.CopyOptions)
if err != nil {
return "", err
}

View File

@ -6,6 +6,7 @@ import (
"context"
"time"
"github.com/containers/image/v5/image"
"github.com/containers/image/v5/manifest"
"github.com/containers/image/v5/types"
"github.com/opencontainers/go-digest"
@ -159,7 +160,7 @@ func (i *Image) Inspect(ctx context.Context, options *InspectOptions) (*ImageDat
if err != nil {
return nil, err
}
manifestRaw, manifestType, err := src.GetManifest(ctx, nil)
manifestRaw, manifestType, err := image.UnparsedInstance(src, nil).Manifest(ctx)
if err != nil {
return nil, err
}

View File

@ -8,6 +8,7 @@ import (
"github.com/containers/storage"
storageTypes "github.com/containers/storage/types"
digest "github.com/opencontainers/go-digest"
ociv1 "github.com/opencontainers/image-spec/specs-go/v1"
"github.com/sirupsen/logrus"
)
@ -22,6 +23,10 @@ type layerTree struct {
// emptyImages do not have any top-layer so we cannot create a
// *layerNode for them.
emptyImages []*Image
// manifestList keep track of images based on their digest.
// Library will use this map when checking if a image is dangling.
// If an image is used in a manifestList it is NOT dangling
manifestListDigests map[digest.Digest]struct{}
}
// node returns a layerNode for the specified layerID.
@ -95,15 +100,16 @@ func (r *Runtime) newFreshLayerTree() (*layerTree, error) {
if err != nil {
return nil, err
}
return r.newLayerTreeFromData(images, layers)
return r.newLayerTreeFromData(images, layers, false)
}
// newLayerTreeFromData extracts a layerTree from the given the layers and images.
// The caller is responsible for (layers, images) being consistent.
func (r *Runtime) newLayerTreeFromData(images []*Image, layers []storage.Layer) (*layerTree, error) {
func (r *Runtime) newLayerTreeFromData(images []*Image, layers []storage.Layer, generateManifestDigestList bool) (*layerTree, error) {
tree := layerTree{
nodes: make(map[string]*layerNode),
ociCache: make(map[string]*ociv1.Image),
nodes: make(map[string]*layerNode),
ociCache: make(map[string]*ociv1.Image),
manifestListDigests: make(map[digest.Digest]struct{}),
}
// First build a tree purely based on layer information.
@ -124,6 +130,30 @@ func (r *Runtime) newLayerTreeFromData(images []*Image, layers []storage.Layer)
topLayer := img.TopLayer()
if topLayer == "" {
tree.emptyImages = append(tree.emptyImages, img)
// When img is a manifest list, cache the lists of
// digests refereenced in manifest list. Digests can
// be used to check for dangling images.
if !generateManifestDigestList {
continue
}
// ignore errors, common errors are
// - image is not manifest
// - image has been removed from the store in the meantime
// In all cases we should ensure image listing still works and not error out.
mlist, err := img.ToManifestList()
if err != nil {
// If it is not a manifest it likely is a regular image so just ignore it.
// If the image is unknown that likely means there was a race where the image/manifest
// was removed after out MultiList() call so we ignore that as well.
if errors.Is(err, ErrNotAManifestList) || errors.Is(err, storageTypes.ErrImageUnknown) {
continue
}
return nil, err
}
for _, digest := range mlist.list.Instances() {
tree.manifestListDigests[digest] = struct{}{}
}
continue
}
node, exists := tree.nodes[topLayer]
@ -141,19 +171,6 @@ func (r *Runtime) newLayerTreeFromData(images []*Image, layers []storage.Layer)
return &tree, nil
}
// layersOf returns all storage layers of the specified image.
func (t *layerTree) layersOf(image *Image) []*storage.Layer {
var layers []*storage.Layer
node := t.node(image.TopLayer())
for node != nil {
if node.layer != nil {
layers = append(layers, node.layer)
}
node = node.parent
}
return layers
}
// children returns the child images of parent. Child images are images with
// either the same top layer as parent or parent being the true parent layer.
// Furthermore, the history of the parent and child images must match with the

View File

@ -30,7 +30,7 @@ func (r *Runtime) doLoadReference(ctx context.Context, ref types.ImageReference,
case dockerArchiveTransport.Transport.Name():
images, err = r.loadMultiImageDockerArchive(ctx, ref, &options.CopyOptions)
default:
images, err = r.copyFromDefault(ctx, ref, &options.CopyOptions)
_, images, err = r.copyFromDefault(ctx, ref, &options.CopyOptions)
}
return images, ref.Transport().Name(), err
}
@ -49,6 +49,9 @@ func (r *Runtime) LoadReference(ctx context.Context, ref types.ImageReference, o
// Load loads one or more images (depending on the transport) from the
// specified path. The path may point to an image the following transports:
// oci, oci-archive, dir, docker-archive.
//
// Load returns a string slice with names of recently loaded images.
// If images are unnamed in the source, it returns a string slice of image IDs instead.
func (r *Runtime) Load(ctx context.Context, path string, options *LoadOptions) ([]string, error) {
logrus.Debugf("Loading image from %q", path)
@ -112,7 +115,7 @@ func (r *Runtime) Load(ctx context.Context, path string, options *LoadOptions) (
// Give a decent error message if nothing above worked.
// we want the colon here for the multiline error
//nolint:revive
//nolint:revive,staticcheck
loadError := errors.New("payload does not match any of the supported image formats:")
for _, err := range loadErrors {
loadError = fmt.Errorf("%v\n * %v", loadError, err)
@ -142,7 +145,8 @@ func (r *Runtime) loadMultiImageDockerArchive(ctx context.Context, ref types.Ima
// should.
path := ref.StringWithinTransport()
if err := fileutils.Exists(path); err != nil {
return r.copyFromDockerArchive(ctx, ref, options)
_, names, err := r.copyFromDockerArchive(ctx, ref, options)
return names, err
}
reader, err := dockerArchiveTransport.NewReader(r.systemContextCopy(), path)
@ -163,7 +167,7 @@ func (r *Runtime) loadMultiImageDockerArchive(ctx context.Context, ref types.Ima
var copiedImages []string
for _, list := range refLists {
for _, listRef := range list {
names, err := r.copyFromDockerArchiveReaderReference(ctx, reader, listRef, options)
_, names, err := r.copyFromDockerArchiveReaderReference(ctx, reader, listRef, options)
if err != nil {
return nil, err
}

View File

@ -18,6 +18,7 @@ import (
"github.com/containers/common/pkg/supplemented"
imageCopy "github.com/containers/image/v5/copy"
"github.com/containers/image/v5/docker"
"github.com/containers/image/v5/image"
"github.com/containers/image/v5/manifest"
"github.com/containers/image/v5/oci/layout"
"github.com/containers/image/v5/signature"
@ -370,11 +371,12 @@ func (i *Image) IsManifestList(ctx context.Context) (bool, error) {
if err != nil {
return false, err
}
imgRef, err := ref.NewImageSource(ctx, i.runtime.systemContextCopy())
imgSrc, err := ref.NewImageSource(ctx, i.runtime.systemContextCopy())
if err != nil {
return false, err
}
_, manifestType, err := imgRef.GetManifest(ctx, nil)
defer imgSrc.Close()
_, manifestType, err := image.UnparsedInstance(imgSrc, nil).Manifest(ctx)
if err != nil {
return false, err
}
@ -717,7 +719,7 @@ func (m *ManifestList) AnnotateInstance(d digest.Digest, options *ManifestListAn
return err
}
defer src.Close()
subjectManifestBytes, subjectManifestType, err := src.GetManifest(ctx, nil)
subjectManifestBytes, subjectManifestType, err := image.UnparsedInstance(src, nil).Manifest(ctx)
if err != nil {
return err
}
@ -792,7 +794,7 @@ func (m *ManifestList) Push(ctx context.Context, destination string, options *Ma
// NOTE: we're using the logic in copier to create a proper
// types.SystemContext. This prevents us from having an error prone
// code duplicate here.
copier, err := m.image.runtime.newCopier(&options.CopyOptions, nil)
copier, err := m.image.runtime.newCopier(&options.CopyOptions)
if err != nil {
return "", err
}

View File

@ -149,7 +149,7 @@ func LoadFromImage(store storage.Store, image string) (string, List, error) {
}
manifestList, err := manifests.FromBlob(manifestBytes)
if err != nil {
return "", nil, err
return "", nil, fmt.Errorf("decoding manifest blob for image %q: %w", image, err)
}
list := &list{
List: manifestList,
@ -526,7 +526,7 @@ func (l *list) Add(ctx context.Context, sys *types.SystemContext, ref types.Imag
var instanceInfos []instanceInfo
var manifestDigest digest.Digest
primaryManifestBytes, primaryManifestType, err := src.GetManifest(ctx, nil)
primaryManifestBytes, primaryManifestType, err := image.UnparsedInstance(src, nil).Manifest(ctx)
if err != nil {
return "", fmt.Errorf("reading manifest from %q: %w", transports.ImageName(ref), err)
}
@ -613,7 +613,8 @@ func (l *list) Add(ctx context.Context, sys *types.SystemContext, ref types.Imag
knownConfigTypes := []string{manifest.DockerV2Schema2ConfigMediaType, v1.MediaTypeImageConfig}
for _, instanceInfo := range instanceInfos {
manifestBytes, manifestType, err := src.GetManifest(ctx, instanceInfo.instanceDigest)
unparsedInstance := image.UnparsedInstance(src, instanceInfo.instanceDigest)
manifestBytes, manifestType, err := unparsedInstance.Manifest(ctx)
if err != nil {
return "", fmt.Errorf("reading manifest from %q, instance %q: %w", transports.ImageName(ref), instanceInfo.instanceDigest, err)
}
@ -625,7 +626,7 @@ func (l *list) Add(ctx context.Context, sys *types.SystemContext, ref types.Imag
hasPlatformConfig := instanceInfo.ArtifactType == "" && slices.Contains(knownConfigTypes, instanceInfo.ConfigInfo.MediaType)
needToParsePlatformConfig := (instanceInfo.OS == "" || instanceInfo.Architecture == "")
if hasPlatformConfig && needToParsePlatformConfig {
img, err := image.FromUnparsedImage(ctx, sys, image.UnparsedInstance(src, instanceInfo.instanceDigest))
img, err := image.FromUnparsedImage(ctx, sys, unparsedInstance)
if err != nil {
return "", fmt.Errorf("reading configuration blob from %q: %w", transports.ImageName(ref), err)
}
@ -712,12 +713,12 @@ func (l *list) AddArtifact(ctx context.Context, sys *types.SystemContext, option
// reason.
var subject *v1.Descriptor
if options.SubjectReference != nil {
subjectReference, err := options.SubjectReference.NewImageSource(ctx, sys)
subjectSource, err := options.SubjectReference.NewImageSource(ctx, sys)
if err != nil {
return "", fmt.Errorf("setting up to read manifest and configuration from subject %q: %w", transports.ImageName(options.SubjectReference), err)
}
defer subjectReference.Close()
subjectManifestBytes, subjectManifestType, err := subjectReference.GetManifest(ctx, nil)
defer subjectSource.Close()
subjectManifestBytes, subjectManifestType, err := image.UnparsedInstance(subjectSource, nil).Manifest(ctx)
if err != nil {
return "", fmt.Errorf("reading manifest from subject %q: %w", transports.ImageName(options.SubjectReference), err)
}

View File

@ -30,7 +30,7 @@ func NormalizeName(name string) (reference.Named, error) {
// Enforce "localhost" if needed.
registry := reference.Domain(named)
if !(strings.ContainsAny(registry, ".:") || registry == "localhost") {
if !strings.ContainsAny(registry, ".:") && registry != "localhost" {
name = toLocalImageName(ref.String())
}

View File

@ -21,7 +21,6 @@ import (
ociTransport "github.com/containers/image/v5/oci/layout"
"github.com/containers/image/v5/pkg/shortnames"
storageTransport "github.com/containers/image/v5/storage"
"github.com/containers/image/v5/transports"
"github.com/containers/image/v5/transports/alltransports"
"github.com/containers/image/v5/types"
"github.com/containers/storage"
@ -52,6 +51,10 @@ type PullOptions struct {
// The error is storage.ErrImageUnknown iff the pull policy is set to "never"
// and no local image has been found. This allows for an easier integration
// into some users of this package (e.g., Buildah).
//
// Pull returns a slice of the pulled images.
//
// WARNING: the Digest field of the returned image might not be a value relevant to the user issuing the pull.
func (r *Runtime) Pull(ctx context.Context, name string, pullPolicy config.PullPolicy, options *PullOptions) (_ []*Image, pullError error) {
logrus.Debugf("Pulling image %s (policy: %s)", name, pullPolicy)
if r.eventChannel != nil {
@ -156,7 +159,7 @@ func (r *Runtime) Pull(ctx context.Context, name string, pullPolicy config.PullP
options.Variant = r.systemContext.VariantChoice
}
var pulledImages []string
var pulledImages []*Image
// Dispatch the copy operation.
switch ref.Transport().Name() {
@ -166,24 +169,18 @@ func (r *Runtime) Pull(ctx context.Context, name string, pullPolicy config.PullP
// DOCKER ARCHIVE
case dockerArchiveTransport.Transport.Name():
pulledImages, err = r.copyFromDockerArchive(ctx, ref, &options.CopyOptions)
pulledImages, _, err = r.copyFromDockerArchive(ctx, ref, &options.CopyOptions)
// ALL OTHER TRANSPORTS
default:
pulledImages, err = r.copyFromDefault(ctx, ref, &options.CopyOptions)
pulledImages, _, err = r.copyFromDefault(ctx, ref, &options.CopyOptions)
}
if err != nil {
return nil, err
}
localImages := []*Image{}
for _, iName := range pulledImages {
image, _, err := r.LookupImage(iName, nil)
if err != nil {
return nil, fmt.Errorf("locating pulled image %q name in containers storage: %w", iName, err)
}
for _, image := range pulledImages {
// Note that we can ignore the 2nd return value here. Some
// images may ship with "wrong" platform, but we already warn
// about it. Throwing an error is not (yet) the plan.
@ -206,11 +203,9 @@ func (r *Runtime) Pull(ctx context.Context, name string, pullPolicy config.PullP
// Note that we use the input name here to preserve the transport data.
r.writeEvent(&Event{ID: image.ID(), Name: name, Time: time.Now(), Type: EventTypeImagePull})
}
localImages = append(localImages, image)
}
return localImages, pullError
return pulledImages, pullError
}
// nameFromAnnotations returns a reference string to be used as an image name,
@ -229,10 +224,10 @@ func nameFromAnnotations(annotations map[string]string) string {
// copyFromDefault is the default copier for a number of transports. Other
// transports require some specific dancing, sometimes Yoga.
func (r *Runtime) copyFromDefault(ctx context.Context, ref types.ImageReference, options *CopyOptions) ([]string, error) {
c, err := r.newCopier(options, nil)
func (r *Runtime) copyFromDefault(ctx context.Context, ref types.ImageReference, options *CopyOptions) ([]*Image, []string, error) {
c, err := r.newCopier(options)
if err != nil {
return nil, err
return nil, nil, err
}
defer c.Close()
@ -243,7 +238,7 @@ func (r *Runtime) copyFromDefault(ctx context.Context, ref types.ImageReference,
// Normalize to docker.io if needed (see containers/podman/issues/10998).
named, err := reference.ParseNormalizedNamed(strings.TrimLeft(ref.StringWithinTransport(), ":/"))
if err != nil {
return nil, err
return nil, nil, err
}
imageName = named.String()
storageName = imageName
@ -252,7 +247,7 @@ func (r *Runtime) copyFromDefault(ctx context.Context, ref types.ImageReference,
// Normalize to docker.io if needed (see containers/podman/issues/10998).
named, err := reference.ParseNormalizedNamed(ref.StringWithinTransport())
if err != nil {
return nil, err
return nil, nil, err
}
imageName = named.String()
storageName = imageName
@ -264,7 +259,7 @@ func (r *Runtime) copyFromDefault(ctx context.Context, ref types.ImageReference,
// the path to a directory as the name.
storageName, err = getImageID(ctx, ref, nil)
if err != nil {
return nil, err
return nil, nil, err
}
imageName = "sha256:" + storageName[1:]
} else { // If the OCI-reference includes an image reference, use it
@ -275,7 +270,7 @@ func (r *Runtime) copyFromDefault(ctx context.Context, ref types.ImageReference,
case ociArchiveTransport.Transport.Name():
manifestDescriptor, err := ociArchiveTransport.LoadManifestDescriptorWithContext(r.SystemContext(), ref)
if err != nil {
return nil, err
return nil, nil, err
}
storageName = nameFromAnnotations(manifestDescriptor.Annotations)
switch len(storageName) {
@ -283,13 +278,13 @@ func (r *Runtime) copyFromDefault(ctx context.Context, ref types.ImageReference,
// If there's no reference name in the annotations, compute an ID.
storageName, err = getImageID(ctx, ref, nil)
if err != nil {
return nil, err
return nil, nil, err
}
imageName = "sha256:" + storageName[1:]
default:
named, err := NormalizeName(storageName)
if err != nil {
return nil, err
return nil, nil, err
}
imageName = named.String()
storageName = imageName
@ -299,7 +294,7 @@ func (r *Runtime) copyFromDefault(ctx context.Context, ref types.ImageReference,
storageName = ref.StringWithinTransport()
named := ref.DockerReference()
if named == nil {
return nil, fmt.Errorf("could not get an image name for storage reference %q", ref)
return nil, nil, fmt.Errorf("could not get an image name for storage reference %q", ref)
}
imageName = named.String()
@ -309,7 +304,7 @@ func (r *Runtime) copyFromDefault(ctx context.Context, ref types.ImageReference,
// instead of looking at the StringWithinTransport().
storageName, err = getImageID(ctx, ref, nil)
if err != nil {
return nil, err
return nil, nil, err
}
imageName = "sha256:" + storageName[1:]
}
@ -317,11 +312,14 @@ func (r *Runtime) copyFromDefault(ctx context.Context, ref types.ImageReference,
// Create a storage reference.
destRef, err := storageTransport.Transport.ParseStoreReference(r.store, storageName)
if err != nil {
return nil, fmt.Errorf("parsing %q: %w", storageName, err)
return nil, nil, fmt.Errorf("parsing %q: %w", storageName, err)
}
_, err = c.Copy(ctx, ref, destRef)
return []string{imageName}, err
image, err := c.copyToStorage(ctx, ref, destRef)
if err != nil {
return nil, nil, fmt.Errorf("unable to perform copy: %w", err)
}
resolvedImage := r.storageToImage(image, nil)
return []*Image{resolvedImage}, []string{imageName}, err
}
// storageReferencesFromArchiveReader returns a slice of image references inside the
@ -368,12 +366,12 @@ func (r *Runtime) storageReferencesReferencesFromArchiveReader(ctx context.Conte
}
// copyFromDockerArchive copies one image from the specified reference.
func (r *Runtime) copyFromDockerArchive(ctx context.Context, ref types.ImageReference, options *CopyOptions) ([]string, error) {
func (r *Runtime) copyFromDockerArchive(ctx context.Context, ref types.ImageReference, options *CopyOptions) ([]*Image, []string, error) {
// There may be more than one image inside the docker archive, so we
// need a quick glimpse inside.
reader, readerRef, err := dockerArchiveTransport.NewReaderForReference(&r.systemContext, ref)
if err != nil {
return nil, err
return nil, nil, err
}
defer func() {
if err := reader.Close(); err != nil {
@ -385,35 +383,38 @@ func (r *Runtime) copyFromDockerArchive(ctx context.Context, ref types.ImageRefe
}
// copyFromDockerArchiveReaderReference copies the specified readerRef from reader.
func (r *Runtime) copyFromDockerArchiveReaderReference(ctx context.Context, reader *dockerArchiveTransport.Reader, readerRef types.ImageReference, options *CopyOptions) ([]string, error) {
c, err := r.newCopier(options, nil)
func (r *Runtime) copyFromDockerArchiveReaderReference(ctx context.Context, reader *dockerArchiveTransport.Reader, readerRef types.ImageReference, options *CopyOptions) ([]*Image, []string, error) {
c, err := r.newCopier(options)
if err != nil {
return nil, err
return nil, nil, err
}
defer c.Close()
// Get a slice of storage references we can copy.
references, destNames, err := r.storageReferencesReferencesFromArchiveReader(ctx, readerRef, reader)
if err != nil {
return nil, err
return nil, nil, err
}
images := []*Image{}
// Now copy all of the images. Use readerRef for performance.
for _, destRef := range references {
if _, err := c.Copy(ctx, readerRef, destRef); err != nil {
return nil, err
image, err := c.copyToStorage(ctx, readerRef, destRef)
if err != nil {
return nil, nil, err
}
resolvedImage := r.storageToImage(image, nil)
images = append(images, resolvedImage)
}
return destNames, nil
return images, destNames, nil
}
// copyFromRegistry pulls the specified, possibly unqualified, name from a
// registry. On successful pull it returns the ID of the image in local
// storage.
// registry. On successful pull it returns slice of the pulled images.
//
// If options.All is set, all tags from the specified registry will be pulled.
func (r *Runtime) copyFromRegistry(ctx context.Context, ref types.ImageReference, inputName string, pullPolicy config.PullPolicy, options *PullOptions) ([]string, error) {
func (r *Runtime) copyFromRegistry(ctx context.Context, ref types.ImageReference, inputName string, pullPolicy config.PullPolicy, options *PullOptions) ([]*Image, error) {
// Sanity check.
if err := pullPolicy.Validate(); err != nil {
return nil, err
@ -424,7 +425,7 @@ func (r *Runtime) copyFromRegistry(ctx context.Context, ref types.ImageReference
if err != nil {
return nil, err
}
return []string{pulled}, nil
return []*Image{pulled}, nil
}
// Copy all tags
@ -434,7 +435,7 @@ func (r *Runtime) copyFromRegistry(ctx context.Context, ref types.ImageReference
return nil, err
}
pulledIDs := []string{}
pulledImages := []*Image{}
for _, tag := range tags {
select { // Let's be gentle with Podman remote.
case <-ctx.Done():
@ -450,19 +451,18 @@ func (r *Runtime) copyFromRegistry(ctx context.Context, ref types.ImageReference
if err != nil {
return nil, err
}
pulledIDs = append(pulledIDs, pulled)
pulledImages = append(pulledImages, pulled)
}
return pulledIDs, nil
return pulledImages, nil
}
// copySingleImageFromRegistry pulls the specified, possibly unqualified, name
// from a registry. On successful pull it returns the ID of the image in local
// storage (or, FIXME, a name/ID? that could be resolved in local storage)
func (r *Runtime) copySingleImageFromRegistry(ctx context.Context, imageName string, pullPolicy config.PullPolicy, options *PullOptions) (string, error) { //nolint:gocyclo
// from a registry. On successful pull it returns the Image from the local storage.
func (r *Runtime) copySingleImageFromRegistry(ctx context.Context, imageName string, pullPolicy config.PullPolicy, options *PullOptions) (*Image, error) { //nolint:gocyclo
// Sanity check.
if err := pullPolicy.Validate(); err != nil {
return "", err
return nil, err
}
var (
@ -487,14 +487,7 @@ func (r *Runtime) copySingleImageFromRegistry(ctx context.Context, imageName str
if options.OS != runtime.GOOS {
lookupImageOptions.OS = options.OS
}
// FIXME: We sometimes return resolvedImageName from this function.
// The function documentation says this returns an image ID, resolvedImageName is frequently not an image ID.
//
// Ultimately Runtime.Pull looks up the returned name... again, possibly finding some other match
// than we did.
//
// This should be restructured so that the image we found here is returned to the caller of Pull
// directly, without another image -> name -> image round-trip and possible inconsistency.
localImage, resolvedImageName, err = r.LookupImage(imageName, lookupImageOptions)
if err != nil && !errors.Is(err, storage.ErrImageUnknown) {
logrus.Errorf("Looking up %s in local storage: %v", imageName, err)
@ -525,23 +518,23 @@ func (r *Runtime) copySingleImageFromRegistry(ctx context.Context, imageName str
if pullPolicy == config.PullPolicyNever {
if localImage != nil {
logrus.Debugf("Pull policy %q and %s resolved to local image %s", pullPolicy, imageName, resolvedImageName)
return resolvedImageName, nil
return localImage, nil
}
logrus.Debugf("Pull policy %q but no local image has been found for %s", pullPolicy, imageName)
return "", fmt.Errorf("%s: %w", imageName, storage.ErrImageUnknown)
return nil, fmt.Errorf("%s: %w", imageName, storage.ErrImageUnknown)
}
if pullPolicy == config.PullPolicyMissing && localImage != nil {
return resolvedImageName, nil
return localImage, nil
}
// If we looked up the image by ID, we cannot really pull from anywhere.
if localImage != nil && strings.HasPrefix(localImage.ID(), imageName) {
switch pullPolicy {
case config.PullPolicyAlways:
return "", fmt.Errorf("pull policy is always but image has been referred to by ID (%s)", imageName)
return nil, fmt.Errorf("pull policy is always but image has been referred to by ID (%s)", imageName)
default:
return resolvedImageName, nil
return localImage, nil
}
}
@ -566,9 +559,9 @@ func (r *Runtime) copySingleImageFromRegistry(ctx context.Context, imageName str
resolved, err := shortnames.Resolve(sys, imageName)
if err != nil {
if localImage != nil && pullPolicy == config.PullPolicyNewer {
return resolvedImageName, nil
return localImage, nil
}
return "", err
return nil, err
}
// NOTE: Below we print the description from the short-name resolution.
@ -598,10 +591,9 @@ func (r *Runtime) copySingleImageFromRegistry(ctx context.Context, imageName str
if socketPath, ok := os.LookupEnv("NOTIFY_SOCKET"); ok {
options.extendTimeoutSocket = socketPath
}
var resolvedReference types.ImageReference
c, err := r.newCopier(&options.CopyOptions, &resolvedReference)
c, err := r.newCopier(&options.CopyOptions)
if err != nil {
return "", err
return nil, err
}
defer c.Close()
@ -611,7 +603,7 @@ func (r *Runtime) copySingleImageFromRegistry(ctx context.Context, imageName str
logrus.Debugf("Attempting to pull candidate %s for %s", candidateString, imageName)
srcRef, err := registryTransport.NewReference(candidate.Value)
if err != nil {
return "", err
return nil, err
}
if pullPolicy == config.PullPolicyNewer && localImage != nil {
@ -629,18 +621,19 @@ func (r *Runtime) copySingleImageFromRegistry(ctx context.Context, imageName str
destRef, err := storageTransport.Transport.ParseStoreReference(r.store, candidate.Value.String())
if err != nil {
return "", err
return nil, err
}
if err := writeDesc(); err != nil {
return "", err
return nil, err
}
if options.Writer != nil {
if _, err := io.WriteString(options.Writer, fmt.Sprintf("Trying to pull %s...\n", candidateString)); err != nil {
return "", err
return nil, err
}
}
if _, err := c.Copy(ctx, srcRef, destRef); err != nil {
image, err := c.copyToStorage(ctx, srcRef, destRef)
if err != nil {
logrus.Debugf("Error pulling candidate %s: %v", candidateString, err)
pullErrors = append(pullErrors, err)
continue
@ -651,25 +644,18 @@ func (r *Runtime) copySingleImageFromRegistry(ctx context.Context, imageName str
// read-only which can cause issues.
logrus.Errorf("Error recording short-name alias %q: %v", candidateString, err)
}
logrus.Debugf("Pulled candidate %s successfully", candidateString)
if resolvedReference == nil { // resolvedReference should always be set for storageTransport destinations
return "", fmt.Errorf("internal error: After pulling %s, resolvedReference is nil", candidateString)
}
_, image, err := storageTransport.ResolveReference(resolvedReference)
if err != nil {
return "", fmt.Errorf("resolving an already-resolved reference %q to the pulled image: %w", transports.ImageName(resolvedReference), err)
}
return image.ID, nil
resolvedImage := r.storageToImage(image, nil)
return resolvedImage, err
}
if localImage != nil && pullPolicy == config.PullPolicyNewer {
return resolvedImageName, nil
return localImage, nil
}
if len(pullErrors) == 0 {
return "", fmt.Errorf("internal error: no image pulled (pull policy %s)", pullPolicy)
return nil, fmt.Errorf("internal error: no image pulled (pull policy %s)", pullPolicy)
}
return "", resolved.FormatPullErrors(pullErrors)
return nil, resolved.FormatPullErrors(pullErrors)
}

View File

@ -118,7 +118,7 @@ func (r *Runtime) Push(ctx context.Context, source, destination string, options
}
}
c, err := r.newCopier(&options.CopyOptions, nil)
c, err := r.newCopier(&options.CopyOptions)
if err != nil {
return nil, err
}

View File

@ -634,7 +634,7 @@ func (r *Runtime) ListImages(ctx context.Context, options *ListImagesOptions) ([
var tree *layerTree
if needsLayerTree {
tree, err = r.newLayerTreeFromData(images, snapshot.Layers)
tree, err = r.newLayerTreeFromData(images, snapshot.Layers, true)
if err != nil {
return nil, err
}

View File

@ -119,7 +119,7 @@ func (r *Runtime) saveSingleImage(ctx context.Context, name, format, path string
return err
}
c, err := r.newCopier(&options.CopyOptions, nil)
c, err := r.newCopier(&options.CopyOptions)
if err != nil {
return err
}
@ -204,7 +204,7 @@ func (r *Runtime) saveDockerArchive(ctx context.Context, names []string, path st
copyOpts := options.CopyOptions
copyOpts.dockerArchiveAdditionalTags = local.tags
c, err := r.newCopier(&copyOpts, nil)
c, err := r.newCopier(&copyOpts)
if err != nil {
return err
}

View File

@ -217,7 +217,7 @@ func (r *Runtime) searchImageInRegistry(ctx context.Context, term, registry stri
paramsArr := []SearchResult{}
for i := range limit {
// Check whether query matches filters
if !(filterMatchesAutomatedFilter(&options.Filter, results[i]) && filterMatchesOfficialFilter(&options.Filter, results[i]) && filterMatchesStarFilter(&options.Filter, results[i])) {
if !filterMatchesAutomatedFilter(&options.Filter, results[i]) || !filterMatchesOfficialFilter(&options.Filter, results[i]) || !filterMatchesStarFilter(&options.Filter, results[i]) {
continue
}
official := ""

View File

@ -24,10 +24,15 @@ var (
// NameRegex is a regular expression to validate names.
// This must NOT be changed.
NameRegex = regexp.Delayed("^[a-zA-Z0-9][a-zA-Z0-9_.-]*$")
// RegexError is thrown in presence of an invalid name.
RegexError = fmt.Errorf("names must match [a-zA-Z0-9][a-zA-Z0-9_.-]*: %w", ErrInvalidArg) // nolint:revive // This lint is new and we do not want to break the API.
// ErrInvalidName is thrown in presence of an invalid name.
ErrInvalidName = fmt.Errorf("names must match [a-zA-Z0-9][a-zA-Z0-9_.-]*: %w", ErrInvalidArg)
// Deprecated: use [ErrInvalidName] instead.
RegexError = ErrInvalidName
// NotHexRegex is a regular expression to check if a string is
// a hexadecimal string.
NotHexRegex = regexp.Delayed(`[^0-9a-fA-F]`)
// MaxInterfaceNameLength is the maximum length of a network interface name
MaxInterfaceNameLength = 15
)

View File

@ -24,7 +24,7 @@ profile {{.Name}} flags=(attach_disconnected,mediate_deleted) {
# Allow certain signals from OCI runtimes (podman, runc and crun)
signal (receive) peer={/usr/bin/,/usr/sbin/,}runc,
signal (receive) peer={/usr/bin/,/usr/sbin/,}crun*,
signal (receive) set=(int, quit, kill, term) peer={/usr/bin/,/usr/sbin/,}podman,
signal (receive) peer={/usr/bin/,/usr/sbin/,}podman,
{{end}}
deny @{PROC}/* w, # deny write for all files directly in /proc (not in a subdir)

View File

@ -11,10 +11,9 @@ import (
"strconv"
"strings"
"github.com/opencontainers/runc/libcontainer/cgroups"
"github.com/opencontainers/runc/libcontainer/cgroups/fs"
"github.com/opencontainers/runc/libcontainer/cgroups/fs2"
"github.com/opencontainers/runc/libcontainer/configs"
"github.com/opencontainers/cgroups"
"github.com/opencontainers/cgroups/fs"
"github.com/opencontainers/cgroups/fs2"
)
type linuxBlkioHandler struct {
@ -26,7 +25,7 @@ func getBlkioHandler() *linuxBlkioHandler {
}
// Apply set the specified constraints
func (c *linuxBlkioHandler) Apply(ctr *CgroupControl, res *configs.Resources) error {
func (c *linuxBlkioHandler) Apply(ctr *CgroupControl, res *cgroups.Resources) error {
if ctr.cgroup2 {
man, err := fs2.NewManager(ctr.config, filepath.Join(cgroupRoot, ctr.config.Path))
if err != nil {

View File

@ -8,9 +8,11 @@ import (
"context"
"errors"
"fmt"
"maps"
"math"
"os"
"path/filepath"
"slices"
"strconv"
"strings"
"sync"
@ -21,11 +23,9 @@ import (
"github.com/containers/storage/pkg/unshare"
systemdDbus "github.com/coreos/go-systemd/v22/dbus"
"github.com/godbus/dbus/v5"
"github.com/opencontainers/runc/libcontainer/cgroups"
"github.com/opencontainers/runc/libcontainer/cgroups/fs2"
"github.com/opencontainers/runc/libcontainer/configs"
"github.com/opencontainers/cgroups"
"github.com/opencontainers/cgroups/fs2"
"github.com/sirupsen/logrus"
"golang.org/x/exp/maps"
"golang.org/x/sys/unix"
)
@ -44,7 +44,7 @@ var (
// CgroupControl controls a cgroup hierarchy
type CgroupControl struct {
cgroup2 bool
config *configs.Cgroup
config *cgroups.Cgroup
systemd bool
// List of additional cgroup subsystems joined that
// do not have a custom handler.
@ -58,7 +58,7 @@ type controller struct {
type controllerHandler interface {
Create(*CgroupControl) (bool, error)
Apply(*CgroupControl, *configs.Resources) error
Apply(*CgroupControl, *cgroups.Resources) error
Destroy(*CgroupControl) error
Stat(*CgroupControl, *cgroups.Stats) error
}
@ -297,14 +297,14 @@ func readFileByKeyAsUint64(path, key string) (uint64, error) {
}
// New creates a new cgroup control
func New(path string, resources *configs.Resources) (*CgroupControl, error) {
func New(path string, resources *cgroups.Resources) (*CgroupControl, error) {
cgroup2, err := IsCgroup2UnifiedMode()
if err != nil {
return nil, err
}
control := &CgroupControl{
cgroup2: cgroup2,
config: &configs.Cgroup{
config: &cgroups.Cgroup{
Path: path,
Resources: resources,
},
@ -326,7 +326,7 @@ func New(path string, resources *configs.Resources) (*CgroupControl, error) {
}
// NewSystemd creates a new cgroup control
func NewSystemd(path string, resources *configs.Resources) (*CgroupControl, error) {
func NewSystemd(path string, resources *cgroups.Resources) (*CgroupControl, error) {
cgroup2, err := IsCgroup2UnifiedMode()
if err != nil {
return nil, err
@ -334,7 +334,7 @@ func NewSystemd(path string, resources *configs.Resources) (*CgroupControl, erro
control := &CgroupControl{
cgroup2: cgroup2,
systemd: true,
config: &configs.Cgroup{
config: &cgroups.Cgroup{
Path: path,
Resources: resources,
Rootless: unshare.IsRootless(),
@ -353,7 +353,7 @@ func Load(path string) (*CgroupControl, error) {
control := &CgroupControl{
cgroup2: cgroup2,
systemd: false,
config: &configs.Cgroup{
config: &cgroups.Cgroup{
Path: path,
},
}
@ -485,7 +485,7 @@ func (c *CgroupControl) DeleteByPath(path string) error {
}
// Update updates the cgroups
func (c *CgroupControl) Update(resources *configs.Resources) error {
func (c *CgroupControl) Update(resources *cgroups.Resources) error {
for _, h := range handlers {
if err := h.Apply(c, resources); err != nil {
return err
@ -503,7 +503,7 @@ func (c *CgroupControl) AddPid(pid int) error {
return fs2.CreateCgroupPath(path, c.config)
}
names := maps.Keys(handlers)
names := slices.Collect(maps.Keys(handlers))
for _, c := range c.additionalControllers {
if !c.symlink {
@ -533,7 +533,6 @@ func (c *CgroupControl) Stat() (*cgroups.Stats, error) {
if !errors.Is(err, os.ErrNotExist) {
return nil, err
}
logrus.Warningf("Failed to retrieve cgroup stats: %v", err)
continue
}
found = true
@ -843,11 +842,16 @@ func UserOwnsCurrentSystemdCgroup() (bool, error) {
if err != nil {
return false, err
}
s := st.Sys()
if s == nil {
return false, fmt.Errorf("stat cgroup path %s", cgroupPath)
return false, fmt.Errorf("stat cgroup path is nil %s", cgroupPath)
}
//nolint:errcheck // This cast should never fail, if it does we get a interface
// conversion panic and a stack trace on how we ended up here which is more
// valuable than returning a human friendly error test as we don't know how it
// happened.
if int(s.(*syscall.Stat_t).Uid) != uid {
return false, nil
}

View File

@ -8,10 +8,9 @@ import (
"path/filepath"
"strconv"
"github.com/opencontainers/runc/libcontainer/cgroups"
"github.com/opencontainers/runc/libcontainer/cgroups/fs"
"github.com/opencontainers/runc/libcontainer/cgroups/fs2"
"github.com/opencontainers/runc/libcontainer/configs"
"github.com/opencontainers/cgroups"
"github.com/opencontainers/cgroups/fs"
"github.com/opencontainers/cgroups/fs2"
)
type linuxCPUHandler struct {
@ -23,7 +22,7 @@ func getCPUHandler() *linuxCPUHandler {
}
// Apply set the specified constraints
func (c *linuxCPUHandler) Apply(ctr *CgroupControl, res *configs.Resources) error {
func (c *linuxCPUHandler) Apply(ctr *CgroupControl, res *cgroups.Resources) error {
if ctr.cgroup2 {
man, err := fs2.NewManager(ctr.config, filepath.Join(cgroupRoot, ctr.config.Path))
if err != nil {

View File

@ -5,10 +5,9 @@ package cgroups
import (
"path/filepath"
"github.com/opencontainers/runc/libcontainer/cgroups"
"github.com/opencontainers/runc/libcontainer/cgroups/fs"
"github.com/opencontainers/runc/libcontainer/cgroups/fs2"
"github.com/opencontainers/runc/libcontainer/configs"
"github.com/opencontainers/cgroups"
"github.com/opencontainers/cgroups/fs"
"github.com/opencontainers/cgroups/fs2"
)
type linuxCpusetHandler struct {
@ -20,7 +19,7 @@ func getCpusetHandler() *linuxCpusetHandler {
}
// Apply set the specified constraints
func (c *linuxCpusetHandler) Apply(ctr *CgroupControl, res *configs.Resources) error {
func (c *linuxCpusetHandler) Apply(ctr *CgroupControl, res *cgroups.Resources) error {
if ctr.cgroup2 {
man, err := fs2.NewManager(ctr.config, filepath.Join(cgroupRoot, ctr.config.Path))
if err != nil {

View File

@ -7,10 +7,9 @@ import (
"path/filepath"
"strconv"
"github.com/opencontainers/runc/libcontainer/cgroups"
"github.com/opencontainers/runc/libcontainer/cgroups/fs"
"github.com/opencontainers/runc/libcontainer/cgroups/fs2"
"github.com/opencontainers/runc/libcontainer/configs"
"github.com/opencontainers/cgroups"
"github.com/opencontainers/cgroups/fs"
"github.com/opencontainers/cgroups/fs2"
)
type linuxMemHandler struct {
@ -22,7 +21,7 @@ func getMemoryHandler() *linuxMemHandler {
}
// Apply set the specified constraints
func (c *linuxMemHandler) Apply(ctr *CgroupControl, res *configs.Resources) error {
func (c *linuxMemHandler) Apply(ctr *CgroupControl, res *cgroups.Resources) error {
if ctr.cgroup2 {
man, err := fs2.NewManager(ctr.config, filepath.Join(cgroupRoot, ctr.config.Path))
if err != nil {

View File

@ -5,10 +5,9 @@ package cgroups
import (
"path/filepath"
"github.com/opencontainers/runc/libcontainer/cgroups"
"github.com/opencontainers/runc/libcontainer/cgroups/fs"
"github.com/opencontainers/runc/libcontainer/cgroups/fs2"
"github.com/opencontainers/runc/libcontainer/configs"
"github.com/opencontainers/cgroups"
"github.com/opencontainers/cgroups/fs"
"github.com/opencontainers/cgroups/fs2"
)
type linuxPidHandler struct {
@ -20,7 +19,7 @@ func getPidsHandler() *linuxPidHandler {
}
// Apply set the specified constraints
func (c *linuxPidHandler) Apply(ctr *CgroupControl, res *configs.Resources) error {
func (c *linuxPidHandler) Apply(ctr *CgroupControl, res *cgroups.Resources) error {
if ctr.cgroup2 {
man, err := fs2.NewManager(ctr.config, filepath.Join(cgroupRoot, ctr.config.Path))
if err != nil {

View File

@ -14,7 +14,7 @@ import (
systemdDbus "github.com/coreos/go-systemd/v22/dbus"
"github.com/godbus/dbus/v5"
"github.com/opencontainers/runc/libcontainer/configs"
"github.com/opencontainers/cgroups"
)
type BlkioDev struct {
@ -22,7 +22,7 @@ type BlkioDev struct {
Bytes uint64
}
func systemdCreate(resources *configs.Resources, path string, c *systemdDbus.Conn) error {
func systemdCreate(resources *cgroups.Resources, path string, c *systemdDbus.Conn) error {
slice, name := filepath.Split(path)
slice = strings.TrimSuffix(slice, "/")
@ -150,7 +150,7 @@ func systemdDestroyConn(path string, c *systemdDbus.Conn) error {
return nil
}
func resourcesToProps(res *configs.Resources, v2 bool) (map[string]uint64, map[string]string, map[string][]byte, map[string]int64, map[string][]BlkioDev, error) {
func resourcesToProps(res *cgroups.Resources, v2 bool) (map[string]uint64, map[string]string, map[string][]byte, map[string]int64, map[string][]BlkioDev, error) {
bMap := make(map[string][]byte)
// this array is not used but will be once more resource limits are added
sMap := make(map[string]string)

View File

@ -14,8 +14,7 @@ import (
"sync"
"github.com/containers/storage/pkg/fileutils"
"github.com/opencontainers/runc/libcontainer/cgroups"
"github.com/opencontainers/runc/libcontainer/configs"
"github.com/opencontainers/cgroups"
"github.com/sirupsen/logrus"
"golang.org/x/sys/unix"
)
@ -122,7 +121,7 @@ func BlkioFiles(cgroupPath string) (wtFile, wtDevFile string) {
}
// SetBlkioThrottle sets the throttle limits for the cgroup
func SetBlkioThrottle(res *configs.Resources, cgroupPath string) error {
func SetBlkioThrottle(res *cgroups.Resources, cgroupPath string) error {
for _, td := range res.BlkioThrottleReadBpsDevice {
if err := WriteFile(cgroupPath, "blkio.throttle.read_bps_device", fmt.Sprintf("%d:%d %d", td.Major, td.Minor, td.Rate)); err != nil {
return err
@ -222,7 +221,7 @@ func MoveUnderCgroup(cgroup, subtree string, processes []uint32) error {
}
// root cgroup, skip it
if parts[2] == "/" && !(unifiedMode && parts[1] == "") {
if parts[2] == "/" && (!unifiedMode || parts[1] != "") {
continue
}
@ -262,7 +261,7 @@ func MoveUnderCgroup(cgroup, subtree string, processes []uint32) error {
if len(processes) > 0 {
for _, pid := range processes {
if _, err := f.WriteString(fmt.Sprintf("%d\n", pid)); err != nil {
if _, err := fmt.Fprintf(f, "%d\n", pid); err != nil {
logrus.Debugf("Cannot move process %d to cgroup %q: %v", pid, newCgroup, err)
}
}

View File

@ -12,7 +12,6 @@ import (
"github.com/containers/common/internal/attributedstring"
"github.com/containers/common/libnetwork/types"
"github.com/containers/common/pkg/capabilities"
"github.com/containers/storage/pkg/fileutils"
"github.com/containers/storage/pkg/homedir"
"github.com/containers/storage/pkg/unshare"
@ -979,24 +978,6 @@ func (c *Config) GetDefaultEnvEx(envHost, httpProxy bool) []string {
return append(env, c.Containers.Env.Get()...)
}
// Capabilities returns the capabilities parses the Add and Drop capability
// list from the default capabilities for the container
func (c *Config) Capabilities(user string, addCapabilities, dropCapabilities []string) ([]string, error) {
userNotRoot := func(user string) bool {
if user == "" || user == "root" || user == "0" {
return false
}
return true
}
defaultCapabilities := c.Containers.DefaultCapabilities.Get()
if userNotRoot(user) {
defaultCapabilities = []string{}
}
return capabilities.MergeCapabilities(defaultCapabilities, addCapabilities, dropCapabilities)
}
// Device parses device mapping string to a src, dest & permissions string
// Valid values for device looklike:
//

View File

@ -1,6 +1,7 @@
package config
import (
"github.com/containers/common/pkg/capabilities"
selinux "github.com/opencontainers/selinux/go-selinux"
)
@ -26,3 +27,21 @@ var defaultHelperBinariesDir = []string{
"/usr/libexec/podman",
"/usr/lib/podman",
}
// Capabilities returns the capabilities parses the Add and Drop capability
// list from the default capabilities for the container
func (c *Config) Capabilities(user string, addCapabilities, dropCapabilities []string) ([]string, error) {
userNotRoot := func(user string) bool {
if user == "" || user == "root" || user == "0" {
return false
}
return true
}
defaultCapabilities := c.Containers.DefaultCapabilities.Get()
if userNotRoot(user) {
defaultCapabilities = []string{}
}
return capabilities.MergeCapabilities(defaultCapabilities, addCapabilities, dropCapabilities)
}

View File

@ -6,7 +6,7 @@ import (
"fmt"
"os"
"path/filepath"
"regexp"
"strconv"
"strings"
"github.com/containers/storage/pkg/fileutils"
@ -97,8 +97,9 @@ func (c *ContainersConfig) validateTZ() error {
}
func (c *ContainersConfig) validateUmask() error {
validUmask := regexp.MustCompile(`^[0-7]{1,4}$`)
if !validUmask.MatchString(c.Umask) {
// Valid values are 0 to 7777 octal.
_, err := strconv.ParseUint(c.Umask, 8, 12)
if err != nil {
return fmt.Errorf("not a valid umask %s", c.Umask)
}
return nil

View File

@ -5,3 +5,9 @@ package config
func selinuxEnabled() bool {
return false
}
// Capabilities returns the capabilities parses the Add and Drop capability
// list from the default capabilities for the container
func (c *Config) Capabilities(user string, addCapabilities, dropCapabilities []string) ([]string, error) {
return nil, nil
}

View File

@ -236,13 +236,12 @@ default_sysctls = [
#
#mounts = []
# Default way to to create a Network namespace for the container
# Options are:
# `private` Create private Network Namespace for the container.
# `host` Share host Network Namespace with the container.
# `none` Containers do not use the network
# Default way to create a NET namespace for the container.
# The option is mapped to the **--network** argument for the podman commands, it accepts the same values as that option.
# For example it can be set to `bridge`, `host`, `none`, `pasta` and more, see the podman-create(1)
# manual for all available options.
#
#netns = "private"
#netns = ""
# Do not modify the `/etc/hosts` file in the container. Podman assumes control
# over the container's `/etc/hosts` file by default; refer to the `--add-host`
@ -381,14 +380,17 @@ default_sysctls = [
#firewall_driver = ""
# The network name of the default network to attach pods to.
# The name of the default network as seen in `podman network ls`. This option only effects the network assignment when
# the bridge network mode is selected, i.e. `--network bridge`. It is the default for rootful containers but not as
# rootless. To change the default network mode use the **netns** option under the `[containers]` table.
#
# Note: This should not be changed while you have any containers using this network.
#
#default_network = "podman"
# The default subnet for the default network given in default_network.
# If a network with that name does not exist, a new network using that name and
# this subnet will be created.
# Must be a valid IPv4 CIDR prefix.
#
# Note: This should not be changed if any containers are currently running on the default network.
#
#default_subnet = "10.88.0.0/16"
@ -897,7 +899,7 @@ default_sysctls = [
# Linux:
# qemu - Open source machine emulator and virtualizer. (Default)
# Windows: there are currently two options:
# wsl - Windows Subsystem for Linux (Default)
# wsl - Windows Subsystem for Linux (Default)
# hyperv - Windows Server Virtualization
# Mac: there are currently two options:
# applehv - Default Apple Hypervisor (Default)

View File

@ -182,13 +182,12 @@ default_sysctls = [
#
#log_tag = ""
# Default way to to create a Network namespace for the container
# Options are:
# `private` Create private Network Namespace for the container.
# `host` Share host Network Namespace with the container.
# `none` Containers do not use the network
# Default way to create a NET namespace for the container.
# The option is mapped to the **--network** argument for the podman commands, it accepts the same values as that option.
# For example it can be set to `bridge`, `host`, `none`, `pasta` and more, see the podman-create(1)
# manual for all available options.
#
#netns = "private"
#netns = ""
# Create /etc/hosts for the container. By default, container engine manage
# /etc/hosts, automatically adding the container's own IP address.
@ -292,14 +291,17 @@ default_sysctls = [
# "/usr/lib/netavark",
#]
# The network name of the default network to attach pods to.
# The name of the default network as seen in `podman network ls`. This option only effects the network assignment when
# the bridge network mode is selected, i.e. `--network bridge`. It is the default for rootful containers but not as
# rootless. To change the default network mode use the **netns** option under the `[containers]` table.
#
# Note: This should not be changed while you have any containers using this network.
#
#default_network = "podman"
# The default subnet for the default network given in default_network.
# If a network with that name does not exist, a new network using that name and
# this subnet will be created.
# Must be a valid IPv4 CIDR prefix.
#
# Note: This should not be changed if any containers are currently running on the default network.
#
#default_subnet = "10.88.0.0/16"

View File

@ -8,6 +8,7 @@ import (
"path/filepath"
"runtime"
"strings"
"sync"
"github.com/containers/common/internal/attributedstring"
nettypes "github.com/containers/common/libnetwork/types"
@ -36,8 +37,8 @@ const (
defaultInitName = "catatonit"
)
var (
DefaultMaskedPaths = []string{
func getMaskedPaths() ([]string, error) {
maskedPaths := []string{
"/proc/acpi",
"/proc/kcore",
"/proc/keys",
@ -49,8 +50,34 @@ var (
"/sys/devices/virtual/powercap",
"/sys/firmware",
"/sys/fs/selinux",
"/proc/interrupts",
}
maskedPathsToGlob := []string{
"/sys/devices/system/cpu/cpu*/thermal_throttle",
}
for _, p := range maskedPathsToGlob {
matches, err := filepath.Glob(p)
if err != nil {
return nil, err
}
maskedPaths = append(maskedPaths, matches...)
}
return maskedPaths, nil
}
var DefaultMaskedPaths = sync.OnceValue(func() []string {
maskedPaths, err := getMaskedPaths()
// this should never happen, the only error possible
// is ErrBadPattern and the patterns that were added must be valid
if err != nil {
panic(err)
}
return maskedPaths
})
var (
DefaultReadOnlyPaths = []string{
"/proc/asound",
"/proc/bus",
@ -539,7 +566,7 @@ func (c *Config) NetNS() string {
return c.Containers.NetNS
}
func (c EngineConfig) EventsLogMaxSize() uint64 {
func (c *EngineConfig) EventsLogMaxSize() uint64 {
return uint64(c.EventsLogFileMaxSize)
}

View File

@ -17,7 +17,7 @@ func DeviceFromPath(device string) ([]devices.Device, error) {
return nil, err
}
if unshare.IsRootless() && src != dst {
return nil, fmt.Errorf("Renaming device %s to %s is not supported in rootless containers", src, dst)
return nil, fmt.Errorf("renaming device %s to %s is not supported in rootless containers", src, dst)
}
srcInfo, err := os.Stat(src)
if err != nil {

View File

@ -5,10 +5,12 @@ import (
"io"
"math"
"net"
"net/http"
"net/url"
"syscall"
"time"
"github.com/containers/image/v5/docker"
"github.com/docker/distribution/registry/api/errcode"
errcodev2 "github.com/docker/distribution/registry/api/v2"
"github.com/hashicorp/go-multierror"
@ -47,7 +49,7 @@ func IfNecessary(ctx context.Context, operation func() error, options *Options)
logrus.Warnf("Failed, retrying in %s ... (%d/%d). Error: %v", delay, attempt+1, options.MaxRetry, err)
select {
case <-time.After(delay):
break
// Do nothing.
case <-ctx.Done():
return err
}
@ -81,6 +83,13 @@ func IsErrorRetryable(err error) bool {
return false
}
return true
case docker.UnexpectedHTTPStatusError:
// Retry on 502, 502 and 503 http server errors, they appear to be quite common in the field.
// https://github.com/containers/common/issues/2299
if e.StatusCode >= http.StatusBadGateway && e.StatusCode <= http.StatusGatewayTimeout {
return true
}
return false
case *net.OpError:
return IsErrorRetryable(e.Err)
case *url.Error: // This includes errors returned by the net/http client.

View File

@ -6,12 +6,14 @@ import (
"errors"
"fmt"
"io"
"maps"
"net"
"net/url"
"os"
"os/user"
"path/filepath"
"regexp"
"slices"
"strings"
"time"
@ -29,7 +31,6 @@ import (
"github.com/skeema/knownhosts"
"golang.org/x/crypto/ssh"
"golang.org/x/crypto/ssh/agent"
"golang.org/x/exp/maps"
)
func golangConnectionCreate(options ConnectionCreateOptions) error {
@ -287,7 +288,7 @@ func ValidateAndConfigure(uri *url.URL, iden string, insecureIsMachineConnection
dedup[fp] = s
}
uniq := maps.Values(dedup)
uniq := slices.Collect(maps.Values(dedup))
authMethods = append(authMethods, ssh.PublicKeysCallback(func() ([]ssh.Signer, error) {
return uniq, nil
}))

View File

@ -205,7 +205,7 @@ func (s *supplementedImageReference) NewImageSource(ctx context.Context, sys *ty
}
// Read the default manifest for the image.
manifestBytes, manifestType, err := src.GetManifest(ctx, nil)
manifestBytes, manifestType, err := image.UnparsedInstance(src, nil).Manifest(ctx)
if err != nil {
return nil, fmt.Errorf("reading default manifest from image %q: %w", transports.ImageName(ref), err)
}
@ -261,7 +261,7 @@ func (s *supplementedImageReference) NewImageSource(ctx context.Context, sys *ty
}
// Read the instance's manifest.
manifestBytes, manifestType, err := manifestToRead.src.GetManifest(ctx, manifestToRead.instance)
manifestBytes, manifestType, err := image.UnparsedInstance(manifestToRead.src, manifestToRead.instance).Manifest(ctx)
if err != nil {
// if errors.Is(err, storage.ErrImageUnknown) || errors.Is(err, os.ErrNotExist) {
// Trust that we either don't need it, or that it's in another reference.

View File

@ -31,8 +31,9 @@ func GetTimestamp(value string, reference time.Time) (string, error) {
}
var format string
// if the string has a Z or a + or three dashes use parse otherwise use parseinlocation
parseInLocation := !(strings.ContainsAny(value, "zZ+") || strings.Count(value, "-") == 3)
// If the string has a Z, or a +, or three dashes,
// then use time.Parse, otherwise use time.ParseInLocation.
parseInLocation := !strings.ContainsAny(value, "zZ+") && strings.Count(value, "-") != 3
switch {
case strings.Contains(value, "."):

View File

@ -15,7 +15,7 @@ import (
// StringInSlice determines if a string is in a string slice, returns bool.
//
// Deprecated: Use [golang.org/x/exp/slices.Contains] instead.
// Deprecated: Use [slices.Contains] instead.
func StringInSlice(s string, sl []string) bool {
return slices.Contains(sl, s)
}

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