aboutsummaryrefslogtreecommitdiff
path: root/pkg/domain
diff options
context:
space:
mode:
Diffstat (limited to 'pkg/domain')
-rw-r--r--pkg/domain/entities/engine_container.go1
-rw-r--r--pkg/domain/entities/engine_image.go2
-rw-r--r--pkg/domain/entities/events.go10
-rw-r--r--pkg/domain/entities/images.go2
-rw-r--r--pkg/domain/entities/manifest.go15
-rw-r--r--pkg/domain/entities/reports/scp.go5
-rw-r--r--pkg/domain/entities/volumes.go5
-rw-r--r--pkg/domain/filters/containers.go10
-rw-r--r--pkg/domain/filters/volumes.go8
-rw-r--r--pkg/domain/infra/abi/containers.go144
-rw-r--r--pkg/domain/infra/abi/containers_runlabel.go8
-rw-r--r--pkg/domain/infra/abi/images.go191
-rw-r--r--pkg/domain/infra/abi/manifest.go47
-rw-r--r--pkg/domain/infra/abi/network.go103
-rw-r--r--pkg/domain/infra/abi/parse/parse.go10
-rw-r--r--pkg/domain/infra/abi/play.go82
-rw-r--r--pkg/domain/infra/abi/pods.go144
-rw-r--r--pkg/domain/infra/abi/secrets.go9
-rw-r--r--pkg/domain/infra/abi/system.go40
-rw-r--r--pkg/domain/infra/abi/terminal/sigproxy_linux.go4
-rw-r--r--pkg/domain/infra/abi/terminal/terminal.go10
-rw-r--r--pkg/domain/infra/abi/terminal/terminal_linux.go11
-rw-r--r--pkg/domain/infra/abi/volumes.go14
-rw-r--r--pkg/domain/infra/runtime_libpod.go4
-rw-r--r--pkg/domain/infra/tunnel/containers.go26
-rw-r--r--pkg/domain/infra/tunnel/helpers.go4
-rw-r--r--pkg/domain/infra/tunnel/images.go28
-rw-r--r--pkg/domain/infra/tunnel/pods.go10
-rw-r--r--pkg/domain/infra/tunnel/volumes.go4
-rw-r--r--pkg/domain/utils/scp.go581
-rw-r--r--pkg/domain/utils/utils_test.go39
31 files changed, 1172 insertions, 399 deletions
diff --git a/pkg/domain/entities/engine_container.go b/pkg/domain/entities/engine_container.go
index df42876f6..e4eb808b4 100644
--- a/pkg/domain/entities/engine_container.go
+++ b/pkg/domain/entities/engine_container.go
@@ -104,4 +104,5 @@ type ContainerEngine interface {
VolumePrune(ctx context.Context, options VolumePruneOptions) ([]*reports.PruneReport, error)
VolumeRm(ctx context.Context, namesOrIds []string, opts VolumeRmOptions) ([]*VolumeRmReport, error)
VolumeUnmount(ctx context.Context, namesOrIds []string) ([]*VolumeUnmountReport, error)
+ VolumeReload(ctx context.Context) (*VolumeReloadReport, error)
}
diff --git a/pkg/domain/entities/engine_image.go b/pkg/domain/entities/engine_image.go
index 5011d82aa..5f76ae50b 100644
--- a/pkg/domain/entities/engine_image.go
+++ b/pkg/domain/entities/engine_image.go
@@ -22,12 +22,12 @@ type ImageEngine interface {
Push(ctx context.Context, source string, destination string, opts ImagePushOptions) error
Remove(ctx context.Context, images []string, opts ImageRemoveOptions) (*ImageRemoveReport, []error)
Save(ctx context.Context, nameOrID string, tags []string, options ImageSaveOptions) error
+ Scp(ctx context.Context, src, dst string, parentFlags []string, quiet bool) error
Search(ctx context.Context, term string, opts ImageSearchOptions) ([]ImageSearchReport, error)
SetTrust(ctx context.Context, args []string, options SetTrustOptions) error
ShowTrust(ctx context.Context, args []string, options ShowTrustOptions) (*ShowTrustReport, error)
Shutdown(ctx context.Context)
Tag(ctx context.Context, nameOrID string, tags []string, options ImageTagOptions) error
- Transfer(ctx context.Context, source ImageScpOptions, dest ImageScpOptions, parentFlags []string) error
Tree(ctx context.Context, nameOrID string, options ImageTreeOptions) (*ImageTreeReport, error)
Unmount(ctx context.Context, images []string, options ImageUnmountOptions) ([]*ImageUnmountReport, error)
Untag(ctx context.Context, nameOrID string, tags []string, options ImageUntagOptions) error
diff --git a/pkg/domain/entities/events.go b/pkg/domain/entities/events.go
index d8ba0f1d3..de218b285 100644
--- a/pkg/domain/entities/events.go
+++ b/pkg/domain/entities/events.go
@@ -14,6 +14,7 @@ type Event struct {
// TODO: it would be nice to have full control over the types at some
// point and fork such Docker types.
dockerEvents.Message
+ HealthStatus string
}
// ConvertToLibpodEvent converts an entities event to a libpod one.
@@ -44,6 +45,7 @@ func ConvertToLibpodEvent(e Event) *libpodEvents.Event {
Status: status,
Time: time.Unix(0, e.TimeNano),
Type: t,
+ HealthStatus: e.HealthStatus,
Details: libpodEvents.Details{
Attributes: details,
},
@@ -59,7 +61,7 @@ func ConvertToEntitiesEvent(e libpodEvents.Event) *Event {
attributes["image"] = e.Image
attributes["name"] = e.Name
attributes["containerExitCode"] = strconv.Itoa(e.ContainerExitCode)
- return &Event{dockerEvents.Message{
+ message := dockerEvents.Message{
// Compatibility with clients that still look for deprecated API elements
Status: e.Status.String(),
ID: e.ID,
@@ -73,5 +75,9 @@ func ConvertToEntitiesEvent(e libpodEvents.Event) *Event {
Scope: "local",
Time: e.Time.Unix(),
TimeNano: e.Time.UnixNano(),
- }}
+ }
+ return &Event{
+ message,
+ e.HealthStatus,
+ }
}
diff --git a/pkg/domain/entities/images.go b/pkg/domain/entities/images.go
index 11f6e8687..da317cfad 100644
--- a/pkg/domain/entities/images.go
+++ b/pkg/domain/entities/images.go
@@ -325,6 +325,8 @@ type ImageScpOptions struct {
Image string `json:"image,omitempty"`
// User is used in conjunction with Transfer to determine if a valid user was given to save from/load into
User string `json:"user,omitempty"`
+ // Tag is the name to be used for the image on the destination
+ Tag string `json:"tag,omitempty"`
}
// ImageScpConnections provides the ssh related information used in remote image transfer
diff --git a/pkg/domain/entities/manifest.go b/pkg/domain/entities/manifest.go
index 81f3e837b..e88c5f854 100644
--- a/pkg/domain/entities/manifest.go
+++ b/pkg/domain/entities/manifest.go
@@ -67,6 +67,21 @@ type ManifestModifyOptions struct {
type ManifestRemoveOptions struct {
}
+// ManifestRemoveReport provides the model for the removed manifest
+//
+// swagger:model
+type ManifestRemoveReport struct {
+ // Deleted manifest list.
+ Deleted []string `json:",omitempty"`
+ // Untagged images. Can be longer than Deleted.
+ Untagged []string `json:",omitempty"`
+ // Errors associated with operation
+ Errors []string `json:",omitempty"`
+ // ExitCode describes the exit codes as described in the `podman rmi`
+ // man page.
+ ExitCode int
+}
+
// ManifestModifyReport provides the model for removed digests and changed manifest
//
// swagger:model
diff --git a/pkg/domain/entities/reports/scp.go b/pkg/domain/entities/reports/scp.go
new file mode 100644
index 000000000..1e102bab3
--- /dev/null
+++ b/pkg/domain/entities/reports/scp.go
@@ -0,0 +1,5 @@
+package reports
+
+type ScpReport struct {
+ Id string `json:"Id"` //nolint:revive,stylecheck
+}
diff --git a/pkg/domain/entities/volumes.go b/pkg/domain/entities/volumes.go
index 556df16c1..9a06b2238 100644
--- a/pkg/domain/entities/volumes.go
+++ b/pkg/domain/entities/volumes.go
@@ -54,6 +54,11 @@ type VolumeListReport struct {
VolumeConfigResponse
}
+// VolumeReloadReport describes the response from reload volume plugins
+type VolumeReloadReport struct {
+ define.VolumeReload
+}
+
/*
* Docker API compatibility types
*/
diff --git a/pkg/domain/filters/containers.go b/pkg/domain/filters/containers.go
index e2ab8d70c..f88a165e7 100644
--- a/pkg/domain/filters/containers.go
+++ b/pkg/domain/filters/containers.go
@@ -1,6 +1,7 @@
package filters
import (
+ "errors"
"fmt"
"strconv"
"strings"
@@ -10,7 +11,6 @@ import (
"github.com/containers/podman/v4/libpod"
"github.com/containers/podman/v4/libpod/define"
"github.com/containers/podman/v4/pkg/util"
- "github.com/pkg/errors"
)
// GenerateContainerFilterFuncs return ContainerFilter functions based of filter.
@@ -36,7 +36,7 @@ func GenerateContainerFilterFuncs(filter string, filterValues []string, r *libpo
for _, exitCode := range filterValues {
ec, err := strconv.ParseInt(exitCode, 10, 32)
if err != nil {
- return nil, errors.Wrapf(err, "exited code out of range %q", ec)
+ return nil, fmt.Errorf("exited code out of range %q: %w", ec, err)
}
exitCodes = append(exitCodes, int32(ec))
}
@@ -184,7 +184,7 @@ func GenerateContainerFilterFuncs(filter string, filterValues []string, r *libpo
for _, podNameOrID := range filterValues {
p, err := r.LookupPod(podNameOrID)
if err != nil {
- if errors.Cause(err) == define.ErrNoSuchPod {
+ if errors.Is(err, define.ErrNoSuchPod) {
continue
}
return nil, err
@@ -291,7 +291,7 @@ func GenerateContainerFilterFuncs(filter string, filterValues []string, r *libpo
return false
}, filterValueError
}
- return nil, errors.Errorf("%s is an invalid filter", filter)
+ return nil, fmt.Errorf("%s is an invalid filter", filter)
}
// GeneratePruneContainerFilterFuncs return ContainerFilter functions based of filter for prune operation
@@ -304,7 +304,7 @@ func GeneratePruneContainerFilterFuncs(filter string, filterValues []string, r *
case "until":
return prepareUntilFilterFunc(filterValues)
}
- return nil, errors.Errorf("%s is an invalid filter", filter)
+ return nil, fmt.Errorf("%s is an invalid filter", filter)
}
func prepareUntilFilterFunc(filterValues []string) (func(container *libpod.Container) bool, error) {
diff --git a/pkg/domain/filters/volumes.go b/pkg/domain/filters/volumes.go
index e88bd4228..a18e6332c 100644
--- a/pkg/domain/filters/volumes.go
+++ b/pkg/domain/filters/volumes.go
@@ -2,6 +2,7 @@ package filters
import (
"net/url"
+ "regexp"
"strings"
"github.com/containers/podman/v4/libpod"
@@ -15,9 +16,12 @@ func GenerateVolumeFilters(filters url.Values) ([]libpod.VolumeFilter, error) {
for _, val := range v {
switch filter {
case "name":
- nameVal := val
+ nameRegexp, err := regexp.Compile(val)
+ if err != nil {
+ return nil, err
+ }
vf = append(vf, func(v *libpod.Volume) bool {
- return nameVal == v.Name()
+ return nameRegexp.MatchString(v.Name())
})
case "driver":
driverVal := val
diff --git a/pkg/domain/infra/abi/containers.go b/pkg/domain/infra/abi/containers.go
index c7cd0cb56..1688be57e 100644
--- a/pkg/domain/infra/abi/containers.go
+++ b/pkg/domain/infra/abi/containers.go
@@ -2,6 +2,7 @@ package abi
import (
"context"
+ "errors"
"fmt"
"io/ioutil"
"os"
@@ -16,7 +17,6 @@ import (
"github.com/containers/image/v5/manifest"
"github.com/containers/podman/v4/libpod"
"github.com/containers/podman/v4/libpod/define"
- "github.com/containers/podman/v4/libpod/events"
"github.com/containers/podman/v4/libpod/logs"
"github.com/containers/podman/v4/pkg/checkpoint"
"github.com/containers/podman/v4/pkg/domain/entities"
@@ -33,12 +33,11 @@ import (
"github.com/containers/podman/v4/pkg/specgenutil"
"github.com/containers/podman/v4/pkg/util"
"github.com/containers/storage"
- "github.com/pkg/errors"
"github.com/sirupsen/logrus"
)
// getContainersAndInputByContext gets containers whether all, latest, or a slice of names/ids
-// is specified. It also returns a list of the corresponding input name used to lookup each container.
+// is specified. It also returns a list of the corresponding input name used to look up each container.
func getContainersAndInputByContext(all, latest bool, names []string, runtime *libpod.Runtime) (ctrs []*libpod.Container, rawInput []string, err error) {
var ctr *libpod.Container
ctrs = []*libpod.Container{}
@@ -81,7 +80,7 @@ func getContainersByContext(all, latest bool, names []string, runtime *libpod.Ru
func (ic *ContainerEngine) ContainerExists(ctx context.Context, nameOrID string, options entities.ContainerExistsOptions) (*entities.BoolReport, error) {
_, err := ic.Libpod.LookupContainer(nameOrID)
if err != nil {
- if errors.Cause(err) != define.ErrNoSuchCtr {
+ if !errors.Is(err, define.ErrNoSuchCtr) {
return nil, err
}
if options.External {
@@ -121,7 +120,7 @@ func (ic *ContainerEngine) ContainerPause(ctx context.Context, namesOrIds []stri
report := make([]*entities.PauseUnpauseReport, 0, len(ctrs))
for _, c := range ctrs {
err := c.Pause()
- if err != nil && options.All && errors.Cause(err) == define.ErrCtrStateInvalid {
+ if err != nil && options.All && errors.Is(err, define.ErrCtrStateInvalid) {
logrus.Debugf("Container %s is not running", c.ID())
continue
}
@@ -138,7 +137,7 @@ func (ic *ContainerEngine) ContainerUnpause(ctx context.Context, namesOrIds []st
report := make([]*entities.PauseUnpauseReport, 0, len(ctrs))
for _, c := range ctrs {
err := c.Unpause()
- if err != nil && options.All && errors.Cause(err) == define.ErrCtrStateInvalid {
+ if err != nil && options.All && errors.Is(err, define.ErrCtrStateInvalid) {
logrus.Debugf("Container %s is not paused", c.ID())
continue
}
@@ -149,7 +148,7 @@ func (ic *ContainerEngine) ContainerUnpause(ctx context.Context, namesOrIds []st
func (ic *ContainerEngine) ContainerStop(ctx context.Context, namesOrIds []string, options entities.StopOptions) ([]*entities.StopReport, error) {
names := namesOrIds
ctrs, rawInputs, err := getContainersAndInputByContext(options.All, options.Latest, names, ic.Libpod)
- if err != nil && !(options.Ignore && errors.Cause(err) == define.ErrNoSuchCtr) {
+ if err != nil && !(options.Ignore && errors.Is(err, define.ErrNoSuchCtr)) {
return nil, err
}
ctrMap := map[string]string{}
@@ -167,13 +166,13 @@ func (ic *ContainerEngine) ContainerStop(ctx context.Context, namesOrIds []strin
}
if err != nil {
switch {
- case errors.Cause(err) == define.ErrCtrStopped:
+ case errors.Is(err, define.ErrCtrStopped):
logrus.Debugf("Container %s is already stopped", c.ID())
- case options.All && errors.Cause(err) == define.ErrCtrStateInvalid:
+ case options.All && errors.Is(err, define.ErrCtrStateInvalid):
logrus.Debugf("Container %s is not running, could not stop", c.ID())
// container never created in OCI runtime
// docker parity: do nothing just return container id
- case errors.Cause(err) == define.ErrCtrStateInvalid:
+ case errors.Is(err, define.ErrCtrStateInvalid):
logrus.Debugf("Container %s is either not created on runtime or is in a invalid state", c.ID())
default:
return err
@@ -183,7 +182,7 @@ func (ic *ContainerEngine) ContainerStop(ctx context.Context, namesOrIds []strin
if err != nil {
// Issue #7384 and #11384: If the container is configured for
// auto-removal, it might already have been removed at this point.
- // We still need to to cleanup since we do not know if the other cleanup process is successful
+ // We still need to clean up since we do not know if the other cleanup process is successful
if c.AutoRemove() && (errors.Is(err, define.ErrNoSuchCtr) || errors.Is(err, define.ErrCtrRemoved)) {
return nil
}
@@ -239,7 +238,7 @@ func (ic *ContainerEngine) ContainerKill(ctx context.Context, namesOrIds []strin
reports := make([]*entities.KillReport, 0, len(ctrs))
for _, con := range ctrs {
err := con.Kill(uint(sig))
- if options.All && errors.Cause(err) == define.ErrCtrStateInvalid {
+ if options.All && errors.Is(err, define.ErrCtrStateInvalid) {
logrus.Debugf("Container %s is not running", con.ID())
continue
}
@@ -290,8 +289,7 @@ func (ic *ContainerEngine) removeContainer(ctx context.Context, ctr *libpod.Cont
return nil
}
logrus.Debugf("Failed to remove container %s: %s", ctr.ID(), err.Error())
- switch errors.Cause(err) {
- case define.ErrNoSuchCtr:
+ if errors.Is(err, define.ErrNoSuchCtr) {
// Ignore if the container does not exist (anymore) when either
// it has been requested by the user of if the container is a
// service one. Service containers are removed along with its
@@ -302,7 +300,7 @@ func (ic *ContainerEngine) removeContainer(ctx context.Context, ctr *libpod.Cont
logrus.Debugf("Ignoring error (--allow-missing): %v", err)
return nil
}
- case define.ErrCtrRemoved:
+ } else if errors.Is(err, define.ErrCtrRemoved) {
return nil
}
return err
@@ -318,15 +316,15 @@ func (ic *ContainerEngine) ContainerRm(ctx context.Context, namesOrIds []string,
for _, ctr := range names {
report := reports.RmReport{Id: ctr}
report.Err = ic.Libpod.RemoveStorageContainer(ctr, options.Force)
- switch errors.Cause(report.Err) {
- case nil:
+ //nolint:gocritic
+ if report.Err == nil {
// remove container names that we successfully deleted
rmReports = append(rmReports, &report)
- case define.ErrNoSuchCtr, define.ErrCtrExists:
+ } else if errors.Is(report.Err, define.ErrNoSuchCtr) || errors.Is(report.Err, define.ErrCtrExists) {
// There is still a potential this is a libpod container
tmpNames = append(tmpNames, ctr)
- default:
- if _, err := ic.Libpod.LookupContainer(ctr); errors.Cause(err) == define.ErrNoSuchCtr {
+ } else {
+ if _, err := ic.Libpod.LookupContainer(ctr); errors.Is(err, define.ErrNoSuchCtr) {
// remove container failed, but not a libpod container
rmReports = append(rmReports, &report)
continue
@@ -338,7 +336,7 @@ func (ic *ContainerEngine) ContainerRm(ctx context.Context, namesOrIds []string,
names = tmpNames
ctrs, err := getContainersByContext(options.All, options.Latest, names, ic.Libpod)
- if err != nil && !(options.Ignore && errors.Cause(err) == define.ErrNoSuchCtr) {
+ if err != nil && !(options.Ignore && errors.Is(err, define.ErrNoSuchCtr)) {
// Failed to get containers. If force is specified, get the containers ID
// and evict them
if !options.Force {
@@ -350,7 +348,7 @@ func (ic *ContainerEngine) ContainerRm(ctx context.Context, namesOrIds []string,
report := reports.RmReport{Id: ctr}
_, err := ic.Libpod.EvictContainer(ctx, ctr, options.Volumes)
if err != nil {
- if options.Ignore && errors.Cause(err) == define.ErrNoSuchCtr {
+ if options.Ignore && errors.Is(err, define.ErrNoSuchCtr) {
logrus.Debugf("Ignoring error (--allow-missing): %v", err)
rmReports = append(rmReports, &report)
continue
@@ -427,7 +425,7 @@ func (ic *ContainerEngine) ContainerInspect(ctx context.Context, namesOrIds []st
ctr, err := ic.Libpod.GetLatestContainer()
if err != nil {
if errors.Is(err, define.ErrNoSuchCtr) {
- return nil, []error{errors.Wrapf(err, "no containers to inspect")}, nil
+ return nil, []error{fmt.Errorf("no containers to inspect: %w", err)}, nil
}
return nil, nil, err
}
@@ -453,7 +451,7 @@ func (ic *ContainerEngine) ContainerInspect(ctx context.Context, namesOrIds []st
// ErrNoSuchCtr is non-fatal, other errors will be
// treated as fatal.
if errors.Is(err, define.ErrNoSuchCtr) {
- errs = append(errs, errors.Errorf("no such container %s", name))
+ errs = append(errs, fmt.Errorf("no such container %s", name))
continue
}
return nil, nil, err
@@ -464,7 +462,7 @@ func (ic *ContainerEngine) ContainerInspect(ctx context.Context, namesOrIds []st
// ErrNoSuchCtr is non-fatal, other errors will be
// treated as fatal.
if errors.Is(err, define.ErrNoSuchCtr) {
- errs = append(errs, errors.Errorf("no such container %s", name))
+ errs = append(errs, fmt.Errorf("no such container %s", name))
continue
}
return nil, nil, err
@@ -488,7 +486,7 @@ func (ic *ContainerEngine) ContainerTop(ctx context.Context, options entities.To
container, err = ic.Libpod.LookupContainer(options.NameOrID)
}
if err != nil {
- return nil, errors.Wrap(err, "unable to lookup requested container")
+ return nil, fmt.Errorf("unable to look up requested container: %w", err)
}
// Run Top.
@@ -513,12 +511,12 @@ func (ic *ContainerEngine) ContainerCommit(ctx context.Context, nameOrID string,
case "oci":
mimeType = buildah.OCIv1ImageManifest
if len(options.Message) > 0 {
- return nil, errors.Errorf("messages are only compatible with the docker image format (-f docker)")
+ return nil, fmt.Errorf("messages are only compatible with the docker image format (-f docker)")
}
case "docker":
mimeType = manifest.DockerV2Schema2MediaType
default:
- return nil, errors.Errorf("unrecognized image format %q", options.Format)
+ return nil, fmt.Errorf("unrecognized image format %q", options.Format)
}
sc := ic.Libpod.SystemContext()
@@ -635,13 +633,13 @@ func (ic *ContainerEngine) ContainerRestore(ctx context.Context, namesOrIds []st
containers, err = getContainersByContext(false, options.Latest, namesOrIds, ic.Libpod)
default:
for _, nameOrID := range namesOrIds {
- logrus.Debugf("lookup container: %q", nameOrID)
+ logrus.Debugf("look up container: %q", nameOrID)
ctr, err := ic.Libpod.LookupContainer(nameOrID)
if err == nil {
containers = append(containers, ctr)
} else {
// If container was not found, check if this is a checkpoint image
- logrus.Debugf("lookup image: %q", nameOrID)
+ logrus.Debugf("look up image: %q", nameOrID)
img, _, err := ic.Libpod.LibimageRuntime().LookupImage(nameOrID, nil)
if err != nil {
return nil, fmt.Errorf("no such container or image: %s", nameOrID)
@@ -661,7 +659,7 @@ func (ic *ContainerEngine) ContainerRestore(ctx context.Context, namesOrIds []st
// CRImportCheckpoint is expected to import exactly one container from checkpoint image
checkpointImageImportErrors = append(
checkpointImageImportErrors,
- errors.Errorf("unable to import checkpoint from image: %q: %v", nameOrID, err),
+ fmt.Errorf("unable to import checkpoint from image: %q: %v", nameOrID, err),
)
} else {
containers = append(containers, importedContainers[0])
@@ -721,16 +719,16 @@ func (ic *ContainerEngine) ContainerAttach(ctx context.Context, nameOrID string,
ctr := ctrs[0]
conState, err := ctr.State()
if err != nil {
- return errors.Wrapf(err, "unable to determine state of %s", ctr.ID())
+ return fmt.Errorf("unable to determine state of %s: %w", ctr.ID(), err)
}
if conState != define.ContainerStateRunning {
- return errors.Errorf("you can only attach to running containers")
+ return fmt.Errorf("you can only attach to running containers")
}
// If the container is in a pod, also set to recursively start dependencies
err = terminal.StartAttachCtr(ctx, ctr, options.Stdout, options.Stderr, options.Stdin, options.DetachKeys, options.SigProxy, false)
- if err != nil && errors.Cause(err) != define.ErrDetach {
- return errors.Wrapf(err, "error attaching to container %s", ctr.ID())
+ if err != nil && !errors.Is(err, define.ErrDetach) {
+ return fmt.Errorf("error attaching to container %s: %w", ctr.ID(), err)
}
os.Stdout.WriteString("\n")
return nil
@@ -752,12 +750,12 @@ func makeExecConfig(options entities.ExecOptions, rt *libpod.Runtime) (*libpod.E
storageConfig := rt.StorageConfig()
runtimeConfig, err := rt.GetConfig()
if err != nil {
- return nil, errors.Wrapf(err, "error retrieving Libpod configuration to build exec exit command")
+ return nil, fmt.Errorf("error retrieving Libpod configuration to build exec exit command: %w", err)
}
// TODO: Add some ability to toggle syslog
exitCommandArgs, err := specgenutil.CreateExitCommandArgs(storageConfig, runtimeConfig, logrus.IsLevelEnabled(logrus.DebugLevel), false, true)
if err != nil {
- return nil, errors.Wrapf(err, "error constructing exit command for exec session")
+ return nil, fmt.Errorf("error constructing exit command for exec session: %w", err)
}
execConfig.ExitCommand = exitCommandArgs
@@ -775,7 +773,7 @@ func checkExecPreserveFDs(options entities.ExecOptions) error {
for _, e := range entries {
i, err := strconv.Atoi(e.Name())
if err != nil {
- return errors.Wrapf(err, "cannot parse %s in /proc/self/fd", e.Name())
+ return fmt.Errorf("cannot parse %s in /proc/self/fd: %w", e.Name(), err)
}
m[i] = true
}
@@ -892,7 +890,7 @@ func (ic *ContainerEngine) ContainerStart(ctx context.Context, namesOrIds []stri
if options.Attach {
err = terminal.StartAttachCtr(ctx, ctr, options.Stdout, options.Stderr, options.Stdin, options.DetachKeys, options.SigProxy, !ctrRunning)
- if errors.Cause(err) == define.ErrDetach {
+ if errors.Is(err, define.ErrDetach) {
// User manually detached
// Exit cleanly immediately
reports = append(reports, &entities.ContainerStartReport{
@@ -904,7 +902,7 @@ func (ic *ContainerEngine) ContainerStart(ctx context.Context, namesOrIds []stri
return reports, nil
}
- if errors.Cause(err) == define.ErrWillDeadlock {
+ if errors.Is(err, define.ErrWillDeadlock) {
logrus.Debugf("Deadlock error: %v", err)
reports = append(reports, &entities.ContainerStartReport{
Id: ctr.ID(),
@@ -912,7 +910,7 @@ func (ic *ContainerEngine) ContainerStart(ctx context.Context, namesOrIds []stri
Err: err,
ExitCode: define.ExitCode(err),
})
- return reports, errors.Errorf("attempting to start container %s would cause a deadlock; please run 'podman system renumber' to resolve", ctr.ID())
+ return reports, fmt.Errorf("attempting to start container %s would cause a deadlock; please run 'podman system renumber' to resolve", ctr.ID())
}
if ctrRunning {
@@ -937,8 +935,9 @@ func (ic *ContainerEngine) ContainerStart(ctx context.Context, namesOrIds []stri
logrus.Errorf("Removing container %s: %v", ctr.ID(), err)
}
}
- return reports, errors.Wrapf(err, "unable to start container %s", ctr.ID())
+ return reports, fmt.Errorf("unable to start container %s: %w", ctr.ID(), err)
}
+
exitCode = ic.GetContainerExitCode(ctx, ctr)
reports = append(reports, &entities.ContainerStartReport{
Id: ctr.ID(),
@@ -960,12 +959,12 @@ func (ic *ContainerEngine) ContainerStart(ctx context.Context, namesOrIds []stri
}
if err := ctr.Start(ctx, true); err != nil {
report.Err = err
- if errors.Cause(err) == define.ErrWillDeadlock {
- report.Err = errors.Wrapf(err, "please run 'podman system renumber' to resolve deadlocks")
+ if errors.Is(err, define.ErrWillDeadlock) {
+ report.Err = fmt.Errorf("please run 'podman system renumber' to resolve deadlocks: %w", err)
reports = append(reports, report)
continue
}
- report.Err = errors.Wrapf(err, "unable to start container %q", ctr.ID())
+ report.Err = fmt.Errorf("unable to start container %q: %w", ctr.ID(), err)
reports = append(reports, report)
if ctr.AutoRemove() {
if err := ic.removeContainer(ctx, ctr, entities.RmOptions{}); err != nil {
@@ -1001,7 +1000,7 @@ func (ic *ContainerEngine) Diff(ctx context.Context, namesOrIDs []string, opts e
if opts.Latest {
ctnr, err := ic.Libpod.GetLatestContainer()
if err != nil {
- return nil, errors.Wrap(err, "unable to get latest container")
+ return nil, fmt.Errorf("unable to get latest container: %w", err)
}
base = ctnr.ID()
}
@@ -1064,7 +1063,7 @@ func (ic *ContainerEngine) ContainerRun(ctx context.Context, opts entities.Conta
// We've manually detached from the container
// Do not perform cleanup, or wait for container exit code
// Just exit immediately
- if errors.Cause(err) == define.ErrDetach {
+ if errors.Is(err, define.ErrDetach) {
report.ExitCode = 0
return &report, nil
}
@@ -1074,10 +1073,10 @@ func (ic *ContainerEngine) ContainerRun(ctx context.Context, opts entities.Conta
logrus.Debugf("unable to remove container %s after failing to start and attach to it", ctr.ID())
}
}
- if errors.Cause(err) == define.ErrWillDeadlock {
+ if errors.Is(err, define.ErrWillDeadlock) {
logrus.Debugf("Deadlock error on %q: %v", ctr.ID(), err)
report.ExitCode = define.ExitCode(err)
- return &report, errors.Errorf("attempting to start container %s would cause a deadlock; please run 'podman system renumber' to resolve", ctr.ID())
+ return &report, fmt.Errorf("attempting to start container %s would cause a deadlock; please run 'podman system renumber' to resolve", ctr.ID())
}
report.ExitCode = define.ExitCode(err)
return &report, err
@@ -1086,8 +1085,8 @@ func (ic *ContainerEngine) ContainerRun(ctx context.Context, opts entities.Conta
if opts.Rm && !ctr.ShouldRestart(ctx) {
var timeout *uint
if err := ic.Libpod.RemoveContainer(ctx, ctr, false, true, timeout); err != nil {
- if errors.Cause(err) == define.ErrNoSuchCtr ||
- errors.Cause(err) == define.ErrCtrRemoved {
+ if errors.Is(err, define.ErrNoSuchCtr) ||
+ errors.Is(err, define.ErrCtrRemoved) {
logrus.Infof("Container %s was already removed, skipping --rm", ctr.ID())
} else {
logrus.Errorf("Removing container %s: %v", ctr.ID(), err)
@@ -1099,25 +1098,11 @@ func (ic *ContainerEngine) ContainerRun(ctx context.Context, opts entities.Conta
func (ic *ContainerEngine) GetContainerExitCode(ctx context.Context, ctr *libpod.Container) int {
exitCode, err := ctr.Wait(ctx)
- if err == nil {
- return int(exitCode)
- }
- if errors.Cause(err) != define.ErrNoSuchCtr {
- logrus.Errorf("Could not retrieve exit code: %v", err)
+ if err != nil {
+ logrus.Errorf("Waiting for container %s: %v", ctr.ID(), err)
return define.ExecErrorCodeNotFound
}
- // Make 4 attempt with 0.25s backoff between each for 1 second total
- var event *events.Event
- for i := 0; i < 4; i++ {
- event, err = ic.Libpod.GetLastContainerEvent(ctx, ctr.ID(), events.Exited)
- if err != nil {
- time.Sleep(250 * time.Millisecond)
- continue
- }
- return event.ContainerExitCode
- }
- logrus.Errorf("Could not retrieve exit code from event: %v", err)
- return define.ExecErrorCodeNotFound
+ return int(exitCode)
}
func (ic *ContainerEngine) ContainerLogs(ctx context.Context, containers []string, options entities.ContainerLogsOptions) error {
@@ -1194,12 +1179,12 @@ func (ic *ContainerEngine) ContainerCleanup(ctx context.Context, namesOrIds []st
var timeout *uint
err = ic.Libpod.RemoveContainer(ctx, ctr, false, true, timeout)
if err != nil {
- report.RmErr = errors.Wrapf(err, "failed to cleanup and remove container %v", ctr.ID())
+ report.RmErr = fmt.Errorf("failed to clean up and remove container %v: %w", ctr.ID(), err)
}
} else {
err := ctr.Cleanup(ctx)
if err != nil {
- report.CleanErr = errors.Wrapf(err, "failed to cleanup container %v", ctr.ID())
+ report.CleanErr = fmt.Errorf("failed to clean up container %v: %w", ctr.ID(), err)
}
}
@@ -1226,7 +1211,7 @@ func (ic *ContainerEngine) ContainerInit(ctx context.Context, namesOrIds []strin
err := ctr.Init(ctx, ctr.PodID() != "")
// If we're initializing all containers, ignore invalid state errors
- if options.All && errors.Cause(err) == define.ErrCtrStateInvalid {
+ if options.All && errors.Is(err, define.ErrCtrStateInvalid) {
err = nil
}
report.Err = err
@@ -1337,7 +1322,7 @@ func (ic *ContainerEngine) ContainerUnmount(ctx context.Context, nameOrIDs []str
if mounted {
report := entities.ContainerUnmountReport{Id: sctr.ID}
if _, report.Err = ic.Libpod.UnmountStorageContainer(sctr.ID, options.Force); report.Err != nil {
- if errors.Cause(report.Err) != define.ErrCtrExists {
+ if !errors.Is(report.Err, define.ErrCtrExists) {
reports = append(reports, &report)
}
} else {
@@ -1371,11 +1356,11 @@ func (ic *ContainerEngine) ContainerUnmount(ctx context.Context, nameOrIDs []str
report := entities.ContainerUnmountReport{Id: ctr.ID()}
if err := ctr.Unmount(options.Force); err != nil {
- if options.All && errors.Cause(err) == storage.ErrLayerNotMounted {
+ if options.All && errors.Is(err, storage.ErrLayerNotMounted) {
logrus.Debugf("Error umounting container %s, storage.ErrLayerNotMounted", ctr.ID())
continue
}
- report.Err = errors.Wrapf(err, "error unmounting container %s", ctr.ID())
+ report.Err = fmt.Errorf("error unmounting container %s: %w", ctr.ID(), err)
}
reports = append(reports, &report)
}
@@ -1424,7 +1409,7 @@ func (ic *ContainerEngine) Shutdown(_ context.Context) {
func (ic *ContainerEngine) ContainerStats(ctx context.Context, namesOrIds []string, options entities.ContainerStatsOptions) (statsChan chan entities.ContainerStatsReport, err error) {
if options.Interval < 1 {
- return nil, errors.New("Invalid interval, must be a positive number greater zero")
+ return nil, errors.New("invalid interval, must be a positive number greater zero")
}
if rootless.IsRootless() {
unified, err := cgroups.IsCgroup2UnifiedMode()
@@ -1479,19 +1464,18 @@ func (ic *ContainerEngine) ContainerStats(ctx context.Context, namesOrIds []stri
computeStats := func() ([]define.ContainerStats, error) {
containers, err = containerFunc()
if err != nil {
- return nil, errors.Wrapf(err, "unable to get list of containers")
+ return nil, fmt.Errorf("unable to get list of containers: %w", err)
}
reportStats := []define.ContainerStats{}
for _, ctr := range containers {
stats, err := ctr.GetContainerStats(containerStats[ctr.ID()])
if err != nil {
- cause := errors.Cause(err)
- if queryAll && (cause == define.ErrCtrRemoved || cause == define.ErrNoSuchCtr || cause == define.ErrCtrStateInvalid) {
+ if queryAll && (errors.Is(err, define.ErrCtrRemoved) || errors.Is(err, define.ErrNoSuchCtr) || errors.Is(err, define.ErrCtrStateInvalid)) {
continue
}
- if cause == cgroups.ErrCgroupV1Rootless {
- err = cause
+ if errors.Is(err, cgroups.ErrCgroupV1Rootless) {
+ err = cgroups.ErrCgroupV1Rootless
}
return nil, err
}
diff --git a/pkg/domain/infra/abi/containers_runlabel.go b/pkg/domain/infra/abi/containers_runlabel.go
index fac15f72d..463988c87 100644
--- a/pkg/domain/infra/abi/containers_runlabel.go
+++ b/pkg/domain/infra/abi/containers_runlabel.go
@@ -2,6 +2,7 @@ package abi
import (
"context"
+ "errors"
"fmt"
"os"
"path/filepath"
@@ -14,7 +15,6 @@ import (
envLib "github.com/containers/podman/v4/pkg/env"
"github.com/containers/podman/v4/utils"
"github.com/google/shlex"
- "github.com/pkg/errors"
"github.com/sirupsen/logrus"
)
@@ -40,7 +40,7 @@ func (ic *ContainerEngine) ContainerRunlabel(ctx context.Context, label string,
}
if len(pulledImages) != 1 {
- return errors.Errorf("internal error: expected an image to be pulled (or an error)")
+ return errors.New("internal error: expected an image to be pulled (or an error)")
}
// Extract the runlabel from the image.
@@ -57,7 +57,7 @@ func (ic *ContainerEngine) ContainerRunlabel(ctx context.Context, label string,
}
}
if runlabel == "" {
- return errors.Errorf("cannot find the value of label: %s in image: %s", label, imageRef)
+ return fmt.Errorf("cannot find the value of label: %s in image: %s", label, imageRef)
}
cmd, env, err := generateRunlabelCommand(runlabel, pulledImages[0], imageRef, args, options)
@@ -86,7 +86,7 @@ func (ic *ContainerEngine) ContainerRunlabel(ctx context.Context, label string,
name := cmd[i+1]
ctr, err := ic.Libpod.LookupContainer(name)
if err != nil {
- if errors.Cause(err) != define.ErrNoSuchCtr {
+ if !errors.Is(err, define.ErrNoSuchCtr) {
logrus.Debugf("Error occurred searching for container %s: %v", name, err)
return err
}
diff --git a/pkg/domain/infra/abi/images.go b/pkg/domain/infra/abi/images.go
index d63de2424..38008c7b9 100644
--- a/pkg/domain/infra/abi/images.go
+++ b/pkg/domain/infra/abi/images.go
@@ -2,7 +2,9 @@ package abi
import (
"context"
+ "errors"
"fmt"
+ "io/fs"
"io/ioutil"
"net/url"
"os"
@@ -29,12 +31,10 @@ import (
domainUtils "github.com/containers/podman/v4/pkg/domain/utils"
"github.com/containers/podman/v4/pkg/errorhandling"
"github.com/containers/podman/v4/pkg/rootless"
- "github.com/containers/podman/v4/utils"
"github.com/containers/storage"
dockerRef "github.com/docker/distribution/reference"
"github.com/opencontainers/go-digest"
imgspecv1 "github.com/opencontainers/image-spec/specs-go/v1"
- "github.com/pkg/errors"
"github.com/sirupsen/logrus"
)
@@ -128,14 +128,14 @@ func (ir *ImageEngine) History(ctx context.Context, nameOrID string, opts entiti
func (ir *ImageEngine) Mount(ctx context.Context, nameOrIDs []string, opts entities.ImageMountOptions) ([]*entities.ImageMountReport, error) {
if opts.All && len(nameOrIDs) > 0 {
- return nil, errors.Errorf("cannot mix --all with images")
+ return nil, errors.New("cannot mix --all with images")
}
if os.Geteuid() != 0 {
if driver := ir.Libpod.StorageConfig().GraphDriverName; driver != "vfs" {
// Do not allow to mount a graphdriver that is not vfs if we are creating the userns as part
// of the mount command.
- return nil, errors.Errorf("cannot mount using driver %s in rootless mode", driver)
+ return nil, fmt.Errorf("cannot mount using driver %s in rootless mode", driver)
}
became, ret, err := rootless.BecomeRootInUserNS("")
@@ -194,7 +194,7 @@ func (ir *ImageEngine) Mount(ctx context.Context, nameOrIDs []string, opts entit
func (ir *ImageEngine) Unmount(ctx context.Context, nameOrIDs []string, options entities.ImageUnmountOptions) ([]*entities.ImageUnmountReport, error) {
if options.All && len(nameOrIDs) > 0 {
- return nil, errors.Errorf("cannot mix --all with images")
+ return nil, errors.New("cannot mix --all with images")
}
listImagesOptions := &libimage.ListImagesOptions{}
@@ -292,7 +292,7 @@ func (ir *ImageEngine) Push(ctx context.Context, source string, destination stri
case "v2s2", "docker":
manifestType = manifest.DockerV2Schema2MediaType
default:
- return errors.Errorf("unknown format %q. Choose on of the supported formats: 'oci', 'v2s1', or 'v2s2'", options.Format)
+ return fmt.Errorf("unknown format %q. Choose on of the supported formats: 'oci', 'v2s1', or 'v2s2'", options.Format)
}
pushOptions := &libimage.PushOptions{}
@@ -350,22 +350,6 @@ func (ir *ImageEngine) Push(ctx context.Context, source string, destination stri
}
return pushError
}
-
-// Transfer moves images between root and rootless storage so the user specified in the scp call can access and use the image modified by root
-func (ir *ImageEngine) Transfer(ctx context.Context, source entities.ImageScpOptions, dest entities.ImageScpOptions, parentFlags []string) error {
- if source.User == "" {
- return errors.Wrapf(define.ErrInvalidArg, "you must define a user when transferring from root to rootless storage")
- }
- podman, err := os.Executable()
- if err != nil {
- return err
- }
- if rootless.IsRootless() && (len(dest.User) == 0 || dest.User == "root") { // if we are rootless and do not have a destination user we can just use sudo
- return transferRootless(source, dest, podman, parentFlags)
- }
- return transferRootful(source, dest, podman, parentFlags)
-}
-
func (ir *ImageEngine) Tag(ctx context.Context, nameOrID string, tags []string, options entities.ImageTagOptions) error {
// Allow tagging manifest list instead of resolving instances from manifest
lookupOptions := &libimage.LookupImageOptions{ManifestList: true}
@@ -539,12 +523,12 @@ func removeErrorsToExitCode(rmErrors []error) int {
}
for _, e := range rmErrors {
- switch errors.Cause(e) {
- case storage.ErrImageUnknown, storage.ErrLayerUnknown:
+ //nolint:gocritic
+ if errors.Is(e, storage.ErrImageUnknown) || errors.Is(e, storage.ErrLayerUnknown) {
noSuchImageErrors = true
- case storage.ErrImageUsedByContainer:
+ } else if errors.Is(e, storage.ErrImageUsedByContainer) {
inUseErrors = true
- default:
+ } else {
otherErrors = true
}
}
@@ -606,11 +590,11 @@ func (ir *ImageEngine) Shutdown(_ context.Context) {
func (ir *ImageEngine) Sign(ctx context.Context, names []string, options entities.SignOptions) (*entities.SignReport, error) {
mech, err := signature.NewGPGSigningMechanism()
if err != nil {
- return nil, errors.Wrap(err, "error initializing GPG")
+ return nil, fmt.Errorf("error initializing GPG: %w", err)
}
defer mech.Close()
if err := mech.SupportsSigning(); err != nil {
- return nil, errors.Wrap(err, "signing is not supported")
+ return nil, fmt.Errorf("signing is not supported: %w", err)
}
sc := ir.Libpod.SystemContext()
sc.DockerCertPath = options.CertDir
@@ -620,11 +604,11 @@ func (ir *ImageEngine) Sign(ctx context.Context, names []string, options entitie
err = func() error {
srcRef, err := alltransports.ParseImageName(signimage)
if err != nil {
- return errors.Wrapf(err, "error parsing image name")
+ return fmt.Errorf("error parsing image name: %w", err)
}
rawSource, err := srcRef.NewImageSource(ctx, sc)
if err != nil {
- return errors.Wrapf(err, "error getting image source")
+ return fmt.Errorf("error getting image source: %w", err)
}
defer func() {
if err = rawSource.Close(); err != nil {
@@ -633,17 +617,17 @@ func (ir *ImageEngine) Sign(ctx context.Context, names []string, options entitie
}()
topManifestBlob, manifestType, err := rawSource.GetManifest(ctx, nil)
if err != nil {
- return errors.Wrapf(err, "error getting manifest blob")
+ return fmt.Errorf("error getting manifest blob: %w", err)
}
dockerReference := rawSource.Reference().DockerReference()
if dockerReference == nil {
- return errors.Errorf("cannot determine canonical Docker reference for destination %s", transports.ImageName(rawSource.Reference()))
+ return fmt.Errorf("cannot determine canonical Docker reference for destination %s", transports.ImageName(rawSource.Reference()))
}
var sigStoreDir string
if options.Directory != "" {
repo := reference.Path(dockerReference)
if path.Clean(repo) != repo { // Coverage: This should not be reachable because /./ and /../ components are not valid in docker references
- return errors.Errorf("Unexpected path elements in Docker reference %s for signature storage", dockerReference.String())
+ return fmt.Errorf("unexpected path elements in Docker reference %s for signature storage", dockerReference.String())
}
sigStoreDir = filepath.Join(options.Directory, repo)
} else {
@@ -663,11 +647,11 @@ func (ir *ImageEngine) Sign(ctx context.Context, names []string, options entitie
if options.All {
if !manifest.MIMETypeIsMultiImage(manifestType) {
- return errors.Errorf("%s is not a multi-architecture image (manifest type %s)", signimage, manifestType)
+ return fmt.Errorf("%s is not a multi-architecture image (manifest type %s)", signimage, manifestType)
}
list, err := manifest.ListFromBlob(topManifestBlob, manifestType)
if err != nil {
- return errors.Wrapf(err, "Error parsing manifest list %q", string(topManifestBlob))
+ return fmt.Errorf("error parsing manifest list %q: %w", string(topManifestBlob), err)
}
instanceDigests := list.Instances()
for _, instanceDigest := range instanceDigests {
@@ -677,13 +661,13 @@ func (ir *ImageEngine) Sign(ctx context.Context, names []string, options entitie
return err
}
if err = putSignature(man, mech, sigStoreDir, instanceDigest, dockerReference, options); err != nil {
- return errors.Wrapf(err, "error storing signature for %s, %v", dockerReference.String(), instanceDigest)
+ return fmt.Errorf("error storing signature for %s, %v: %w", dockerReference.String(), instanceDigest, err)
}
}
return nil
}
if err = putSignature(topManifestBlob, mech, sigStoreDir, manifestDigest, dockerReference, options); err != nil {
- return errors.Wrapf(err, "error storing signature for %s, %v", dockerReference.String(), manifestDigest)
+ return fmt.Errorf("error storing signature for %s, %v: %w", dockerReference.String(), manifestDigest, err)
}
return nil
}()
@@ -694,53 +678,32 @@ func (ir *ImageEngine) Sign(ctx context.Context, names []string, options entitie
return nil, nil
}
-func getSigFilename(sigStoreDirPath string) (string, error) {
- sigFileSuffix := 1
- sigFiles, err := ioutil.ReadDir(sigStoreDirPath)
+func (ir *ImageEngine) Scp(ctx context.Context, src, dst string, parentFlags []string, quiet bool) error {
+ rep, source, dest, flags, err := domainUtils.ExecuteTransfer(src, dst, parentFlags, quiet)
if err != nil {
- return "", err
- }
- sigFilenames := make(map[string]bool)
- for _, file := range sigFiles {
- sigFilenames[file.Name()] = true
+ return err
}
- for {
- sigFilename := "signature-" + strconv.Itoa(sigFileSuffix)
- if _, exists := sigFilenames[sigFilename]; !exists {
- return sigFilename, nil
+ if (rep == nil && err == nil) && (source != nil && dest != nil) { // we need to execute the transfer
+ err := Transfer(ctx, *source, *dest, flags)
+ if err != nil {
+ return err
}
- sigFileSuffix++
}
+ return nil
}
-func localPathFromURI(url *url.URL) (string, error) {
- if url.Scheme != "file" {
- return "", errors.Errorf("writing to %s is not supported. Use a supported scheme", url.String())
- }
- return url.Path, nil
-}
-
-// putSignature creates signature and saves it to the signstore file
-func putSignature(manifestBlob []byte, mech signature.SigningMechanism, sigStoreDir string, instanceDigest digest.Digest, dockerReference dockerRef.Reference, options entities.SignOptions) error {
- newSig, err := signature.SignDockerManifest(manifestBlob, dockerReference.String(), mech, options.SignBy)
- if err != nil {
- return err
- }
- signatureDir := fmt.Sprintf("%s@%s=%s", sigStoreDir, instanceDigest.Algorithm(), instanceDigest.Hex())
- if err := os.MkdirAll(signatureDir, 0751); err != nil {
- // The directory is allowed to exist
- if !os.IsExist(err) {
- return err
- }
+func Transfer(ctx context.Context, source entities.ImageScpOptions, dest entities.ImageScpOptions, parentFlags []string) error {
+ if source.User == "" {
+ return fmt.Errorf("you must define a user when transferring from root to rootless storage: %w", define.ErrInvalidArg)
}
- sigFilename, err := getSigFilename(signatureDir)
+ podman, err := os.Executable()
if err != nil {
return err
}
- if err = ioutil.WriteFile(filepath.Join(signatureDir, sigFilename), newSig, 0644); err != nil {
- return err
+ if rootless.IsRootless() && (len(dest.User) == 0 || dest.User == "root") { // if we are rootless and do not have a destination user we can just use sudo
+ return transferRootless(source, dest, podman, parentFlags)
}
- return nil
+ return transferRootful(source, dest, podman, parentFlags)
}
// TransferRootless creates new podman processes using exec.Command and sudo, transferring images between the given source and destination users
@@ -763,7 +726,7 @@ func transferRootless(source entities.ImageScpOptions, dest entities.ImageScpOpt
} else {
cmdSave = exec.Command(podman)
}
- cmdSave = utils.CreateSCPCommand(cmdSave, saveCommand)
+ cmdSave = domainUtils.CreateSCPCommand(cmdSave, saveCommand)
logrus.Debugf("Executing save command: %q", cmdSave)
err := cmdSave.Run()
if err != nil {
@@ -776,8 +739,11 @@ func transferRootless(source entities.ImageScpOptions, dest entities.ImageScpOpt
} else {
cmdLoad = exec.Command(podman)
}
- cmdLoad = utils.CreateSCPCommand(cmdLoad, loadCommand)
+ cmdLoad = domainUtils.CreateSCPCommand(cmdLoad, loadCommand)
logrus.Debugf("Executing load command: %q", cmdLoad)
+ if len(dest.Tag) > 0 {
+ return domainUtils.ScpTag(cmdLoad, podman, dest)
+ }
return cmdLoad.Run()
}
@@ -833,11 +799,20 @@ func transferRootful(source entities.ImageScpOptions, dest entities.ImageScpOpti
return err
}
}
- err = execPodman(uSave, saveCommand)
+ _, err = execTransferPodman(uSave, saveCommand, false)
if err != nil {
return err
}
- return execPodman(uLoad, loadCommand)
+ out, err := execTransferPodman(uLoad, loadCommand, (len(dest.Tag) > 0))
+ if err != nil {
+ return err
+ }
+ if out != nil {
+ image := domainUtils.ExtractImage(out)
+ _, err := execTransferPodman(uLoad, []string{podman, "tag", image, dest.Tag}, false)
+ return err
+ }
+ return nil
}
func lookupUser(u string) (*user.User, error) {
@@ -847,10 +822,10 @@ func lookupUser(u string) (*user.User, error) {
return user.Lookup(u)
}
-func execPodman(execUser *user.User, command []string) error {
- cmdLogin, err := utils.LoginUser(execUser.Username)
+func execTransferPodman(execUser *user.User, command []string, needToTag bool) ([]byte, error) {
+ cmdLogin, err := domainUtils.LoginUser(execUser.Username)
if err != nil {
- return err
+ return nil, err
}
defer func() {
@@ -864,11 +839,11 @@ func execPodman(execUser *user.User, command []string) error {
cmd.Stdout = os.Stdout
uid, err := strconv.ParseInt(execUser.Uid, 10, 32)
if err != nil {
- return err
+ return nil, err
}
gid, err := strconv.ParseInt(execUser.Gid, 10, 32)
if err != nil {
- return err
+ return nil, err
}
cmd.SysProcAttr = &syscall.SysProcAttr{
Credential: &syscall.Credential{
@@ -878,5 +853,55 @@ func execPodman(execUser *user.User, command []string) error {
NoSetGroups: false,
},
}
- return cmd.Run()
+ if needToTag {
+ cmd.Stdout = nil
+ return cmd.Output()
+ }
+ return nil, cmd.Run()
+}
+
+func getSigFilename(sigStoreDirPath string) (string, error) {
+ sigFileSuffix := 1
+ sigFiles, err := ioutil.ReadDir(sigStoreDirPath)
+ if err != nil {
+ return "", err
+ }
+ sigFilenames := make(map[string]bool)
+ for _, file := range sigFiles {
+ sigFilenames[file.Name()] = true
+ }
+ for {
+ sigFilename := "signature-" + strconv.Itoa(sigFileSuffix)
+ if _, exists := sigFilenames[sigFilename]; !exists {
+ return sigFilename, nil
+ }
+ sigFileSuffix++
+ }
+}
+
+func localPathFromURI(url *url.URL) (string, error) {
+ if url.Scheme != "file" {
+ return "", fmt.Errorf("writing to %s is not supported. Use a supported scheme", url.String())
+ }
+ return url.Path, nil
+}
+
+// putSignature creates signature and saves it to the signstore file
+func putSignature(manifestBlob []byte, mech signature.SigningMechanism, sigStoreDir string, instanceDigest digest.Digest, dockerReference dockerRef.Reference, options entities.SignOptions) error {
+ newSig, err := signature.SignDockerManifest(manifestBlob, dockerReference.String(), mech, options.SignBy)
+ if err != nil {
+ return err
+ }
+ signatureDir := fmt.Sprintf("%s@%s=%s", sigStoreDir, instanceDigest.Algorithm(), instanceDigest.Hex())
+ if err := os.MkdirAll(signatureDir, 0751); err != nil {
+ // The directory is allowed to exist
+ if !errors.Is(err, fs.ErrExist) {
+ return err
+ }
+ }
+ sigFilename, err := getSigFilename(signatureDir)
+ if err != nil {
+ return err
+ }
+ return ioutil.WriteFile(filepath.Join(signatureDir, sigFilename), newSig, 0644)
}
diff --git a/pkg/domain/infra/abi/manifest.go b/pkg/domain/infra/abi/manifest.go
index 8b52c335c..d20744d76 100644
--- a/pkg/domain/infra/abi/manifest.go
+++ b/pkg/domain/infra/abi/manifest.go
@@ -4,9 +4,12 @@ import (
"bytes"
"context"
"encoding/json"
+ "fmt"
"os"
"strings"
+ "errors"
+
"github.com/containers/common/libimage"
cp "github.com/containers/image/v5/copy"
"github.com/containers/image/v5/manifest"
@@ -17,7 +20,6 @@ import (
"github.com/containers/storage"
"github.com/opencontainers/go-digest"
imgspecv1 "github.com/opencontainers/image-spec/specs-go/v1"
- "github.com/pkg/errors"
"github.com/sirupsen/logrus"
)
@@ -46,7 +48,7 @@ func (ir *ImageEngine) ManifestCreate(ctx context.Context, name string, images [
func (ir *ImageEngine) ManifestExists(ctx context.Context, name string) (*entities.BoolReport, error) {
_, err := ir.Libpod.LibimageRuntime().LookupManifestList(name)
if err != nil {
- if errors.Cause(err) == storage.ErrImageUnknown {
+ if errors.Is(err, storage.ErrImageUnknown) {
return &entities.BoolReport{Value: false}, nil
}
return nil, err
@@ -63,15 +65,13 @@ func (ir *ImageEngine) ManifestInspect(ctx context.Context, name string) ([]byte
manifestList, err := ir.Libpod.LibimageRuntime().LookupManifestList(name)
if err != nil {
- switch errors.Cause(err) {
- // Do a remote inspect if there's no local image or if the
- // local image is not a manifest list.
- case storage.ErrImageUnknown, libimage.ErrNotAManifestList:
+ if errors.Is(err, storage.ErrImageUnknown) || errors.Is(err, libimage.ErrNotAManifestList) {
+ // Do a remote inspect if there's no local image or if the
+ // local image is not a manifest list.
return ir.remoteManifestInspect(ctx, name)
-
- default:
- return nil, err
}
+
+ return nil, err
}
schema2List, err := manifestList.Inspect()
@@ -86,7 +86,7 @@ func (ir *ImageEngine) ManifestInspect(ctx context.Context, name string) ([]byte
var b bytes.Buffer
if err := json.Indent(&b, rawSchema2List, "", " "); err != nil {
- return nil, errors.Wrapf(err, "error rendering manifest %s for display", name)
+ return nil, fmt.Errorf("error rendering manifest %s for display: %w", name, err)
}
return b.Bytes(), nil
}
@@ -113,8 +113,7 @@ func (ir *ImageEngine) remoteManifestInspect(ctx context.Context, name string) (
// FIXME should we use multierror package instead?
// we want the new line here so ignore the linter
- //nolint:revive
- latestErr = errors.Wrapf(latestErr, "tried %v\n", e)
+ latestErr = fmt.Errorf("tried %v\n: %w", e, latestErr)
}
}
@@ -125,14 +124,14 @@ func (ir *ImageEngine) remoteManifestInspect(ctx context.Context, name string) (
}
src, err := ref.NewImageSource(ctx, sys)
if err != nil {
- appendErr(errors.Wrapf(err, "reading image %q", transports.ImageName(ref)))
+ appendErr(fmt.Errorf("reading image %q: %w", transports.ImageName(ref), err))
continue
}
defer src.Close()
manifestBytes, manifestType, err := src.GetManifest(ctx, nil)
if err != nil {
- appendErr(errors.Wrapf(err, "loading manifest %q", transports.ImageName(ref)))
+ appendErr(fmt.Errorf("loading manifest %q: %w", transports.ImageName(ref), err))
continue
}
@@ -150,7 +149,7 @@ func (ir *ImageEngine) remoteManifestInspect(ctx context.Context, name string) (
logrus.Warnf("The manifest type %s is not a manifest list but a single image.", manType)
schema2Manifest, err := manifest.Schema2FromManifest(result)
if err != nil {
- return nil, errors.Wrapf(err, "error parsing manifest blob %q as a %q", string(result), manType)
+ return nil, fmt.Errorf("error parsing manifest blob %q as a %q: %w", string(result), manType, err)
}
if result, err = schema2Manifest.Serialize(); err != nil {
return nil, err
@@ -158,7 +157,7 @@ func (ir *ImageEngine) remoteManifestInspect(ctx context.Context, name string) (
default:
listBlob, err := manifest.ListFromBlob(result, manType)
if err != nil {
- return nil, errors.Wrapf(err, "error parsing manifest blob %q as a %q", string(result), manType)
+ return nil, fmt.Errorf("error parsing manifest blob %q as a %q: %w", string(result), manType, err)
}
list, err := listBlob.ConvertToMIMEType(manifest.DockerV2ListMediaType)
if err != nil {
@@ -170,7 +169,7 @@ func (ir *ImageEngine) remoteManifestInspect(ctx context.Context, name string) (
}
if err = json.Indent(&b, result, "", " "); err != nil {
- return nil, errors.Wrapf(err, "error rendering manifest %s for display", name)
+ return nil, fmt.Errorf("error rendering manifest %s for display: %w", name, err)
}
return b.Bytes(), nil
}
@@ -213,7 +212,7 @@ func (ir *ImageEngine) ManifestAdd(ctx context.Context, name string, images []st
for _, annotationSpec := range opts.Annotation {
spec := strings.SplitN(annotationSpec, "=", 2)
if len(spec) != 2 {
- return "", errors.Errorf("no value given for annotation %q", spec[0])
+ return "", fmt.Errorf("no value given for annotation %q", spec[0])
}
annotations[spec[0]] = spec[1]
}
@@ -231,7 +230,7 @@ func (ir *ImageEngine) ManifestAdd(ctx context.Context, name string, images []st
func (ir *ImageEngine) ManifestAnnotate(ctx context.Context, name, image string, opts entities.ManifestAnnotateOptions) (string, error) {
instanceDigest, err := digest.Parse(image)
if err != nil {
- return "", errors.Errorf(`invalid image digest "%s": %v`, image, err)
+ return "", fmt.Errorf(`invalid image digest "%s": %v`, image, err)
}
manifestList, err := ir.Libpod.LibimageRuntime().LookupManifestList(name)
@@ -251,7 +250,7 @@ func (ir *ImageEngine) ManifestAnnotate(ctx context.Context, name, image string,
for _, annotationSpec := range opts.Annotation {
spec := strings.SplitN(annotationSpec, "=", 2)
if len(spec) != 2 {
- return "", errors.Errorf("no value given for annotation %q", spec[0])
+ return "", fmt.Errorf("no value given for annotation %q", spec[0])
}
annotations[spec[0]] = spec[1]
}
@@ -269,7 +268,7 @@ func (ir *ImageEngine) ManifestAnnotate(ctx context.Context, name, image string,
func (ir *ImageEngine) ManifestRemoveDigest(ctx context.Context, name, image string) (string, error) {
instanceDigest, err := digest.Parse(image)
if err != nil {
- return "", errors.Errorf(`invalid image digest "%s": %v`, image, err)
+ return "", fmt.Errorf(`invalid image digest "%s": %v`, image, err)
}
manifestList, err := ir.Libpod.LibimageRuntime().LookupManifestList(name)
@@ -293,7 +292,7 @@ func (ir *ImageEngine) ManifestRm(ctx context.Context, names []string) (report *
func (ir *ImageEngine) ManifestPush(ctx context.Context, name, destination string, opts entities.ImagePushOptions) (string, error) {
manifestList, err := ir.Libpod.LibimageRuntime().LookupManifestList(name)
if err != nil {
- return "", errors.Wrapf(err, "error retrieving local image from image name %s", name)
+ return "", fmt.Errorf("error retrieving local image from image name %s: %w", name, err)
}
var manifestType string
@@ -304,7 +303,7 @@ func (ir *ImageEngine) ManifestPush(ctx context.Context, name, destination strin
case "v2s2", "docker":
manifestType = manifest.DockerV2Schema2MediaType
default:
- return "", errors.Errorf("unknown format %q. Choose one of the supported formats: 'oci' or 'v2s2'", opts.Format)
+ return "", fmt.Errorf("unknown format %q. Choose one of the supported formats: 'oci' or 'v2s2'", opts.Format)
}
}
@@ -333,7 +332,7 @@ func (ir *ImageEngine) ManifestPush(ctx context.Context, name, destination strin
if opts.Rm {
if _, rmErrors := ir.Libpod.LibimageRuntime().RemoveImages(ctx, []string{manifestList.ID()}, nil); len(rmErrors) > 0 {
- return "", errors.Wrap(rmErrors[0], "error removing manifest after push")
+ return "", fmt.Errorf("error removing manifest after push: %w", rmErrors[0])
}
}
diff --git a/pkg/domain/infra/abi/network.go b/pkg/domain/infra/abi/network.go
index c5f3cf8b3..2428abfe9 100644
--- a/pkg/domain/infra/abi/network.go
+++ b/pkg/domain/infra/abi/network.go
@@ -2,20 +2,51 @@ package abi
import (
"context"
+ "errors"
+ "fmt"
+ "strconv"
"github.com/containers/common/libnetwork/types"
netutil "github.com/containers/common/libnetwork/util"
"github.com/containers/common/pkg/util"
"github.com/containers/podman/v4/libpod/define"
"github.com/containers/podman/v4/pkg/domain/entities"
- "github.com/pkg/errors"
)
func (ic *ContainerEngine) NetworkList(ctx context.Context, options entities.NetworkListOptions) ([]types.Network, error) {
+ // dangling filter is not provided by netutil
+ var wantDangling bool
+
+ val, filterDangling := options.Filters["dangling"]
+ if filterDangling {
+ switch len(val) {
+ case 0:
+ return nil, fmt.Errorf("got no values for filter key \"dangling\"")
+ case 1:
+ var err error
+ wantDangling, err = strconv.ParseBool(val[0])
+ if err != nil {
+ return nil, fmt.Errorf("invalid dangling filter value \"%v\"", val[0])
+ }
+ delete(options.Filters, "dangling")
+ default:
+ return nil, fmt.Errorf("got more than one value for filter key \"dangling\"")
+ }
+ }
+
filters, err := netutil.GenerateNetworkFilters(options.Filters)
if err != nil {
return nil, err
}
+
+ if filterDangling {
+ danglingFilterFunc, err := ic.createDanglingFilterFunc(wantDangling)
+ if err != nil {
+ return nil, err
+ }
+
+ filters = append(filters, danglingFilterFunc)
+ }
nets, err := ic.Libpod.Network().NetworkList(filters...)
return nets, err
}
@@ -26,11 +57,11 @@ func (ic *ContainerEngine) NetworkInspect(ctx context.Context, namesOrIds []stri
for _, name := range namesOrIds {
net, err := ic.Libpod.Network().NetworkInspect(name)
if err != nil {
- if errors.Cause(err) == define.ErrNoSuchNetwork {
- errs = append(errs, errors.Wrapf(err, "network %s", name))
+ if errors.Is(err, define.ErrNoSuchNetwork) {
+ errs = append(errs, fmt.Errorf("network %s: %w", name, err))
continue
} else {
- return nil, nil, errors.Wrapf(err, "error inspecting network %s", name)
+ return nil, nil, fmt.Errorf("error inspecting network %s: %w", name, err)
}
}
networks = append(networks, net)
@@ -50,8 +81,8 @@ func (ic *ContainerEngine) NetworkReload(ctx context.Context, names []string, op
report.Id = ctr.ID()
report.Err = ctr.ReloadNetwork()
// ignore errors for invalid ctr state and network mode when --all is used
- if options.All && (errors.Cause(report.Err) == define.ErrCtrStateInvalid ||
- errors.Cause(report.Err) == define.ErrNetworkModeInvalid) {
+ if options.All && (errors.Is(report.Err, define.ErrCtrStateInvalid) ||
+ errors.Is(report.Err, define.ErrNetworkModeInvalid)) {
continue
}
reports = append(reports, report)
@@ -83,7 +114,7 @@ func (ic *ContainerEngine) NetworkRm(ctx context.Context, namesOrIds []string, o
// if user passes force, we nuke containers and pods
if !options.Force {
// Without the force option, we return an error
- return reports, errors.Wrapf(define.ErrNetworkInUse, "%q has associated containers with it. Use -f to forcibly delete containers and pods", name)
+ return reports, fmt.Errorf("%q has associated containers with it. Use -f to forcibly delete containers and pods: %w", name, define.ErrNetworkInUse)
}
if c.IsInfra() {
// if we have a infra container we need to remove the pod
@@ -94,7 +125,7 @@ func (ic *ContainerEngine) NetworkRm(ctx context.Context, namesOrIds []string, o
if err := ic.Libpod.RemovePod(ctx, pod, true, true, options.Timeout); err != nil {
return reports, err
}
- } else if err := ic.Libpod.RemoveContainer(ctx, c, true, true, options.Timeout); err != nil && errors.Cause(err) != define.ErrNoSuchCtr {
+ } else if err := ic.Libpod.RemoveContainer(ctx, c, true, true, options.Timeout); err != nil && !errors.Is(err, define.ErrNoSuchCtr) {
return reports, err
}
}
@@ -109,7 +140,7 @@ func (ic *ContainerEngine) NetworkRm(ctx context.Context, namesOrIds []string, o
func (ic *ContainerEngine) NetworkCreate(ctx context.Context, network types.Network) (*types.Network, error) {
if util.StringInSlice(network.Name, []string{"none", "host", "bridge", "private", "slirp4netns", "container", "ns"}) {
- return nil, errors.Errorf("cannot create network with name %q because it conflicts with a valid network mode", network.Name)
+ return nil, fmt.Errorf("cannot create network with name %q because it conflicts with a valid network mode", network.Name)
}
network, err := ic.Libpod.Network().NetworkCreate(network)
if err != nil {
@@ -144,6 +175,33 @@ func (ic *ContainerEngine) NetworkExists(ctx context.Context, networkname string
// Network prune removes unused networks
func (ic *ContainerEngine) NetworkPrune(ctx context.Context, options entities.NetworkPruneOptions) ([]*entities.NetworkPruneReport, error) {
+ // get all filters
+ filters, err := netutil.GenerateNetworkPruneFilters(options.Filters)
+ if err != nil {
+ return nil, err
+ }
+ danglingFilterFunc, err := ic.createDanglingFilterFunc(true)
+ if err != nil {
+ return nil, err
+ }
+ filters = append(filters, danglingFilterFunc)
+ nets, err := ic.Libpod.Network().NetworkList(filters...)
+ if err != nil {
+ return nil, err
+ }
+
+ pruneReport := make([]*entities.NetworkPruneReport, 0, len(nets))
+ for _, net := range nets {
+ pruneReport = append(pruneReport, &entities.NetworkPruneReport{
+ Name: net.Name,
+ Error: ic.Libpod.Network().NetworkRemove(net.Name),
+ })
+ }
+ return pruneReport, nil
+}
+
+// danglingFilter function is special and not implemented in libnetwork filters
+func (ic *ContainerEngine) createDanglingFilterFunc(wantDangling bool) (types.FilterFunc, error) {
cons, err := ic.Libpod.GetAllContainers()
if err != nil {
return nil, err
@@ -163,31 +221,12 @@ func (ic *ContainerEngine) NetworkPrune(ctx context.Context, options entities.Ne
// ignore the default network, this one cannot be deleted
networksToKeep[ic.Libpod.GetDefaultNetworkName()] = true
- // get all filters
- filters, err := netutil.GenerateNetworkPruneFilters(options.Filters)
- if err != nil {
- return nil, err
- }
- danglingFilterFunc := func(net types.Network) bool {
+ return func(net types.Network) bool {
for network := range networksToKeep {
if network == net.Name {
- return false
+ return !wantDangling
}
}
- return true
- }
- filters = append(filters, danglingFilterFunc)
- nets, err := ic.Libpod.Network().NetworkList(filters...)
- if err != nil {
- return nil, err
- }
-
- pruneReport := make([]*entities.NetworkPruneReport, 0, len(nets))
- for _, net := range nets {
- pruneReport = append(pruneReport, &entities.NetworkPruneReport{
- Name: net.Name,
- Error: ic.Libpod.Network().NetworkRemove(net.Name),
- })
- }
- return pruneReport, nil
+ return wantDangling
+ }, nil
}
diff --git a/pkg/domain/infra/abi/parse/parse.go b/pkg/domain/infra/abi/parse/parse.go
index 66794e592..4e8c2e508 100644
--- a/pkg/domain/infra/abi/parse/parse.go
+++ b/pkg/domain/infra/abi/parse/parse.go
@@ -78,6 +78,16 @@ func VolumeOptions(opts map[string]string) ([]libpod.VolumeCreateOption, error)
libpodOptions = append(libpodOptions, libpod.WithVolumeDisableQuota())
// set option "NOQUOTA": "true"
volumeOptions["NOQUOTA"] = "true"
+ case "timeout":
+ if len(splitO) != 2 {
+ return nil, errors.Wrapf(define.ErrInvalidArg, "timeout option must provide a valid timeout in seconds")
+ }
+ intTimeout, err := strconv.Atoi(splitO[1])
+ if err != nil {
+ return nil, errors.Wrapf(err, "cannot convert Timeout %s to an integer", splitO[1])
+ }
+ logrus.Debugf("Removing timeout from options and adding WithTimeout for Timeout %d", intTimeout)
+ libpodOptions = append(libpodOptions, libpod.WithVolumeDriverTimeout(intTimeout))
default:
finalVal = append(finalVal, o)
}
diff --git a/pkg/domain/infra/abi/play.go b/pkg/domain/infra/abi/play.go
index e04ab3a1a..c913fdb69 100644
--- a/pkg/domain/infra/abi/play.go
+++ b/pkg/domain/infra/abi/play.go
@@ -3,6 +3,7 @@ package abi
import (
"bytes"
"context"
+ "errors"
"fmt"
"io"
"io/ioutil"
@@ -29,9 +30,8 @@ import (
"github.com/containers/podman/v4/pkg/util"
"github.com/ghodss/yaml"
"github.com/opencontainers/go-digest"
- "github.com/pkg/errors"
"github.com/sirupsen/logrus"
- yamlv2 "gopkg.in/yaml.v2"
+ yamlv3 "gopkg.in/yaml.v3"
)
// createServiceContainer creates a container that can later on
@@ -114,7 +114,7 @@ func (ic *ContainerEngine) PlayKube(ctx context.Context, body io.Reader, options
// sort kube kinds
documentList, err = sortKubeKinds(documentList)
if err != nil {
- return nil, errors.Wrap(err, "unable to sort kube kinds")
+ return nil, fmt.Errorf("unable to sort kube kinds: %w", err)
}
ipIndex := 0
@@ -126,7 +126,7 @@ func (ic *ContainerEngine) PlayKube(ctx context.Context, body io.Reader, options
for _, document := range documentList {
kind, err := getKubeKind(document)
if err != nil {
- return nil, errors.Wrap(err, "unable to read kube YAML")
+ return nil, fmt.Errorf("unable to read kube YAML: %w", err)
}
// TODO: create constants for the various "kinds" of yaml files.
@@ -154,14 +154,14 @@ func (ic *ContainerEngine) PlayKube(ctx context.Context, body io.Reader, options
var podTemplateSpec v1.PodTemplateSpec
if err := yaml.Unmarshal(document, &podYAML); err != nil {
- return nil, errors.Wrap(err, "unable to read YAML as Kube Pod")
+ return nil, fmt.Errorf("unable to read YAML as Kube Pod: %w", err)
}
podTemplateSpec.ObjectMeta = podYAML.ObjectMeta
podTemplateSpec.Spec = podYAML.Spec
for name, val := range podYAML.Annotations {
if len(val) > define.MaxKubeAnnotation {
- return nil, errors.Errorf("invalid annotation %q=%q value length exceeds Kubernetetes max %d", name, val, define.MaxKubeAnnotation)
+ return nil, fmt.Errorf("invalid annotation %q=%q value length exceeds Kubernetetes max %d", name, val, define.MaxKubeAnnotation)
}
}
for name, val := range options.Annotations {
@@ -182,7 +182,7 @@ func (ic *ContainerEngine) PlayKube(ctx context.Context, body io.Reader, options
var deploymentYAML v1apps.Deployment
if err := yaml.Unmarshal(document, &deploymentYAML); err != nil {
- return nil, errors.Wrap(err, "unable to read YAML as Kube Deployment")
+ return nil, fmt.Errorf("unable to read YAML as Kube Deployment: %w", err)
}
r, err := ic.playKubeDeployment(ctx, &deploymentYAML, options, &ipIndex, configMaps, serviceContainer)
@@ -196,7 +196,7 @@ func (ic *ContainerEngine) PlayKube(ctx context.Context, body io.Reader, options
var pvcYAML v1.PersistentVolumeClaim
if err := yaml.Unmarshal(document, &pvcYAML); err != nil {
- return nil, errors.Wrap(err, "unable to read YAML as Kube PersistentVolumeClaim")
+ return nil, fmt.Errorf("unable to read YAML as Kube PersistentVolumeClaim: %w", err)
}
r, err := ic.playKubePVC(ctx, &pvcYAML)
@@ -210,7 +210,7 @@ func (ic *ContainerEngine) PlayKube(ctx context.Context, body io.Reader, options
var configMap v1.ConfigMap
if err := yaml.Unmarshal(document, &configMap); err != nil {
- return nil, errors.Wrap(err, "unable to read YAML as Kube ConfigMap")
+ return nil, fmt.Errorf("unable to read YAML as Kube ConfigMap: %w", err)
}
configMaps = append(configMaps, configMap)
default:
@@ -240,7 +240,7 @@ func (ic *ContainerEngine) playKubeDeployment(ctx context.Context, deploymentYAM
deploymentName = deploymentYAML.ObjectMeta.Name
if deploymentName == "" {
- return nil, errors.Errorf("Deployment does not have a name")
+ return nil, errors.New("deployment does not have a name")
}
numReplicas = 1
if deploymentYAML.Spec.Replicas != nil {
@@ -253,7 +253,7 @@ func (ic *ContainerEngine) playKubeDeployment(ctx context.Context, deploymentYAM
podName := fmt.Sprintf("%s-pod-%d", deploymentName, i)
podReport, err := ic.playKubePod(ctx, podName, &podSpec, options, ipIndex, deploymentYAML.Annotations, configMaps, serviceContainer)
if err != nil {
- return nil, errors.Wrapf(err, "error encountered while bringing up pod %s", podName)
+ return nil, fmt.Errorf("error encountered while bringing up pod %s: %w", podName, err)
}
report.Pods = append(report.Pods, podReport.Pods...)
}
@@ -275,7 +275,7 @@ func (ic *ContainerEngine) playKubePod(ctx context.Context, podName string, podY
// Assert the pod has a name
if podName == "" {
- return nil, errors.Errorf("pod does not have a name")
+ return nil, fmt.Errorf("pod does not have a name")
}
podOpt := entities.PodCreateOptions{
@@ -295,7 +295,7 @@ func (ic *ContainerEngine) playKubePod(ctx context.Context, podName string, podY
}
if (ns.IsBridge() && len(networks) == 0) || ns.IsHost() {
- return nil, errors.Errorf("invalid value passed to --network: bridge or host networking must be configured in YAML")
+ return nil, fmt.Errorf("invalid value passed to --network: bridge or host networking must be configured in YAML")
}
podOpt.Net.Network = ns
@@ -316,10 +316,10 @@ func (ic *ContainerEngine) playKubePod(ctx context.Context, podName string, podY
// FIXME This is very hard to support properly with a good ux
if len(options.StaticIPs) > *ipIndex {
if !podOpt.Net.Network.IsBridge() {
- return nil, errors.Wrap(define.ErrInvalidArg, "static ip addresses can only be set when the network mode is bridge")
+ return nil, fmt.Errorf("static ip addresses can only be set when the network mode is bridge: %w", define.ErrInvalidArg)
}
if len(podOpt.Net.Networks) != 1 {
- return nil, errors.Wrap(define.ErrInvalidArg, "cannot set static ip addresses for more than network, use netname:ip=<ip> syntax to specify ips for more than network")
+ return nil, fmt.Errorf("cannot set static ip addresses for more than network, use netname:ip=<ip> syntax to specify ips for more than network: %w", define.ErrInvalidArg)
}
for name, netOpts := range podOpt.Net.Networks {
netOpts.StaticIPs = append(netOpts.StaticIPs, options.StaticIPs[*ipIndex])
@@ -331,10 +331,10 @@ func (ic *ContainerEngine) playKubePod(ctx context.Context, podName string, podY
}
if len(options.StaticMACs) > *ipIndex {
if !podOpt.Net.Network.IsBridge() {
- return nil, errors.Wrap(define.ErrInvalidArg, "static mac address can only be set when the network mode is bridge")
+ return nil, fmt.Errorf("static mac address can only be set when the network mode is bridge: %w", define.ErrInvalidArg)
}
if len(podOpt.Net.Networks) != 1 {
- return nil, errors.Wrap(define.ErrInvalidArg, "cannot set static mac address for more than network, use netname:mac=<mac> syntax to specify mac for more than network")
+ return nil, fmt.Errorf("cannot set static mac address for more than network, use netname:mac=<mac> syntax to specify mac for more than network: %w", define.ErrInvalidArg)
}
for name, netOpts := range podOpt.Net.Networks {
netOpts.StaticMAC = nettypes.HardwareAddr(options.StaticMACs[*ipIndex])
@@ -370,11 +370,11 @@ func (ic *ContainerEngine) playKubePod(ctx context.Context, podName string, podY
cm, err := readConfigMapFromFile(f)
if err != nil {
- return nil, errors.Wrapf(err, "%q", p)
+ return nil, fmt.Errorf("%q: %w", p, err)
}
if _, present := configMapIndex[cm.Name]; present {
- return nil, errors.Errorf("ambiguous configuration: the same config map %s is present in YAML and in --configmaps %s file", cm.Name, p)
+ return nil, fmt.Errorf("ambiguous configuration: the same config map %s is present in YAML and in --configmaps %s file", cm.Name, p)
}
configMaps = append(configMaps, cm)
@@ -396,22 +396,22 @@ func (ic *ContainerEngine) playKubePod(ctx context.Context, podName string, podY
// error out instead reuse the current volume.
vol, err = ic.Libpod.GetVolume(v.Source)
if err != nil {
- return nil, errors.Wrapf(err, "cannot re-use local volume for volume from configmap %q", v.Source)
+ return nil, fmt.Errorf("cannot re-use local volume for volume from configmap %q: %w", v.Source, err)
}
} else {
- return nil, errors.Wrapf(err, "cannot create a local volume for volume from configmap %q", v.Source)
+ return nil, fmt.Errorf("cannot create a local volume for volume from configmap %q: %w", v.Source, err)
}
}
mountPoint, err := vol.MountPoint()
if err != nil || mountPoint == "" {
- return nil, errors.Wrapf(err, "unable to get mountpoint of volume %q", vol.Name())
+ return nil, fmt.Errorf("unable to get mountpoint of volume %q: %w", vol.Name(), err)
}
// Create files and add data to the volume mountpoint based on the Items in the volume
for k, v := range v.Items {
dataPath := filepath.Join(mountPoint, k)
f, err := os.Create(dataPath)
if err != nil {
- return nil, errors.Wrapf(err, "cannot create file %q at volume mountpoint %q", k, mountPoint)
+ return nil, fmt.Errorf("cannot create file %q at volume mountpoint %q: %w", k, mountPoint, err)
}
defer f.Close()
_, err = f.WriteString(v)
@@ -492,12 +492,12 @@ func (ic *ContainerEngine) playKubePod(ctx context.Context, podName string, podY
for _, initCtr := range podYAML.Spec.InitContainers {
// Error out if same name is used for more than one container
if _, ok := ctrNames[initCtr.Name]; ok {
- return nil, errors.Errorf("the pod %q is invalid; duplicate container name %q detected", podName, initCtr.Name)
+ return nil, fmt.Errorf("the pod %q is invalid; duplicate container name %q detected", podName, initCtr.Name)
}
ctrNames[initCtr.Name] = ""
// Init containers cannot have either of lifecycle, livenessProbe, readinessProbe, or startupProbe set
if initCtr.Lifecycle != nil || initCtr.LivenessProbe != nil || initCtr.ReadinessProbe != nil || initCtr.StartupProbe != nil {
- return nil, errors.Errorf("cannot create an init container that has either of lifecycle, livenessProbe, readinessProbe, or startupProbe set")
+ return nil, fmt.Errorf("cannot create an init container that has either of lifecycle, livenessProbe, readinessProbe, or startupProbe set")
}
pulledImage, labels, err := ic.getImageAndLabelInfo(ctx, cwd, annotations, writer, initCtr, options)
if err != nil {
@@ -548,7 +548,7 @@ func (ic *ContainerEngine) playKubePod(ctx context.Context, podName string, podY
for _, container := range podYAML.Spec.Containers {
// Error out if the same name is used for more than one container
if _, ok := ctrNames[container.Name]; ok {
- return nil, errors.Errorf("the pod %q is invalid; duplicate container name %q detected", podName, container.Name)
+ return nil, fmt.Errorf("the pod %q is invalid; duplicate container name %q detected", podName, container.Name)
}
ctrNames[container.Name] = ""
pulledImage, labels, err := ic.getImageAndLabelInfo(ctx, cwd, annotations, writer, container, options)
@@ -599,11 +599,11 @@ func (ic *ContainerEngine) playKubePod(ctx context.Context, podName string, podY
if options.Start != types.OptionalBoolFalse {
// Start the containers
podStartErrors, err := pod.Start(ctx)
- if err != nil && errors.Cause(err) != define.ErrPodPartialFail {
+ if err != nil && !errors.Is(err, define.ErrPodPartialFail) {
return nil, err
}
for id, err := range podStartErrors {
- playKubePod.ContainerErrors = append(playKubePod.ContainerErrors, errors.Wrapf(err, "error starting container %s", id).Error())
+ playKubePod.ContainerErrors = append(playKubePod.ContainerErrors, fmt.Errorf("error starting container %s: %w", id, err).Error())
fmt.Println(playKubePod.ContainerErrors)
}
}
@@ -735,14 +735,14 @@ func (ic *ContainerEngine) playKubePVC(ctx context.Context, pvcYAML *v1.Persiste
case util.VolumeUIDAnnotation:
uid, err := strconv.Atoi(v)
if err != nil {
- return nil, errors.Wrapf(err, "cannot convert uid %s to integer", v)
+ return nil, fmt.Errorf("cannot convert uid %s to integer: %w", v, err)
}
volOptions = append(volOptions, libpod.WithVolumeUID(uid))
opts["UID"] = v
case util.VolumeGIDAnnotation:
gid, err := strconv.Atoi(v)
if err != nil {
- return nil, errors.Wrapf(err, "cannot convert gid %s to integer", v)
+ return nil, fmt.Errorf("cannot convert gid %s to integer: %w", v, err)
}
volOptions = append(volOptions, libpod.WithVolumeGID(gid))
opts["GID"] = v
@@ -771,15 +771,15 @@ func readConfigMapFromFile(r io.Reader) (v1.ConfigMap, error) {
content, err := ioutil.ReadAll(r)
if err != nil {
- return cm, errors.Wrapf(err, "unable to read ConfigMap YAML content")
+ return cm, fmt.Errorf("unable to read ConfigMap YAML content: %w", err)
}
if err := yaml.Unmarshal(content, &cm); err != nil {
- return cm, errors.Wrapf(err, "unable to read YAML as Kube ConfigMap")
+ return cm, fmt.Errorf("unable to read YAML as Kube ConfigMap: %w", err)
}
if cm.Kind != "ConfigMap" {
- return cm, errors.Errorf("invalid YAML kind: %q. [ConfigMap] is the only supported by --configmap", cm.Kind)
+ return cm, fmt.Errorf("invalid YAML kind: %q. [ConfigMap] is the only supported by --configmap", cm.Kind)
}
return cm, nil
@@ -790,7 +790,7 @@ func readConfigMapFromFile(r io.Reader) (v1.ConfigMap, error) {
func splitMultiDocYAML(yamlContent []byte) ([][]byte, error) {
var documentList [][]byte
- d := yamlv2.NewDecoder(bytes.NewReader(yamlContent))
+ d := yamlv3.NewDecoder(bytes.NewReader(yamlContent))
for {
var o interface{}
// read individual document
@@ -799,14 +799,14 @@ func splitMultiDocYAML(yamlContent []byte) ([][]byte, error) {
break
}
if err != nil {
- return nil, errors.Wrapf(err, "multi doc yaml could not be split")
+ return nil, fmt.Errorf("multi doc yaml could not be split: %w", err)
}
if o != nil {
// back to bytes
- document, err := yamlv2.Marshal(o)
+ document, err := yamlv3.Marshal(o)
if err != nil {
- return nil, errors.Wrapf(err, "individual doc yaml could not be marshalled")
+ return nil, fmt.Errorf("individual doc yaml could not be marshalled: %w", err)
}
documentList = append(documentList, document)
@@ -915,27 +915,27 @@ func (ic *ContainerEngine) PlayKubeDown(ctx context.Context, body io.Reader, _ e
// sort kube kinds
documentList, err = sortKubeKinds(documentList)
if err != nil {
- return nil, errors.Wrap(err, "unable to sort kube kinds")
+ return nil, fmt.Errorf("unable to sort kube kinds: %w", err)
}
for _, document := range documentList {
kind, err := getKubeKind(document)
if err != nil {
- return nil, errors.Wrap(err, "unable to read as kube YAML")
+ return nil, fmt.Errorf("unable to read as kube YAML: %w", err)
}
switch kind {
case "Pod":
var podYAML v1.Pod
if err := yaml.Unmarshal(document, &podYAML); err != nil {
- return nil, errors.Wrap(err, "unable to read YAML as Kube Pod")
+ return nil, fmt.Errorf("unable to read YAML as Kube Pod: %w", err)
}
podNames = append(podNames, podYAML.ObjectMeta.Name)
case "Deployment":
var deploymentYAML v1apps.Deployment
if err := yaml.Unmarshal(document, &deploymentYAML); err != nil {
- return nil, errors.Wrap(err, "unable to read YAML as Kube Deployment")
+ return nil, fmt.Errorf("unable to read YAML as Kube Deployment: %w", err)
}
var numReplicas int32 = 1
deploymentName := deploymentYAML.ObjectMeta.Name
diff --git a/pkg/domain/infra/abi/pods.go b/pkg/domain/infra/abi/pods.go
index 8638f4783..9a16f7fcd 100644
--- a/pkg/domain/infra/abi/pods.go
+++ b/pkg/domain/infra/abi/pods.go
@@ -2,6 +2,8 @@ package abi
import (
"context"
+ "errors"
+ "fmt"
"strconv"
"strings"
@@ -12,7 +14,6 @@ import (
"github.com/containers/podman/v4/pkg/signal"
"github.com/containers/podman/v4/pkg/specgen"
"github.com/containers/podman/v4/pkg/specgen/generate"
- "github.com/pkg/errors"
"github.com/sirupsen/logrus"
)
@@ -49,7 +50,7 @@ func getPodsByContext(all, latest bool, pods []string, runtime *libpod.Runtime)
func (ic *ContainerEngine) PodExists(ctx context.Context, nameOrID string) (*entities.BoolReport, error) {
_, err := ic.Libpod.LookupPod(nameOrID)
- if err != nil && errors.Cause(err) != define.ErrNoSuchPod {
+ if err != nil && !errors.Is(err, define.ErrNoSuchPod) {
return nil, err
}
return &entities.BoolReport{Value: err == nil}, nil
@@ -69,14 +70,14 @@ func (ic *ContainerEngine) PodKill(ctx context.Context, namesOrIds []string, opt
for _, p := range pods {
report := entities.PodKillReport{Id: p.ID()}
conErrs, err := p.Kill(ctx, uint(sig))
- if err != nil && errors.Cause(err) != define.ErrPodPartialFail {
+ if err != nil && !errors.Is(err, define.ErrPodPartialFail) {
report.Errs = []error{err}
reports = append(reports, &report)
continue
}
if len(conErrs) > 0 {
for id, err := range conErrs {
- report.Errs = append(report.Errs, errors.Wrapf(err, "error killing container %s", id))
+ report.Errs = append(report.Errs, fmt.Errorf("error killing container %s: %w", id, err))
}
reports = append(reports, &report)
continue
@@ -110,7 +111,7 @@ func (ic *ContainerEngine) PodLogs(ctx context.Context, nameOrID string, options
}
}
if !ctrFound {
- return errors.Wrapf(define.ErrNoSuchCtr, "container %s is not in pod %s", options.ContainerName, nameOrID)
+ return fmt.Errorf("container %s is not in pod %s: %w", options.ContainerName, nameOrID, define.ErrNoSuchCtr)
}
} else {
// No container name specified select all containers
@@ -135,13 +136,13 @@ func (ic *ContainerEngine) PodPause(ctx context.Context, namesOrIds []string, op
for _, p := range pods {
report := entities.PodPauseReport{Id: p.ID()}
errs, err := p.Pause(ctx)
- if err != nil && errors.Cause(err) != define.ErrPodPartialFail {
+ if err != nil && !errors.Is(err, define.ErrPodPartialFail) {
report.Errs = []error{err}
continue
}
if len(errs) > 0 {
for id, v := range errs {
- report.Errs = append(report.Errs, errors.Wrapf(v, "error pausing container %s", id))
+ report.Errs = append(report.Errs, fmt.Errorf("error pausing container %s: %w", id, v))
}
reports = append(reports, &report)
continue
@@ -158,15 +159,23 @@ func (ic *ContainerEngine) PodUnpause(ctx context.Context, namesOrIds []string,
return nil, err
}
for _, p := range pods {
+ status, err := p.GetPodStatus()
+ if err != nil {
+ return nil, err
+ }
+ // If the pod is not paused or degraded, there is no need to attempt an unpause on it
+ if status != define.PodStatePaused && status != define.PodStateDegraded {
+ continue
+ }
report := entities.PodUnpauseReport{Id: p.ID()}
errs, err := p.Unpause(ctx)
- if err != nil && errors.Cause(err) != define.ErrPodPartialFail {
+ if err != nil && !errors.Is(err, define.ErrPodPartialFail) {
report.Errs = []error{err}
continue
}
if len(errs) > 0 {
for id, v := range errs {
- report.Errs = append(report.Errs, errors.Wrapf(v, "error unpausing container %s", id))
+ report.Errs = append(report.Errs, fmt.Errorf("error unpausing container %s: %w", id, v))
}
reports = append(reports, &report)
continue
@@ -179,19 +188,19 @@ func (ic *ContainerEngine) PodUnpause(ctx context.Context, namesOrIds []string,
func (ic *ContainerEngine) PodStop(ctx context.Context, namesOrIds []string, options entities.PodStopOptions) ([]*entities.PodStopReport, error) {
reports := []*entities.PodStopReport{}
pods, err := getPodsByContext(options.All, options.Latest, namesOrIds, ic.Libpod)
- if err != nil && !(options.Ignore && errors.Cause(err) == define.ErrNoSuchPod) {
+ if err != nil && !(options.Ignore && errors.Is(err, define.ErrNoSuchPod)) {
return nil, err
}
for _, p := range pods {
report := entities.PodStopReport{Id: p.ID()}
errs, err := p.StopWithTimeout(ctx, false, options.Timeout)
- if err != nil && errors.Cause(err) != define.ErrPodPartialFail {
+ if err != nil && !errors.Is(err, define.ErrPodPartialFail) {
report.Errs = []error{err}
continue
}
if len(errs) > 0 {
for id, v := range errs {
- report.Errs = append(report.Errs, errors.Wrapf(v, "error stopping container %s", id))
+ report.Errs = append(report.Errs, fmt.Errorf("error stopping container %s: %w", id, v))
}
reports = append(reports, &report)
continue
@@ -210,14 +219,14 @@ func (ic *ContainerEngine) PodRestart(ctx context.Context, namesOrIds []string,
for _, p := range pods {
report := entities.PodRestartReport{Id: p.ID()}
errs, err := p.Restart(ctx)
- if err != nil && errors.Cause(err) != define.ErrPodPartialFail {
+ if err != nil && !errors.Is(err, define.ErrPodPartialFail) {
report.Errs = []error{err}
reports = append(reports, &report)
continue
}
if len(errs) > 0 {
for id, v := range errs {
- report.Errs = append(report.Errs, errors.Wrapf(v, "error restarting container %s", id))
+ report.Errs = append(report.Errs, fmt.Errorf("error restarting container %s: %w", id, v))
}
reports = append(reports, &report)
continue
@@ -237,14 +246,14 @@ func (ic *ContainerEngine) PodStart(ctx context.Context, namesOrIds []string, op
for _, p := range pods {
report := entities.PodStartReport{Id: p.ID()}
errs, err := p.Start(ctx)
- if err != nil && errors.Cause(err) != define.ErrPodPartialFail {
+ if err != nil && !errors.Is(err, define.ErrPodPartialFail) {
report.Errs = []error{err}
reports = append(reports, &report)
continue
}
if len(errs) > 0 {
for id, v := range errs {
- report.Errs = append(report.Errs, errors.Wrapf(v, "error starting container %s", id))
+ report.Errs = append(report.Errs, fmt.Errorf("error starting container %s: %w", id, v))
}
reports = append(reports, &report)
continue
@@ -256,7 +265,7 @@ func (ic *ContainerEngine) PodStart(ctx context.Context, namesOrIds []string, op
func (ic *ContainerEngine) PodRm(ctx context.Context, namesOrIds []string, options entities.PodRmOptions) ([]*entities.PodRmReport, error) {
pods, err := getPodsByContext(options.All, options.Latest, namesOrIds, ic.Libpod)
- if err != nil && !(options.Ignore && errors.Cause(err) == define.ErrNoSuchPod) {
+ if err != nil && !(options.Ignore && errors.Is(err, define.ErrNoSuchPod)) {
return nil, err
}
reports := make([]*entities.PodRmReport, 0, len(pods))
@@ -393,7 +402,7 @@ func (ic *ContainerEngine) PodTop(ctx context.Context, options entities.PodTopOp
pod, err = ic.Libpod.LookupPod(options.NameOrID)
}
if err != nil {
- return nil, errors.Wrap(err, "unable to lookup requested container")
+ return nil, fmt.Errorf("unable to look up requested container: %w", err)
}
// Run Top.
@@ -402,6 +411,56 @@ func (ic *ContainerEngine) PodTop(ctx context.Context, options entities.PodTopOp
return report, err
}
+func (ic *ContainerEngine) listPodReportFromPod(p *libpod.Pod) (*entities.ListPodsReport, error) {
+ status, err := p.GetPodStatus()
+ if err != nil {
+ return nil, err
+ }
+ cons, err := p.AllContainers()
+ if err != nil {
+ return nil, err
+ }
+ lpcs := make([]*entities.ListPodContainer, len(cons))
+ for i, c := range cons {
+ state, err := c.State()
+ if err != nil {
+ return nil, err
+ }
+ lpcs[i] = &entities.ListPodContainer{
+ Id: c.ID(),
+ Names: c.Name(),
+ Status: state.String(),
+ }
+ }
+ infraID, err := p.InfraContainerID()
+ if err != nil {
+ return nil, err
+ }
+ networks := []string{}
+ if len(infraID) > 0 {
+ infra, err := p.InfraContainer()
+ if err != nil {
+ return nil, err
+ }
+ networks, err = infra.Networks()
+ if err != nil {
+ return nil, err
+ }
+ }
+ return &entities.ListPodsReport{
+ Cgroup: p.CgroupParent(),
+ Containers: lpcs,
+ Created: p.CreatedTime(),
+ Id: p.ID(),
+ InfraId: infraID,
+ Name: p.Name(),
+ Namespace: p.Namespace(),
+ Networks: networks,
+ Status: status,
+ Labels: p.Labels(),
+ }, nil
+}
+
func (ic *ContainerEngine) PodPs(ctx context.Context, options entities.PodPSOptions) ([]*entities.ListPodsReport, error) {
var (
err error
@@ -431,53 +490,14 @@ func (ic *ContainerEngine) PodPs(ctx context.Context, options entities.PodPSOpti
reports := make([]*entities.ListPodsReport, 0, len(pds))
for _, p := range pds {
- var lpcs []*entities.ListPodContainer
- status, err := p.GetPodStatus()
- if err != nil {
- return nil, err
- }
- cons, err := p.AllContainers()
+ r, err := ic.listPodReportFromPod(p)
if err != nil {
- return nil, err
- }
- for _, c := range cons {
- state, err := c.State()
- if err != nil {
- return nil, err
+ if errors.Is(err, define.ErrNoSuchPod) || errors.Is(err, define.ErrNoSuchCtr) {
+ continue
}
- lpcs = append(lpcs, &entities.ListPodContainer{
- Id: c.ID(),
- Names: c.Name(),
- Status: state.String(),
- })
- }
- infraID, err := p.InfraContainerID()
- if err != nil {
return nil, err
}
- networks := []string{}
- if len(infraID) > 0 {
- infra, err := p.InfraContainer()
- if err != nil {
- return nil, err
- }
- networks, err = infra.Networks()
- if err != nil {
- return nil, err
- }
- }
- reports = append(reports, &entities.ListPodsReport{
- Cgroup: p.CgroupParent(),
- Containers: lpcs,
- Created: p.CreatedTime(),
- Id: p.ID(),
- InfraId: infraID,
- Name: p.Name(),
- Namespace: p.Namespace(),
- Networks: networks,
- Status: status,
- Labels: p.Labels(),
- })
+ reports = append(reports, r)
}
return reports, nil
}
@@ -494,7 +514,7 @@ func (ic *ContainerEngine) PodInspect(ctx context.Context, options entities.PodI
pod, err = ic.Libpod.LookupPod(options.NameOrID)
}
if err != nil {
- return nil, errors.Wrap(err, "unable to lookup requested container")
+ return nil, fmt.Errorf("unable to look up requested container: %w", err)
}
inspect, err := pod.Inspect()
if err != nil {
diff --git a/pkg/domain/infra/abi/secrets.go b/pkg/domain/infra/abi/secrets.go
index b9380ad73..7321ef715 100644
--- a/pkg/domain/infra/abi/secrets.go
+++ b/pkg/domain/infra/abi/secrets.go
@@ -2,13 +2,14 @@ package abi
import (
"context"
+ "fmt"
"io"
"io/ioutil"
"path/filepath"
+ "strings"
"github.com/containers/podman/v4/pkg/domain/entities"
"github.com/containers/podman/v4/pkg/domain/utils"
- "github.com/pkg/errors"
)
func (ic *ContainerEngine) SecretCreate(ctx context.Context, name string, reader io.Reader, options entities.SecretCreateOptions) (*entities.SecretCreateReport, error) {
@@ -60,11 +61,11 @@ func (ic *ContainerEngine) SecretInspect(ctx context.Context, nameOrIDs []string
for _, nameOrID := range nameOrIDs {
secret, err := manager.Lookup(nameOrID)
if err != nil {
- if errors.Cause(err).Error() == "no such secret" {
+ if strings.Contains(err.Error(), "no such secret") {
errs = append(errs, err)
continue
} else {
- return nil, nil, errors.Wrapf(err, "error inspecting secret %s", nameOrID)
+ return nil, nil, fmt.Errorf("error inspecting secret %s: %w", nameOrID, err)
}
}
report := &entities.SecretInfoReport{
@@ -141,7 +142,7 @@ func (ic *ContainerEngine) SecretRm(ctx context.Context, nameOrIDs []string, opt
}
for _, nameOrID := range toRemove {
deletedID, err := manager.Delete(nameOrID)
- if err == nil || errors.Cause(err).Error() == "no such secret" {
+ if err == nil || strings.Contains(err.Error(), "no such secret") {
reports = append(reports, &entities.SecretRmReport{
Err: err,
ID: deletedID,
diff --git a/pkg/domain/infra/abi/system.go b/pkg/domain/infra/abi/system.go
index 820ba529b..96690afef 100644
--- a/pkg/domain/infra/abi/system.go
+++ b/pkg/domain/infra/abi/system.go
@@ -2,6 +2,7 @@ package abi
import (
"context"
+ "errors"
"fmt"
"net/url"
"os"
@@ -19,7 +20,6 @@ import (
"github.com/containers/podman/v4/utils"
"github.com/containers/storage"
"github.com/containers/storage/pkg/unshare"
- "github.com/pkg/errors"
"github.com/sirupsen/logrus"
"github.com/spf13/pflag"
)
@@ -99,7 +99,7 @@ func (ic *ContainerEngine) SetupRootless(_ context.Context, noMoveProcess bool)
}
pausePidPath, err := util.GetRootlessPauseProcessPidPathGivenDir(tmpDir)
if err != nil {
- return errors.Wrapf(err, "could not get pause process pid file path")
+ return fmt.Errorf("could not get pause process pid file path: %w", err)
}
became, ret, err := rootless.TryJoinPauseProcess(pausePidPath)
@@ -125,10 +125,16 @@ func (ic *ContainerEngine) SetupRootless(_ context.Context, noMoveProcess bool)
paths = append(paths, ctr.Config().ConmonPidFile)
}
- became, ret, err = rootless.TryJoinFromFilePaths(pausePidPath, true, paths)
- utils.MovePauseProcessToScope(pausePidPath)
+ if len(paths) > 0 {
+ became, ret, err = rootless.TryJoinFromFilePaths(pausePidPath, true, paths)
+ } else {
+ became, ret, err = rootless.BecomeRootInUserNS(pausePidPath)
+ if err == nil {
+ utils.MovePauseProcessToScope(pausePidPath)
+ }
+ }
if err != nil {
- logrus.Error(errors.Wrapf(err, "invalid internal status, try resetting the pause process with %q", os.Args[0]+" system migrate"))
+ logrus.Error(fmt.Errorf("invalid internal status, try resetting the pause process with %q: %w", os.Args[0]+" system migrate", err))
os.Exit(1)
}
if became {
@@ -265,22 +271,22 @@ func (ic *ContainerEngine) SystemDf(ctx context.Context, options entities.System
iid, _ := c.Image()
state, err := c.State()
if err != nil {
- return nil, errors.Wrapf(err, "Failed to get state of container %s", c.ID())
+ return nil, fmt.Errorf("failed to get state of container %s: %w", c.ID(), err)
}
conSize, err := c.RootFsSize()
if err != nil {
- if errors.Cause(err) == storage.ErrContainerUnknown {
- logrus.Error(errors.Wrapf(err, "Failed to get root file system size of container %s", c.ID()))
+ if errors.Is(err, storage.ErrContainerUnknown) {
+ logrus.Error(fmt.Errorf("failed to get root file system size of container %s: %w", c.ID(), err))
} else {
- return nil, errors.Wrapf(err, "Failed to get root file system size of container %s", c.ID())
+ return nil, fmt.Errorf("failed to get root file system size of container %s: %w", c.ID(), err)
}
}
rwsize, err := c.RWSize()
if err != nil {
- if errors.Cause(err) == storage.ErrContainerUnknown {
- logrus.Error(errors.Wrapf(err, "Failed to get read/write size of container %s", c.ID()))
+ if errors.Is(err, storage.ErrContainerUnknown) {
+ logrus.Error(fmt.Errorf("failed to get read/write size of container %s: %w", c.ID(), err))
} else {
- return nil, errors.Wrapf(err, "Failed to get read/write size of container %s", c.ID())
+ return nil, fmt.Errorf("failed to get read/write size of container %s: %w", c.ID(), err)
}
}
report := entities.SystemDfContainerReport{
@@ -313,8 +319,8 @@ func (ic *ContainerEngine) SystemDf(ctx context.Context, options entities.System
}
dfVolumes := make([]*entities.SystemDfVolumeReport, 0, len(vols))
- var reclaimableSize uint64
for _, v := range vols {
+ var reclaimableSize uint64
var consInUse int
mountPoint, err := v.MountPoint()
if err != nil {
@@ -335,7 +341,7 @@ func (ic *ContainerEngine) SystemDf(ctx context.Context, options entities.System
return nil, err
}
if len(inUse) == 0 {
- reclaimableSize += volSize
+ reclaimableSize = volSize
}
for _, viu := range inUse {
if cutil.StringInSlice(viu, runningContainers) {
@@ -398,9 +404,9 @@ func (ic *ContainerEngine) Unshare(ctx context.Context, args []string, options e
}
// Make sure to unlock, unshare can run for a long time.
rootlessNetNS.Lock.Unlock()
- // We do not want to cleanup the netns after unshare.
- // The problem is that we cannot know if we need to cleanup and
- // secondly unshare should allow user to setup the namespace with
+ // We do not want to clean up the netns after unshare.
+ // The problem is that we cannot know if we need to clean up and
+ // secondly unshare should allow user to set up the namespace with
// special things, e.g. potentially macvlan or something like that.
return rootlessNetNS.Do(unshare)
}
diff --git a/pkg/domain/infra/abi/terminal/sigproxy_linux.go b/pkg/domain/infra/abi/terminal/sigproxy_linux.go
index e02c0532c..16d345f06 100644
--- a/pkg/domain/infra/abi/terminal/sigproxy_linux.go
+++ b/pkg/domain/infra/abi/terminal/sigproxy_linux.go
@@ -1,6 +1,7 @@
package terminal
import (
+ "errors"
"os"
"syscall"
@@ -8,7 +9,6 @@ import (
"github.com/containers/podman/v4/libpod/define"
"github.com/containers/podman/v4/libpod/shutdown"
"github.com/containers/podman/v4/pkg/signal"
- "github.com/pkg/errors"
"github.com/sirupsen/logrus"
)
@@ -39,7 +39,7 @@ func ProxySignals(ctr *libpod.Container) {
}
if err := ctr.Kill(uint(s.(syscall.Signal))); err != nil {
- if errors.Cause(err) == define.ErrCtrStateInvalid {
+ if errors.Is(err, define.ErrCtrStateInvalid) {
logrus.Infof("Ceasing signal forwarding to container %s as it has stopped", ctr.ID())
} else {
logrus.Errorf("forwarding signal %d to container %s: %v", s, ctr.ID(), err)
diff --git a/pkg/domain/infra/abi/terminal/terminal.go b/pkg/domain/infra/abi/terminal/terminal.go
index 692f8dcd5..45ebded26 100644
--- a/pkg/domain/infra/abi/terminal/terminal.go
+++ b/pkg/domain/infra/abi/terminal/terminal.go
@@ -5,7 +5,7 @@ import (
"os"
"os/signal"
- "github.com/containers/podman/v4/libpod/define"
+ "github.com/containers/common/pkg/resize"
lsignal "github.com/containers/podman/v4/pkg/signal"
"github.com/moby/term"
"github.com/pkg/errors"
@@ -18,20 +18,20 @@ type RawTtyFormatter struct {
// getResize returns a TerminalSize command matching stdin's current
// size on success, and nil on errors.
-func getResize() *define.TerminalSize {
+func getResize() *resize.TerminalSize {
winsize, err := term.GetWinsize(os.Stdin.Fd())
if err != nil {
logrus.Warnf("Could not get terminal size %v", err)
return nil
}
- return &define.TerminalSize{
+ return &resize.TerminalSize{
Width: winsize.Width,
Height: winsize.Height,
}
}
// Helper for prepareAttach - set up a goroutine to generate terminal resize events
-func resizeTty(ctx context.Context, resize chan define.TerminalSize) {
+func resizeTty(ctx context.Context, resize chan resize.TerminalSize) {
sigchan := make(chan os.Signal, 1)
signal.Notify(sigchan, lsignal.SIGWINCH)
go func() {
@@ -78,7 +78,7 @@ func (f *RawTtyFormatter) Format(entry *logrus.Entry) ([]byte, error) {
return bytes, err
}
-func handleTerminalAttach(ctx context.Context, resize chan define.TerminalSize) (context.CancelFunc, *term.State, error) {
+func handleTerminalAttach(ctx context.Context, resize chan resize.TerminalSize) (context.CancelFunc, *term.State, error) {
logrus.Debugf("Handling terminal attach")
subCtx, cancel := context.WithCancel(ctx)
diff --git a/pkg/domain/infra/abi/terminal/terminal_linux.go b/pkg/domain/infra/abi/terminal/terminal_linux.go
index 62d36f28d..e8f338418 100644
--- a/pkg/domain/infra/abi/terminal/terminal_linux.go
+++ b/pkg/domain/infra/abi/terminal/terminal_linux.go
@@ -6,6 +6,7 @@ import (
"fmt"
"os"
+ "github.com/containers/common/pkg/resize"
"github.com/containers/podman/v4/libpod"
"github.com/containers/podman/v4/libpod/define"
"github.com/pkg/errors"
@@ -15,14 +16,14 @@ import (
// ExecAttachCtr execs and attaches to a container
func ExecAttachCtr(ctx context.Context, ctr *libpod.Container, execConfig *libpod.ExecConfig, streams *define.AttachStreams) (int, error) {
- var resize chan define.TerminalSize
+ var resizechan chan resize.TerminalSize
haveTerminal := term.IsTerminal(int(os.Stdin.Fd()))
// Check if we are attached to a terminal. If we are, generate resize
// events, and set the terminal to raw mode
if haveTerminal && execConfig.Terminal {
- resize = make(chan define.TerminalSize)
- cancel, oldTermState, err := handleTerminalAttach(ctx, resize)
+ resizechan = make(chan resize.TerminalSize)
+ cancel, oldTermState, err := handleTerminalAttach(ctx, resizechan)
if err != nil {
return -1, err
}
@@ -33,14 +34,14 @@ func ExecAttachCtr(ctx context.Context, ctr *libpod.Container, execConfig *libpo
}
}()
}
- return ctr.Exec(execConfig, streams, resize)
+ return ctr.Exec(execConfig, streams, resizechan)
}
// StartAttachCtr starts and (if required) attaches to a container
// if you change the signature of this function from os.File to io.Writer, it will trigger a downstream
// error. we may need to just lint disable this one.
func StartAttachCtr(ctx context.Context, ctr *libpod.Container, stdout, stderr, stdin *os.File, detachKeys string, sigProxy bool, startContainer bool) error { //nolint: interfacer
- resize := make(chan define.TerminalSize)
+ resize := make(chan resize.TerminalSize)
haveTerminal := term.IsTerminal(int(os.Stdin.Fd()))
diff --git a/pkg/domain/infra/abi/volumes.go b/pkg/domain/infra/abi/volumes.go
index a9c53c140..5e95a0551 100644
--- a/pkg/domain/infra/abi/volumes.go
+++ b/pkg/domain/infra/abi/volumes.go
@@ -2,6 +2,8 @@ package abi
import (
"context"
+ "errors"
+ "fmt"
"github.com/containers/podman/v4/libpod"
"github.com/containers/podman/v4/libpod/define"
@@ -9,7 +11,6 @@ import (
"github.com/containers/podman/v4/pkg/domain/entities/reports"
"github.com/containers/podman/v4/pkg/domain/filters"
"github.com/containers/podman/v4/pkg/domain/infra/abi/parse"
- "github.com/pkg/errors"
)
func (ic *ContainerEngine) VolumeCreate(ctx context.Context, opts entities.VolumeCreateOptions) (*entities.IDOrNameResponse, error) {
@@ -91,11 +92,11 @@ func (ic *ContainerEngine) VolumeInspect(ctx context.Context, namesOrIds []strin
for _, v := range namesOrIds {
vol, err := ic.Libpod.LookupVolume(v)
if err != nil {
- if errors.Cause(err) == define.ErrNoSuchVolume {
- errs = append(errs, errors.Errorf("no such volume %s", v))
+ if errors.Is(err, define.ErrNoSuchVolume) {
+ errs = append(errs, fmt.Errorf("no such volume %s", v))
continue
} else {
- return nil, nil, errors.Wrapf(err, "error inspecting volume %s", v)
+ return nil, nil, fmt.Errorf("error inspecting volume %s: %w", v, err)
}
}
vols = append(vols, vol)
@@ -211,3 +212,8 @@ func (ic *ContainerEngine) VolumeUnmount(ctx context.Context, nameOrIDs []string
return reports, nil
}
+
+func (ic *ContainerEngine) VolumeReload(ctx context.Context) (*entities.VolumeReloadReport, error) {
+ report := ic.Libpod.UpdateVolumePlugins(ctx)
+ return &entities.VolumeReloadReport{VolumeReload: *report}, nil
+}
diff --git a/pkg/domain/infra/runtime_libpod.go b/pkg/domain/infra/runtime_libpod.go
index 03e7ffb5d..162025969 100644
--- a/pkg/domain/infra/runtime_libpod.go
+++ b/pkg/domain/infra/runtime_libpod.go
@@ -342,7 +342,7 @@ func ParseIDMapping(mode namespaces.UsernsMode, uidMapSlice, gidMapSlice []strin
options.HostUIDMapping = false
options.HostGIDMapping = false
- // Simply ignore the setting and do not setup an inner namespace for root as it is a no-op
+ // Simply ignore the setting and do not set up an inner namespace for root as it is a no-op
return &options, nil
}
@@ -394,7 +394,7 @@ func ParseIDMapping(mode namespaces.UsernsMode, uidMapSlice, gidMapSlice []strin
// StartWatcher starts a new SIGHUP go routine for the current config.
func StartWatcher(rt *libpod.Runtime) {
- // Setup the signal notifier
+ // Set up the signal notifier
ch := make(chan os.Signal, 1)
signal.Notify(ch, syscall.SIGHUP)
diff --git a/pkg/domain/infra/tunnel/containers.go b/pkg/domain/infra/tunnel/containers.go
index fb0be629c..5568ccde8 100644
--- a/pkg/domain/infra/tunnel/containers.go
+++ b/pkg/domain/infra/tunnel/containers.go
@@ -2,6 +2,7 @@ package tunnel
import (
"context"
+ "errors"
"fmt"
"io"
"os"
@@ -23,7 +24,6 @@ import (
"github.com/containers/podman/v4/pkg/specgen"
"github.com/containers/podman/v4/pkg/util"
"github.com/containers/storage/types"
- "github.com/pkg/errors"
"github.com/sirupsen/logrus"
)
@@ -64,7 +64,7 @@ func (ic *ContainerEngine) ContainerPause(ctx context.Context, namesOrIds []stri
reports := make([]*entities.PauseUnpauseReport, 0, len(ctrs))
for _, c := range ctrs {
err := containers.Pause(ic.ClientCtx, c.ID, nil)
- if err != nil && options.All && errors.Cause(err).Error() == define.ErrCtrStateInvalid.Error() {
+ if err != nil && options.All && strings.Contains(err.Error(), define.ErrCtrStateInvalid.Error()) {
logrus.Debugf("Container %s is not running", c.ID)
continue
}
@@ -81,7 +81,7 @@ func (ic *ContainerEngine) ContainerUnpause(ctx context.Context, namesOrIds []st
}
for _, c := range ctrs {
err := containers.Unpause(ic.ClientCtx, c.ID, nil)
- if err != nil && options.All && errors.Cause(err).Error() == define.ErrCtrStateInvalid.Error() {
+ if err != nil && options.All && strings.Contains(err.Error(), define.ErrCtrStateInvalid.Error()) {
logrus.Debugf("Container %s is not paused", c.ID)
continue
}
@@ -111,11 +111,11 @@ func (ic *ContainerEngine) ContainerStop(ctx context.Context, namesOrIds []strin
}
if err = containers.Stop(ic.ClientCtx, c.ID, options); err != nil {
// These first two are considered non-fatal under the right conditions
- if errors.Cause(err).Error() == define.ErrCtrStopped.Error() {
+ if strings.Contains(err.Error(), define.ErrCtrStopped.Error()) {
logrus.Debugf("Container %s is already stopped", c.ID)
reports = append(reports, &report)
continue
- } else if opts.All && errors.Cause(err).Error() == define.ErrCtrStateInvalid.Error() {
+ } else if opts.All && strings.Contains(err.Error(), define.ErrCtrStateInvalid.Error()) {
logrus.Debugf("Container %s is not running, could not stop", c.ID)
reports = append(reports, &report)
continue
@@ -146,7 +146,7 @@ func (ic *ContainerEngine) ContainerKill(ctx context.Context, namesOrIds []strin
reports := make([]*entities.KillReport, 0, len(ctrs))
for _, c := range ctrs {
err := containers.Kill(ic.ClientCtx, c.ID, options)
- if err != nil && opts.All && errors.Cause(err).Error() == define.ErrCtrStateInvalid.Error() {
+ if err != nil && opts.All && strings.Contains(err.Error(), define.ErrCtrStateInvalid.Error()) {
logrus.Debugf("Container %s is not running", c.ID)
continue
}
@@ -258,7 +258,7 @@ func (ic *ContainerEngine) ContainerInspect(ctx context.Context, namesOrIds []st
return nil, nil, err
}
if errModel.ResponseCode == 404 {
- errs = append(errs, errors.Errorf("no such container %q", name))
+ errs = append(errs, fmt.Errorf("no such container %q", name))
continue
}
return nil, nil, err
@@ -291,7 +291,7 @@ func (ic *ContainerEngine) ContainerCommit(ctx context.Context, nameOrID string,
if len(opts.ImageName) > 0 {
ref, err := reference.Parse(opts.ImageName)
if err != nil {
- return nil, errors.Wrapf(err, "error parsing reference %q", opts.ImageName)
+ return nil, fmt.Errorf("error parsing reference %q: %w", opts.ImageName, err)
}
if t, ok := ref.(reference.Tagged); ok {
tag = t.Tag()
@@ -300,7 +300,7 @@ func (ic *ContainerEngine) ContainerCommit(ctx context.Context, nameOrID string,
repo = r.Name()
}
if len(repo) < 1 {
- return nil, errors.Errorf("invalid image name %q", opts.ImageName)
+ return nil, fmt.Errorf("invalid image name %q", opts.ImageName)
}
}
options := new(containers.CommitOptions).WithAuthor(opts.Author).WithChanges(opts.Changes).WithComment(opts.Message).WithSquash(opts.Squash)
@@ -502,7 +502,7 @@ func (ic *ContainerEngine) ContainerAttach(ctx context.Context, nameOrID string,
}
ctr := ctrs[0]
if ctr.State != define.ContainerStateRunning.String() {
- return errors.Errorf("you can only attach to running containers")
+ return fmt.Errorf("you can only attach to running containers")
}
options := new(containers.AttachOptions).WithStream(true).WithDetachKeys(opts.DetachKeys)
return containers.Attach(ic.ClientCtx, nameOrID, opts.Stdin, opts.Stdout, opts.Stderr, nil, options)
@@ -695,7 +695,7 @@ func (ic *ContainerEngine) ContainerStart(ctx context.Context, namesOrIds []stri
report.ExitCode = define.ExitCode(report.Err)
report.Err = err
reports = append(reports, &report)
- return reports, errors.Wrapf(report.Err, "unable to start container %s", name)
+ return reports, fmt.Errorf("unable to start container %s: %w", name, report.Err)
}
if ctr.AutoRemove {
// Defer the removal, so we can return early if needed and
@@ -739,7 +739,7 @@ func (ic *ContainerEngine) ContainerStart(ctx context.Context, namesOrIds []stri
reports, err := containers.Remove(ic.ClientCtx, ctr.ID, rmOptions)
logIfRmError(ctr.ID, err, reports)
}
- report.Err = errors.Wrapf(err, "unable to start container %q", name)
+ report.Err = fmt.Errorf("unable to start container %q: %w", name, err)
report.ExitCode = define.ExitCode(err)
reports = append(reports, &report)
continue
@@ -899,7 +899,7 @@ func (ic *ContainerEngine) ContainerInit(ctx context.Context, namesOrIds []strin
err := containers.ContainerInit(ic.ClientCtx, ctr.ID, nil)
// When using all, it is NOT considered an error if a container
// has already been init'd.
- if err != nil && options.All && strings.Contains(errors.Cause(err).Error(), define.ErrCtrStateInvalid.Error()) {
+ if err != nil && options.All && strings.Contains(err.Error(), define.ErrCtrStateInvalid.Error()) {
err = nil
}
reports = append(reports, &entities.ContainerInitReport{
diff --git a/pkg/domain/infra/tunnel/helpers.go b/pkg/domain/infra/tunnel/helpers.go
index 5b14fac37..6c043465c 100644
--- a/pkg/domain/infra/tunnel/helpers.go
+++ b/pkg/domain/infra/tunnel/helpers.go
@@ -20,7 +20,7 @@ func getContainersByContext(contextWithConnection context.Context, all, ignore b
func getContainersAndInputByContext(contextWithConnection context.Context, all, ignore bool, namesOrIDs []string) ([]entities.ListContainer, []string, error) {
if all && len(namesOrIDs) > 0 {
- return nil, nil, errors.New("cannot lookup containers and all")
+ return nil, nil, errors.New("cannot look up containers and all")
}
options := new(containers.ListOptions).WithAll(true).WithSync(true)
allContainers, err := containers.List(contextWithConnection, options)
@@ -77,7 +77,7 @@ func getContainersAndInputByContext(contextWithConnection context.Context, all,
func getPodsByContext(contextWithConnection context.Context, all bool, namesOrIDs []string) ([]*entities.ListPodsReport, error) {
if all && len(namesOrIDs) > 0 {
- return nil, errors.New("cannot lookup specific pods and all")
+ return nil, errors.New("cannot look up specific pods and all")
}
allPods, err := pods.List(contextWithConnection, nil)
diff --git a/pkg/domain/infra/tunnel/images.go b/pkg/domain/infra/tunnel/images.go
index 97838d596..09f8ac4c3 100644
--- a/pkg/domain/infra/tunnel/images.go
+++ b/pkg/domain/infra/tunnel/images.go
@@ -2,6 +2,7 @@ package tunnel
import (
"context"
+ "fmt"
"io/ioutil"
"os"
"strconv"
@@ -12,7 +13,6 @@ import (
"github.com/containers/common/pkg/config"
"github.com/containers/image/v5/docker/reference"
"github.com/containers/image/v5/types"
- "github.com/containers/podman/v4/libpod/define"
"github.com/containers/podman/v4/pkg/bindings/images"
"github.com/containers/podman/v4/pkg/domain/entities"
"github.com/containers/podman/v4/pkg/domain/entities/reports"
@@ -28,7 +28,7 @@ func (ir *ImageEngine) Exists(_ context.Context, nameOrID string) (*entities.Boo
}
func (ir *ImageEngine) Remove(ctx context.Context, imagesArg []string, opts entities.ImageRemoveOptions) (*entities.ImageRemoveReport, []error) {
- options := new(images.RemoveOptions).WithForce(opts.Force).WithIgnore(opts.Ignore).WithAll(opts.All)
+ options := new(images.RemoveOptions).WithForce(opts.Force).WithIgnore(opts.Ignore).WithAll(opts.All).WithLookupManifest(opts.LookupManifest)
return images.Remove(ir.ClientCtx, imagesArg, options)
}
@@ -123,10 +123,6 @@ func (ir *ImageEngine) Pull(ctx context.Context, rawImage string, opts entities.
return &entities.ImagePullReport{Images: pulledImages}, nil
}
-func (ir *ImageEngine) Transfer(ctx context.Context, source entities.ImageScpOptions, dest entities.ImageScpOptions, parentFlags []string) error {
- return errors.Wrapf(define.ErrNotImplemented, "cannot use the remote client to transfer images between root and rootless storage")
-}
-
func (ir *ImageEngine) Tag(ctx context.Context, nameOrID string, tags []string, opt entities.ImageTagOptions) error {
options := new(images.TagOptions)
for _, newTag := range tags {
@@ -367,3 +363,23 @@ func (ir *ImageEngine) Shutdown(_ context.Context) {
func (ir *ImageEngine) Sign(ctx context.Context, names []string, options entities.SignOptions) (*entities.SignReport, error) {
return nil, errors.New("not implemented yet")
}
+
+func (ir *ImageEngine) Scp(ctx context.Context, src, dst string, parentFlags []string, quiet bool) error {
+ options := new(images.ScpOptions)
+
+ var destination *string
+ if len(dst) > 1 {
+ destination = &dst
+ }
+ options.Quiet = &quiet
+ options.Destination = destination
+
+ rep, err := images.Scp(ir.ClientCtx, &src, destination, *options)
+ if err != nil {
+ return err
+ }
+
+ fmt.Println("Loaded Image(s):", rep.Id)
+
+ return nil
+}
diff --git a/pkg/domain/infra/tunnel/pods.go b/pkg/domain/infra/tunnel/pods.go
index 7b1fa231f..bcbd32d1b 100644
--- a/pkg/domain/infra/tunnel/pods.go
+++ b/pkg/domain/infra/tunnel/pods.go
@@ -2,12 +2,12 @@ package tunnel
import (
"context"
+ "errors"
"github.com/containers/podman/v4/libpod/define"
"github.com/containers/podman/v4/pkg/bindings/pods"
"github.com/containers/podman/v4/pkg/domain/entities"
"github.com/containers/podman/v4/pkg/util"
- "github.com/pkg/errors"
)
func (ic *ContainerEngine) PodExists(ctx context.Context, nameOrID string) (*entities.BoolReport, error) {
@@ -80,6 +80,10 @@ func (ic *ContainerEngine) PodUnpause(ctx context.Context, namesOrIds []string,
}
reports := make([]*entities.PodUnpauseReport, 0, len(foundPods))
for _, p := range foundPods {
+ // If the pod is not paused or degraded, there is no need to attempt an unpause on it
+ if p.Status != define.PodStatePaused && p.Status != define.PodStateDegraded {
+ continue
+ }
response, err := pods.Unpause(ic.ClientCtx, p.Id, nil)
if err != nil {
report := entities.PodUnpauseReport{
@@ -97,7 +101,7 @@ func (ic *ContainerEngine) PodUnpause(ctx context.Context, namesOrIds []string,
func (ic *ContainerEngine) PodStop(ctx context.Context, namesOrIds []string, opts entities.PodStopOptions) ([]*entities.PodStopReport, error) {
timeout := -1
foundPods, err := getPodsByContext(ic.ClientCtx, opts.All, namesOrIds)
- if err != nil && !(opts.Ignore && errors.Cause(err) == define.ErrNoSuchPod) {
+ if err != nil && !(opts.Ignore && errors.Is(err, define.ErrNoSuchPod)) {
return nil, err
}
if opts.Timeout != -1 {
@@ -164,7 +168,7 @@ func (ic *ContainerEngine) PodStart(ctx context.Context, namesOrIds []string, op
func (ic *ContainerEngine) PodRm(ctx context.Context, namesOrIds []string, opts entities.PodRmOptions) ([]*entities.PodRmReport, error) {
foundPods, err := getPodsByContext(ic.ClientCtx, opts.All, namesOrIds)
- if err != nil && !(opts.Ignore && errors.Cause(err) == define.ErrNoSuchPod) {
+ if err != nil && !(opts.Ignore && errors.Is(err, define.ErrNoSuchPod)) {
return nil, err
}
reports := make([]*entities.PodRmReport, 0, len(foundPods))
diff --git a/pkg/domain/infra/tunnel/volumes.go b/pkg/domain/infra/tunnel/volumes.go
index 33e090148..6ec35e836 100644
--- a/pkg/domain/infra/tunnel/volumes.go
+++ b/pkg/domain/infra/tunnel/volumes.go
@@ -108,3 +108,7 @@ func (ic *ContainerEngine) VolumeMount(ctx context.Context, nameOrIDs []string)
func (ic *ContainerEngine) VolumeUnmount(ctx context.Context, nameOrIDs []string) ([]*entities.VolumeUnmountReport, error) {
return nil, errors.New("unmounting volumes is not supported for remote clients")
}
+
+func (ic *ContainerEngine) VolumeReload(ctx context.Context) (*entities.VolumeReloadReport, error) {
+ return nil, errors.New("volume reload is not supported for remote clients")
+}
diff --git a/pkg/domain/utils/scp.go b/pkg/domain/utils/scp.go
new file mode 100644
index 000000000..a4ff6b950
--- /dev/null
+++ b/pkg/domain/utils/scp.go
@@ -0,0 +1,581 @@
+package utils
+
+import (
+ "bytes"
+ "fmt"
+ "io/ioutil"
+ "net"
+ "net/url"
+ "os"
+ "os/exec"
+ "os/user"
+ "strconv"
+ "strings"
+ "time"
+
+ scpD "github.com/dtylman/scp"
+
+ "github.com/containers/common/pkg/config"
+ "github.com/containers/podman/v4/libpod/define"
+ "github.com/containers/podman/v4/pkg/domain/entities"
+ "github.com/containers/podman/v4/pkg/terminal"
+ "github.com/docker/distribution/reference"
+ "github.com/pkg/errors"
+ "github.com/sirupsen/logrus"
+ "golang.org/x/crypto/ssh"
+ "golang.org/x/crypto/ssh/agent"
+)
+
+func ExecuteTransfer(src, dst string, parentFlags []string, quiet bool) (*entities.ImageLoadReport, *entities.ImageScpOptions, *entities.ImageScpOptions, []string, error) {
+ source := entities.ImageScpOptions{}
+ dest := entities.ImageScpOptions{}
+ sshInfo := entities.ImageScpConnections{}
+ report := entities.ImageLoadReport{Names: []string{}}
+
+ podman, err := os.Executable()
+ if err != nil {
+ return nil, nil, nil, nil, err
+ }
+
+ f, err := ioutil.TempFile("", "podman") // open temp file for load/save output
+ if err != nil {
+ return nil, nil, nil, nil, err
+ }
+
+ confR, err := config.NewConfig("") // create a hand made config for the remote engine since we might use remote and native at once
+ if err != nil {
+ return nil, nil, nil, nil, errors.Wrapf(err, "could not make config")
+ }
+
+ cfg, err := config.ReadCustomConfig() // get ready to set ssh destination if necessary
+ if err != nil {
+ return nil, nil, nil, nil, err
+ }
+ locations := []*entities.ImageScpOptions{}
+ cliConnections := []string{}
+ args := []string{src}
+ if len(dst) > 0 {
+ args = append(args, dst)
+ }
+ for _, arg := range args {
+ loc, connect, err := ParseImageSCPArg(arg)
+ if err != nil {
+ return nil, nil, nil, nil, err
+ }
+ locations = append(locations, loc)
+ cliConnections = append(cliConnections, connect...)
+ }
+ source = *locations[0]
+ switch {
+ case len(locations) > 1:
+ if err = ValidateSCPArgs(locations); err != nil {
+ return nil, nil, nil, nil, err
+ }
+ dest = *locations[1]
+ case len(locations) == 1:
+ switch {
+ case len(locations[0].Image) == 0:
+ return nil, nil, nil, nil, errors.Wrapf(define.ErrInvalidArg, "no source image specified")
+ case len(locations[0].Image) > 0 && !locations[0].Remote && len(locations[0].User) == 0: // if we have podman image scp $IMAGE
+ return nil, nil, nil, nil, errors.Wrapf(define.ErrInvalidArg, "must specify a destination")
+ }
+ }
+
+ source.Quiet = quiet
+ source.File = f.Name() // after parsing the arguments, set the file for the save/load
+ dest.File = source.File
+ if err = os.Remove(source.File); err != nil { // remove the file and simply use its name so podman creates the file upon save. avoids umask errors
+ return nil, nil, nil, nil, err
+ }
+
+ allLocal := true // if we are all localhost, do not validate connections but if we are using one localhost and one non we need to use sshd
+ for _, val := range cliConnections {
+ if !strings.Contains(val, "@localhost::") {
+ allLocal = false
+ break
+ }
+ }
+ if allLocal {
+ cliConnections = []string{}
+ }
+
+ var serv map[string]config.Destination
+ serv, err = GetServiceInformation(&sshInfo, cliConnections, cfg)
+ if err != nil {
+ return nil, nil, nil, nil, err
+ }
+
+ confR.Engine = config.EngineConfig{Remote: true, CgroupManager: "cgroupfs", ServiceDestinations: serv} // pass the service dest (either remote or something else) to engine
+ saveCmd, loadCmd := CreateCommands(source, dest, parentFlags, podman)
+
+ switch {
+ case source.Remote: // if we want to load FROM the remote, dest can either be local or remote in this case
+ err = SaveToRemote(source.Image, source.File, "", sshInfo.URI[0], sshInfo.Identities[0])
+ if err != nil {
+ return nil, nil, nil, nil, err
+ }
+ if dest.Remote { // we want to load remote -> remote, both source and dest are remote
+ rep, id, err := LoadToRemote(dest, dest.File, "", sshInfo.URI[1], sshInfo.Identities[1])
+ if err != nil {
+ return nil, nil, nil, nil, err
+ }
+ if len(rep) > 0 {
+ fmt.Println(rep)
+ }
+ if len(id) > 0 {
+ report.Names = append(report.Names, id)
+ }
+ break
+ }
+ id, err := ExecPodman(dest, podman, loadCmd)
+ if err != nil {
+ return nil, nil, nil, nil, err
+ }
+ if len(id) > 0 {
+ report.Names = append(report.Names, id)
+ }
+ case dest.Remote: // remote host load, implies source is local
+ _, err = ExecPodman(dest, podman, saveCmd)
+ if err != nil {
+ return nil, nil, nil, nil, err
+ }
+ rep, id, err := LoadToRemote(dest, source.File, "", sshInfo.URI[0], sshInfo.Identities[0])
+ if err != nil {
+ return nil, nil, nil, nil, err
+ }
+ if len(rep) > 0 {
+ fmt.Println(rep)
+ }
+ if len(id) > 0 {
+ report.Names = append(report.Names, id)
+ }
+ if err = os.Remove(source.File); err != nil {
+ return nil, nil, nil, nil, err
+ }
+ default: // else native load, both source and dest are local and transferring between users
+ if source.User == "" { // source user has to be set, destination does not
+ source.User = os.Getenv("USER")
+ if source.User == "" {
+ u, err := user.Current()
+ if err != nil {
+ return nil, nil, nil, nil, errors.Wrapf(err, "could not obtain user, make sure the environmental variable $USER is set")
+ }
+ source.User = u.Username
+ }
+ }
+ return nil, &source, &dest, parentFlags, nil // transfer needs to be done in ABI due to cross issues
+ }
+
+ return &report, nil, nil, nil, nil
+}
+
+// CreateSCPCommand takes an existing command, appends the given arguments and returns a configured podman command for image scp
+func CreateSCPCommand(cmd *exec.Cmd, command []string) *exec.Cmd {
+ cmd.Args = append(cmd.Args, command...)
+ cmd.Env = os.Environ()
+ cmd.Stderr = os.Stderr
+ cmd.Stdout = os.Stdout
+ return cmd
+}
+
+// ScpTag is a helper function for native podman to tag an image after a local load from image SCP
+func ScpTag(cmd *exec.Cmd, podman string, dest entities.ImageScpOptions) error {
+ cmd.Stdout = nil
+ out, err := cmd.Output() // this function captures the output temporarily in order to execute the next command
+ if err != nil {
+ return err
+ }
+ image := ExtractImage(out)
+ if cmd.Args[0] == "sudo" { // transferRootless will need the sudo since we are loading to sudo from a user acct
+ cmd = exec.Command("sudo", podman, "tag", image, dest.Tag)
+ } else {
+ cmd = exec.Command(podman, "tag", image, dest.Tag)
+ }
+ cmd.Stdout = os.Stdout
+ return cmd.Run()
+}
+
+// ExtractImage pulls out the last line of output from save/load (image id)
+func ExtractImage(out []byte) string {
+ fmt.Println(strings.TrimSuffix(string(out), "\n")) // print output
+ stringOut := string(out) // get all output
+ arrOut := strings.Split(stringOut, " ") // split it into an array
+ return strings.ReplaceAll(arrOut[len(arrOut)-1], "\n", "") // replace the trailing \n
+}
+
+// LoginUser starts the user process on the host so that image scp can use systemd-run
+func LoginUser(user string) (*exec.Cmd, error) {
+ sleep, err := exec.LookPath("sleep")
+ if err != nil {
+ return nil, err
+ }
+ machinectl, err := exec.LookPath("machinectl")
+ if err != nil {
+ return nil, err
+ }
+ cmd := exec.Command(machinectl, "shell", "-q", user+"@.host", sleep, "inf")
+ err = cmd.Start()
+ return cmd, err
+}
+
+// loadToRemote takes image and remote connection information. it connects to the specified client
+// and copies the saved image dir over to the remote host and then loads it onto the machine
+// returns a string containing output or an error
+func LoadToRemote(dest entities.ImageScpOptions, localFile string, tag string, url *url.URL, iden string) (string, string, error) {
+ dial, remoteFile, err := CreateConnection(url, iden)
+ if err != nil {
+ return "", "", err
+ }
+ defer dial.Close()
+
+ n, err := scpD.CopyTo(dial, localFile, remoteFile)
+ if err != nil {
+ errOut := strconv.Itoa(int(n)) + " Bytes copied before error"
+ return " ", "", errors.Wrapf(err, errOut)
+ }
+ var run string
+ if tag != "" {
+ return "", "", errors.Wrapf(define.ErrInvalidArg, "Renaming of an image is currently not supported")
+ }
+ podman := os.Args[0]
+ run = podman + " image load --input=" + remoteFile + ";rm " + remoteFile // run ssh image load of the file copied via scp
+ out, err := ExecRemoteCommand(dial, run)
+ if err != nil {
+ return "", "", err
+ }
+ rep := strings.TrimSuffix(string(out), "\n")
+ outArr := strings.Split(rep, " ")
+ id := outArr[len(outArr)-1]
+ if len(dest.Tag) > 0 { // tag the remote image using the output ID
+ run = podman + " tag " + id + " " + dest.Tag
+ _, err = ExecRemoteCommand(dial, run)
+ if err != nil {
+ return "", "", err
+ }
+ }
+ return rep, id, nil
+}
+
+// saveToRemote takes image information and remote connection information. it connects to the specified client
+// and saves the specified image on the remote machine and then copies it to the specified local location
+// returns an error if one occurs.
+func SaveToRemote(image, localFile string, tag string, uri *url.URL, iden string) error {
+ dial, remoteFile, err := CreateConnection(uri, iden)
+
+ if err != nil {
+ return err
+ }
+ defer dial.Close()
+
+ if tag != "" {
+ return errors.Wrapf(define.ErrInvalidArg, "Renaming of an image is currently not supported")
+ }
+ podman := os.Args[0]
+ run := podman + " image save " + image + " --format=oci-archive --output=" + remoteFile // run ssh image load of the file copied via scp. Files are reverse in this case...
+ _, err = ExecRemoteCommand(dial, run)
+ if err != nil {
+ return err
+ }
+ n, err := scpD.CopyFrom(dial, remoteFile, localFile)
+ if _, conErr := ExecRemoteCommand(dial, "rm "+remoteFile); conErr != nil {
+ logrus.Errorf("Removing file on endpoint: %v", conErr)
+ }
+ if err != nil {
+ errOut := strconv.Itoa(int(n)) + " Bytes copied before error"
+ return errors.Wrapf(err, errOut)
+ }
+ return nil
+}
+
+// makeRemoteFile creates the necessary remote file on the host to
+// save or load the image to. returns a string with the file name or an error
+func MakeRemoteFile(dial *ssh.Client) (string, error) {
+ run := "mktemp"
+ remoteFile, err := ExecRemoteCommand(dial, run)
+ if err != nil {
+ return "", err
+ }
+ return strings.TrimSuffix(string(remoteFile), "\n"), nil
+}
+
+// createConnections takes a boolean determining which ssh client to dial
+// and returns the dials client, its newly opened remote file, and an error if applicable.
+func CreateConnection(url *url.URL, iden string) (*ssh.Client, string, error) {
+ cfg, err := ValidateAndConfigure(url, iden)
+ if err != nil {
+ return nil, "", err
+ }
+ dialAdd, err := ssh.Dial("tcp", url.Host, cfg) // dial the client
+ if err != nil {
+ return nil, "", errors.Wrapf(err, "failed to connect")
+ }
+ file, err := MakeRemoteFile(dialAdd)
+ if err != nil {
+ return nil, "", err
+ }
+
+ return dialAdd, file, nil
+}
+
+// GetSerivceInformation takes the parsed list of hosts to connect to and validates the information
+func GetServiceInformation(sshInfo *entities.ImageScpConnections, cliConnections []string, cfg *config.Config) (map[string]config.Destination, error) {
+ var serv map[string]config.Destination
+ var urlS string
+ var iden string
+ for i, val := range cliConnections {
+ splitEnv := strings.SplitN(val, "::", 2)
+ sshInfo.Connections = append(sshInfo.Connections, splitEnv[0])
+ conn, found := cfg.Engine.ServiceDestinations[sshInfo.Connections[i]]
+ if found {
+ urlS = conn.URI
+ iden = conn.Identity
+ } else { // no match, warn user and do a manual connection.
+ urlS = "ssh://" + sshInfo.Connections[i]
+ iden = ""
+ logrus.Warnf("Unknown connection name given. Please use system connection add to specify the default remote socket location")
+ }
+ urlFinal, err := url.Parse(urlS) // create an actual url to pass to exec command
+ if err != nil {
+ return nil, err
+ }
+ if urlFinal.User.Username() == "" {
+ if urlFinal.User, err = GetUserInfo(urlFinal); err != nil {
+ return nil, err
+ }
+ }
+ sshInfo.URI = append(sshInfo.URI, urlFinal)
+ sshInfo.Identities = append(sshInfo.Identities, iden)
+ }
+ return serv, nil
+}
+
+// execPodman executes the podman save/load command given the podman binary
+func ExecPodman(dest entities.ImageScpOptions, podman string, command []string) (string, error) {
+ cmd := exec.Command(podman)
+ CreateSCPCommand(cmd, command[1:])
+ logrus.Debugf("Executing podman command: %q", cmd)
+ if strings.Contains(strings.Join(command, " "), "load") { // need to tag
+ if len(dest.Tag) > 0 {
+ return "", ScpTag(cmd, podman, dest)
+ }
+ cmd.Stdout = nil
+ out, err := cmd.Output()
+ if err != nil {
+ return "", err
+ }
+ img := ExtractImage(out)
+ return img, nil
+ }
+ return "", cmd.Run()
+}
+
+// createCommands forms the podman save and load commands used by SCP
+func CreateCommands(source entities.ImageScpOptions, dest entities.ImageScpOptions, parentFlags []string, podman string) ([]string, []string) {
+ var parentString string
+ quiet := ""
+ if source.Quiet {
+ quiet = "-q "
+ }
+ if len(parentFlags) > 0 {
+ parentString = strings.Join(parentFlags, " ") + " " // if there are parent args, an extra space needs to be added
+ } else {
+ parentString = strings.Join(parentFlags, " ")
+ }
+ loadCmd := strings.Split(fmt.Sprintf("%s %sload %s--input %s", podman, parentString, quiet, dest.File), " ")
+ saveCmd := strings.Split(fmt.Sprintf("%s %vsave %s--output %s %s", podman, parentString, quiet, source.File, source.Image), " ")
+ return saveCmd, loadCmd
+}
+
+// parseImageSCPArg returns the valid connection, and source/destination data based off of the information provided by the user
+// arg is a string containing one of the cli arguments returned is a filled out source/destination options structs as well as a connections array and an error if applicable
+func ParseImageSCPArg(arg string) (*entities.ImageScpOptions, []string, error) {
+ location := entities.ImageScpOptions{}
+ var err error
+ cliConnections := []string{}
+
+ switch {
+ case strings.Contains(arg, "@localhost::"): // image transfer between users
+ location.User = strings.Split(arg, "@")[0]
+ location, err = ValidateImagePortion(location, arg)
+ if err != nil {
+ return nil, nil, err
+ }
+ cliConnections = append(cliConnections, arg)
+ case strings.Contains(arg, "::"):
+ location, err = ValidateImagePortion(location, arg)
+ if err != nil {
+ return nil, nil, err
+ }
+ location.Remote = true
+ cliConnections = append(cliConnections, arg)
+ default:
+ location.Image = arg
+ }
+ return &location, cliConnections, nil
+}
+
+// validateImagePortion is a helper function to validate the image name in an SCP argument
+func ValidateImagePortion(location entities.ImageScpOptions, arg string) (entities.ImageScpOptions, error) {
+ if RemoteArgLength(arg, 1) > 0 {
+ err := ValidateImageName(strings.Split(arg, "::")[1])
+ if err != nil {
+ return location, err
+ }
+ location.Image = strings.Split(arg, "::")[1] // this will get checked/set again once we validate connections
+ }
+ return location, nil
+}
+
+// validateSCPArgs takes the array of source and destination options and checks for common errors
+func ValidateSCPArgs(locations []*entities.ImageScpOptions) error {
+ if len(locations) > 2 {
+ return errors.Wrapf(define.ErrInvalidArg, "cannot specify more than two arguments")
+ }
+ switch {
+ case len(locations[0].Image) > 0 && len(locations[1].Image) > 0:
+ locations[1].Tag = locations[1].Image
+ locations[1].Image = ""
+ case len(locations[0].Image) == 0 && len(locations[1].Image) == 0:
+ return errors.Wrapf(define.ErrInvalidArg, "a source image must be specified")
+ }
+ return nil
+}
+
+// validateImageName makes sure that the image given is valid and no injections are occurring
+// we simply use this for error checking, bot setting the image
+func ValidateImageName(input string) error {
+ // ParseNormalizedNamed transforms a shortname image into its
+ // full name reference so busybox => docker.io/library/busybox
+ // we want to keep our shortnames, so only return an error if
+ // we cannot parse what the user has given us
+ _, err := reference.ParseNormalizedNamed(input)
+ return err
+}
+
+// remoteArgLength is a helper function to simplify the extracting of host argument data
+// returns an int which contains the length of a specified index in a host::image string
+func RemoteArgLength(input string, side int) int {
+ if strings.Contains(input, "::") {
+ return len((strings.Split(input, "::"))[side])
+ }
+ return -1
+}
+
+// ExecRemoteCommand takes a ssh client connection and a command to run and executes the
+// command on the specified client. The function returns the Stdout from the client or the Stderr
+func ExecRemoteCommand(dial *ssh.Client, run string) ([]byte, error) {
+ sess, err := dial.NewSession() // new ssh client session
+ if err != nil {
+ return nil, err
+ }
+ defer sess.Close()
+
+ var buffer bytes.Buffer
+ var bufferErr bytes.Buffer
+ sess.Stdout = &buffer // output from client funneled into buffer
+ sess.Stderr = &bufferErr // err form client funneled into buffer
+ if err := sess.Run(run); err != nil { // run the command on the ssh client
+ return nil, errors.Wrapf(err, bufferErr.String())
+ }
+ return buffer.Bytes(), nil
+}
+
+func GetUserInfo(uri *url.URL) (*url.Userinfo, error) {
+ var (
+ usr *user.User
+ err error
+ )
+ if u, found := os.LookupEnv("_CONTAINERS_ROOTLESS_UID"); found {
+ usr, err = user.LookupId(u)
+ if err != nil {
+ return nil, errors.Wrapf(err, "failed to lookup rootless user")
+ }
+ } else {
+ usr, err = user.Current()
+ if err != nil {
+ return nil, errors.Wrapf(err, "failed to obtain current user")
+ }
+ }
+
+ pw, set := uri.User.Password()
+ if set {
+ return url.UserPassword(usr.Username, pw), nil
+ }
+ return url.User(usr.Username), nil
+}
+
+// ValidateAndConfigure will take a ssh url and an identity key (rsa and the like) and ensure the information given is valid
+// iden iden can be blank to mean no identity key
+// once the function validates the information it creates and returns an ssh.ClientConfig.
+func ValidateAndConfigure(uri *url.URL, iden string) (*ssh.ClientConfig, error) {
+ var signers []ssh.Signer
+ passwd, passwdSet := uri.User.Password()
+ if iden != "" { // iden might be blank if coming from image scp or if no validation is needed
+ value := iden
+ s, err := terminal.PublicKey(value, []byte(passwd))
+ if err != nil {
+ return nil, errors.Wrapf(err, "failed to read identity %q", value)
+ }
+ signers = append(signers, s)
+ logrus.Debugf("SSH Ident Key %q %s %s", value, ssh.FingerprintSHA256(s.PublicKey()), s.PublicKey().Type())
+ }
+ if sock, found := os.LookupEnv("SSH_AUTH_SOCK"); found { // validate ssh information, specifically the unix file socket used by the ssh agent.
+ logrus.Debugf("Found SSH_AUTH_SOCK %q, ssh-agent signer enabled", sock)
+
+ c, err := net.Dial("unix", sock)
+ if err != nil {
+ return nil, err
+ }
+ agentSigners, err := agent.NewClient(c).Signers()
+ if err != nil {
+ return nil, err
+ }
+
+ signers = append(signers, agentSigners...)
+
+ if logrus.IsLevelEnabled(logrus.DebugLevel) {
+ for _, s := range agentSigners {
+ logrus.Debugf("SSH Agent Key %s %s", ssh.FingerprintSHA256(s.PublicKey()), s.PublicKey().Type())
+ }
+ }
+ }
+ var authMethods []ssh.AuthMethod // now we validate and check for the authorization methods, most notaibly public key authorization
+ if len(signers) > 0 {
+ var dedup = make(map[string]ssh.Signer)
+ for _, s := range signers {
+ fp := ssh.FingerprintSHA256(s.PublicKey())
+ if _, found := dedup[fp]; found {
+ logrus.Debugf("Dedup SSH Key %s %s", ssh.FingerprintSHA256(s.PublicKey()), s.PublicKey().Type())
+ }
+ dedup[fp] = s
+ }
+
+ var uniq []ssh.Signer
+ for _, s := range dedup {
+ uniq = append(uniq, s)
+ }
+ authMethods = append(authMethods, ssh.PublicKeysCallback(func() ([]ssh.Signer, error) {
+ return uniq, nil
+ }))
+ }
+ if passwdSet { // if password authentication is given and valid, add to the list
+ authMethods = append(authMethods, ssh.Password(passwd))
+ }
+ if len(authMethods) == 0 {
+ authMethods = append(authMethods, ssh.PasswordCallback(func() (string, error) {
+ pass, err := terminal.ReadPassword(fmt.Sprintf("%s's login password:", uri.User.Username()))
+ return string(pass), err
+ }))
+ }
+ tick, err := time.ParseDuration("40s")
+ if err != nil {
+ return nil, err
+ }
+ cfg := &ssh.ClientConfig{
+ User: uri.User.Username(),
+ Auth: authMethods,
+ HostKeyCallback: ssh.InsecureIgnoreHostKey(),
+ Timeout: tick,
+ }
+ return cfg, nil
+}
diff --git a/pkg/domain/utils/utils_test.go b/pkg/domain/utils/utils_test.go
index 952a4b5be..291567f6b 100644
--- a/pkg/domain/utils/utils_test.go
+++ b/pkg/domain/utils/utils_test.go
@@ -5,6 +5,7 @@ import (
"sort"
"testing"
+ "github.com/containers/podman/v4/pkg/domain/entities"
"github.com/stretchr/testify/assert"
)
@@ -74,3 +75,41 @@ func TestToURLValues(t *testing.T) {
})
}
}
+
+func TestParseSCPArgs(t *testing.T) {
+ args := []string{"alpine", "root@localhost::"}
+ var source *entities.ImageScpOptions
+ var dest *entities.ImageScpOptions
+ var err error
+ source, _, err = ParseImageSCPArg(args[0])
+ assert.Nil(t, err)
+ assert.Equal(t, source.Image, "alpine")
+
+ dest, _, err = ParseImageSCPArg(args[1])
+ assert.Nil(t, err)
+ assert.Equal(t, dest.Image, "")
+ assert.Equal(t, dest.User, "root")
+
+ args = []string{"root@localhost::alpine"}
+ source, _, err = ParseImageSCPArg(args[0])
+ assert.Nil(t, err)
+ assert.Equal(t, source.User, "root")
+ assert.Equal(t, source.Image, "alpine")
+
+ args = []string{"charliedoern@192.168.68.126::alpine", "foobar@192.168.68.126::"}
+ source, _, err = ParseImageSCPArg(args[0])
+ assert.Nil(t, err)
+ assert.True(t, source.Remote)
+ assert.Equal(t, source.Image, "alpine")
+
+ dest, _, err = ParseImageSCPArg(args[1])
+ assert.Nil(t, err)
+ assert.True(t, dest.Remote)
+ assert.Equal(t, dest.Image, "")
+
+ args = []string{"charliedoern@192.168.68.126::alpine"}
+ source, _, err = ParseImageSCPArg(args[0])
+ assert.Nil(t, err)
+ assert.True(t, source.Remote)
+ assert.Equal(t, source.Image, "alpine")
+}