diff options
Diffstat (limited to 'pkg')
72 files changed, 682 insertions, 381 deletions
diff --git a/pkg/api/handlers/compat/containers_stats.go b/pkg/api/handlers/compat/containers_stats.go index 77b16b03e..12c5283fc 100644 --- a/pkg/api/handlers/compat/containers_stats.go +++ b/pkg/api/handlers/compat/containers_stats.go @@ -12,6 +12,7 @@ import ( api "github.com/containers/podman/v4/pkg/api/types" docker "github.com/docker/docker/api/types" "github.com/gorilla/schema" + runccgroups "github.com/opencontainers/runc/libcontainer/cgroups" "github.com/pkg/errors" "github.com/sirupsen/logrus" ) @@ -44,18 +45,6 @@ func StatsContainer(w http.ResponseWriter, r *http.Request) { return } - // If the container isn't running, then let's not bother and return - // immediately. - state, err := ctnr.State() - if err != nil { - utils.InternalServerError(w, err) - return - } - if state != define.ContainerStateRunning { - utils.Error(w, http.StatusConflict, define.ErrCtrStateInvalid) - return - } - stats, err := ctnr.GetContainerStats(nil) if err != nil { utils.InternalServerError(w, errors.Wrapf(err, "failed to obtain Container %s stats", name)) @@ -70,7 +59,7 @@ func StatsContainer(w http.ResponseWriter, r *http.Request) { flusher.Flush() } - // Setup JSON encoder for streaming. + // Set up JSON encoder for streaming. coder.SetEscapeHTML(true) var preRead time.Time var preCPUStats CPUStats @@ -144,17 +133,23 @@ streamLabel: // A label to flatten the scope InstanceID: "", } + cfg := ctnr.Config() + memoryLimit := cgroupStat.MemoryStats.Usage.Limit + if cfg.Spec.Linux != nil && cfg.Spec.Linux.Resources != nil && cfg.Spec.Linux.Resources.Memory != nil && *cfg.Spec.Linux.Resources.Memory.Limit > 0 { + memoryLimit = uint64(*cfg.Spec.Linux.Resources.Memory.Limit) + } + systemUsage, _ := cgroups.GetSystemCPUUsage() s := StatsJSON{ Stats: Stats{ Read: time.Now(), PreRead: preRead, PidsStats: docker.PidsStats{ - Current: cgroupStat.Pids.Current, + Current: cgroupStat.PidsStats.Current, Limit: 0, }, BlkioStats: docker.BlkioStats{ - IoServiceBytesRecursive: toBlkioStatEntry(cgroupStat.Blkio.IoServiceBytesRecursive), + IoServiceBytesRecursive: toBlkioStatEntry(cgroupStat.BlkioStats.IoServiceBytesRecursive), IoServicedRecursive: nil, IoQueuedRecursive: nil, IoServiceTimeRecursive: nil, @@ -165,14 +160,14 @@ streamLabel: // A label to flatten the scope }, CPUStats: CPUStats{ CPUUsage: docker.CPUUsage{ - TotalUsage: cgroupStat.CPU.Usage.Total, - PercpuUsage: cgroupStat.CPU.Usage.PerCPU, - UsageInKernelmode: cgroupStat.CPU.Usage.Kernel, - UsageInUsermode: cgroupStat.CPU.Usage.Total - cgroupStat.CPU.Usage.Kernel, + TotalUsage: cgroupStat.CpuStats.CpuUsage.TotalUsage, + PercpuUsage: cgroupStat.CpuStats.CpuUsage.PercpuUsage, + UsageInKernelmode: cgroupStat.CpuStats.CpuUsage.UsageInKernelmode, + UsageInUsermode: cgroupStat.CpuStats.CpuUsage.TotalUsage - cgroupStat.CpuStats.CpuUsage.UsageInKernelmode, }, CPU: stats.CPU, SystemUsage: systemUsage, - OnlineCPUs: uint32(len(cgroupStat.CPU.Usage.PerCPU)), + OnlineCPUs: uint32(len(cgroupStat.CpuStats.CpuUsage.PercpuUsage)), ThrottlingData: docker.ThrottlingData{ Periods: 0, ThrottledPeriods: 0, @@ -181,11 +176,11 @@ streamLabel: // A label to flatten the scope }, PreCPUStats: preCPUStats, MemoryStats: docker.MemoryStats{ - Usage: cgroupStat.Memory.Usage.Usage, - MaxUsage: cgroupStat.Memory.Usage.Limit, + Usage: cgroupStat.MemoryStats.Usage.Usage, + MaxUsage: cgroupStat.MemoryStats.Usage.Limit, Stats: nil, Failcnt: 0, - Limit: cgroupStat.Memory.Usage.Limit, + Limit: memoryLimit, Commit: 0, CommitPeak: 0, PrivateWorkingSet: 0, @@ -222,7 +217,7 @@ streamLabel: // A label to flatten the scope } } -func toBlkioStatEntry(entries []cgroups.BlkIOEntry) []docker.BlkioStatEntry { +func toBlkioStatEntry(entries []runccgroups.BlkioStatEntry) []docker.BlkioStatEntry { results := make([]docker.BlkioStatEntry, len(entries)) for i, e := range entries { bits, err := json.Marshal(e) diff --git a/pkg/api/handlers/compat/images.go b/pkg/api/handlers/compat/images.go index 76a28fadf..981a38c35 100644 --- a/pkg/api/handlers/compat/images.go +++ b/pkg/api/handlers/compat/images.go @@ -165,7 +165,7 @@ func CommitContainer(w http.ResponseWriter, r *http.Request) { utils.Error(w, http.StatusInternalServerError, errors.Wrapf(err, "CommitFailure")) return } - utils.WriteResponse(w, http.StatusCreated, entities.IDResponse{ID: commitImage.ID()}) // nolint + utils.WriteResponse(w, http.StatusCreated, entities.IDResponse{ID: commitImage.ID()}) } func CreateImageFromSrc(w http.ResponseWriter, r *http.Request) { @@ -237,7 +237,7 @@ func CreateImageFromSrc(w http.ResponseWriter, r *http.Request) { Status string `json:"status"` Progress string `json:"progress"` ProgressDetail map[string]string `json:"progressDetail"` - Id string `json:"id"` // nolint + Id string `json:"id"` //nolint:revive,stylecheck }{ Status: report.Id, ProgressDetail: map[string]string{}, @@ -333,7 +333,7 @@ loop: // break out of for/select infinite loop Total int64 `json:"total,omitempty"` } `json:"progressDetail,omitempty"` Error string `json:"error,omitempty"` - Id string `json:"id,omitempty"` // nolint + Id string `json:"id,omitempty"` //nolint:revive,stylecheck } select { case e := <-progress: diff --git a/pkg/api/handlers/compat/images_build.go b/pkg/api/handlers/compat/images_build.go index fe17aa1d4..80fc17f56 100644 --- a/pkg/api/handlers/compat/images_build.go +++ b/pkg/api/handlers/compat/images_build.go @@ -78,15 +78,15 @@ func BuildImage(w http.ResponseWriter, r *http.Request) { AppArmor string `schema:"apparmor"` BuildArgs string `schema:"buildargs"` CacheFrom string `schema:"cachefrom"` - CgroupParent string `schema:"cgroupparent"` // nolint + CgroupParent string `schema:"cgroupparent"` Compression uint64 `schema:"compression"` ConfigureNetwork string `schema:"networkmode"` CPPFlags string `schema:"cppflags"` - CpuPeriod uint64 `schema:"cpuperiod"` // nolint - CpuQuota int64 `schema:"cpuquota"` // nolint - CpuSetCpus string `schema:"cpusetcpus"` // nolint - CpuSetMems string `schema:"cpusetmems"` // nolint - CpuShares uint64 `schema:"cpushares"` // nolint + CpuPeriod uint64 `schema:"cpuperiod"` //nolint:revive,stylecheck + CpuQuota int64 `schema:"cpuquota"` //nolint:revive,stylecheck + CpuSetCpus string `schema:"cpusetcpus"` //nolint:revive,stylecheck + CpuSetMems string `schema:"cpusetmems"` //nolint:revive,stylecheck + CpuShares uint64 `schema:"cpushares"` //nolint:revive,stylecheck DNSOptions string `schema:"dnsoptions"` DNSSearch string `schema:"dnssearch"` DNSServers string `schema:"dnsservers"` @@ -101,7 +101,7 @@ func BuildImage(w http.ResponseWriter, r *http.Request) { IdentityLabel bool `schema:"identitylabel"` Ignore bool `schema:"ignore"` Isolation string `schema:"isolation"` - Jobs int `schema:"jobs"` // nolint + Jobs int `schema:"jobs"` LabelOpts string `schema:"labelopts"` Labels string `schema:"labels"` Layers bool `schema:"layers"` @@ -111,6 +111,7 @@ func BuildImage(w http.ResponseWriter, r *http.Request) { Memory int64 `schema:"memory"` NamespaceOptions string `schema:"nsoptions"` NoCache bool `schema:"nocache"` + OmitHistory bool `schema:"omithistory"` OSFeatures []string `schema:"osfeature"` OSVersion string `schema:"osversion"` OutputFormat string `schema:"outputformat"` @@ -366,7 +367,7 @@ func BuildImage(w http.ResponseWriter, r *http.Request) { } } } - var additionalTags []string // nolint + var additionalTags []string for i := 1; i < len(tags); i++ { possiblyNormalizedTag, err := utils.NormalizeToDockerHub(r, tags[i]) if err != nil { @@ -595,6 +596,7 @@ func BuildImage(w http.ResponseWriter, r *http.Request) { LabelOpts: labelOpts, Memory: query.Memory, MemorySwap: query.MemSwap, + OmitHistory: query.OmitHistory, SeccompProfilePath: seccomp, ShmSize: strconv.Itoa(query.ShmSize), Ulimit: ulimits, @@ -799,7 +801,7 @@ func parseNetworkConfigurationPolicy(network string) buildah.NetworkConfiguratio } } -func parseLibPodIsolation(isolation string) (buildah.Isolation, error) { // nolint +func parseLibPodIsolation(isolation string) (buildah.Isolation, error) { if val, err := strconv.Atoi(isolation); err == nil { return buildah.Isolation(val), nil } diff --git a/pkg/api/handlers/compat/images_save.go b/pkg/api/handlers/compat/images_save.go index b39c719a0..6314756f6 100644 --- a/pkg/api/handlers/compat/images_save.go +++ b/pkg/api/handlers/compat/images_save.go @@ -6,7 +6,7 @@ import ( "os" ) -func SaveFromBody(f *os.File, r *http.Request) error { // nolint +func SaveFromBody(f *os.File, r *http.Request) error { if _, err := io.Copy(f, r.Body); err != nil { return err } diff --git a/pkg/api/handlers/libpod/containers.go b/pkg/api/handlers/libpod/containers.go index 6b5bee403..deddcaf93 100644 --- a/pkg/api/handlers/libpod/containers.go +++ b/pkg/api/handlers/libpod/containers.go @@ -115,10 +115,6 @@ func ListContainers(w http.ResponseWriter, r *http.Request) { utils.InternalServerError(w, err) return } - if len(pss) == 0 { - utils.WriteResponse(w, http.StatusOK, "[]") - return - } utils.WriteResponse(w, http.StatusOK, pss) } diff --git a/pkg/api/handlers/libpod/containers_stats.go b/pkg/api/handlers/libpod/containers_stats.go index d34254fd7..46d722a3d 100644 --- a/pkg/api/handlers/libpod/containers_stats.go +++ b/pkg/api/handlers/libpod/containers_stats.go @@ -66,7 +66,7 @@ func StatsContainer(w http.ResponseWriter, r *http.Request) { flusher.Flush() } - // Setup JSON encoder for streaming. + // Set up JSON encoder for streaming. coder := json.NewEncoder(w) coder.SetEscapeHTML(true) diff --git a/pkg/api/handlers/libpod/images.go b/pkg/api/handlers/libpod/images.go index 60ed5feb3..a8a50ae58 100644 --- a/pkg/api/handlers/libpod/images.go +++ b/pkg/api/handlers/libpod/images.go @@ -562,7 +562,7 @@ func CommitContainer(w http.ResponseWriter, r *http.Request) { utils.Error(w, http.StatusInternalServerError, errors.Wrapf(err, "CommitFailure")) return } - utils.WriteResponse(w, http.StatusOK, entities.IDResponse{ID: commitImage.ID()}) // nolint + utils.WriteResponse(w, http.StatusOK, entities.IDResponse{ID: commitImage.ID()}) } func UntagImage(w http.ResponseWriter, r *http.Request) { diff --git a/pkg/api/handlers/utils/containers.go b/pkg/api/handlers/utils/containers.go index 8588b49ba..1795f6ce1 100644 --- a/pkg/api/handlers/utils/containers.go +++ b/pkg/api/handlers/utils/containers.go @@ -191,7 +191,6 @@ func waitDockerCondition(ctx context.Context, containerName string, interval tim var notRunningStates = []define.ContainerStatus{ define.ContainerStateCreated, define.ContainerStateRemoving, - define.ContainerStateStopped, define.ContainerStateExited, define.ContainerStateConfigured, } diff --git a/pkg/api/handlers/utils/images.go b/pkg/api/handlers/utils/images.go index 433231f59..77f6dcf1d 100644 --- a/pkg/api/handlers/utils/images.go +++ b/pkg/api/handlers/utils/images.go @@ -68,7 +68,7 @@ func IsRegistryReference(name string) error { imageRef, err := alltransports.ParseImageName(name) if err != nil { // No supported transport -> assume a docker-stype reference. - return nil // nolint: nilerr + return nil //nolint: nilerr } if imageRef.Transport().Name() == docker.Transport.Name() { return nil diff --git a/pkg/api/server/listener_api.go b/pkg/api/server/listener_api.go index 2d02df7dc..aaaf6688e 100644 --- a/pkg/api/server/listener_api.go +++ b/pkg/api/server/listener_api.go @@ -11,7 +11,7 @@ import ( // ListenUnix follows stdlib net.Listen() API, providing a unix listener for given path // ListenUnix will delete and create files/directories as needed func ListenUnix(network string, path string) (net.Listener, error) { - // setup custom listener for API server + // set up custom listener for API server err := os.MkdirAll(filepath.Dir(path), 0770) if err != nil { return nil, errors.Wrapf(err, "api.ListenUnix() failed to create %s", filepath.Dir(path)) diff --git a/pkg/api/server/server.go b/pkg/api/server/server.go index 7a7e35e8e..5482a8ec2 100644 --- a/pkg/api/server/server.go +++ b/pkg/api/server/server.go @@ -148,7 +148,7 @@ func newServer(runtime *libpod.Runtime, listener net.Listener, opts entities.Ser if logrus.IsLevelEnabled(logrus.TraceLevel) { // If in trace mode log request and response bodies router.Use(loggingHandler()) - router.Walk(func(route *mux.Route, r *mux.Router, ancestors []*mux.Route) error { // nolint + _ = router.Walk(func(route *mux.Route, r *mux.Router, ancestors []*mux.Route) error { path, err := route.GetPathTemplate() if err != nil { path = "<N/A>" diff --git a/pkg/bindings/connection.go b/pkg/bindings/connection.go index 3739ec404..6b3576f31 100644 --- a/pkg/bindings/connection.go +++ b/pkg/bindings/connection.go @@ -95,7 +95,7 @@ func NewConnectionWithIdentity(ctx context.Context, uri string, identity string) return nil, errors.Wrapf(err, "Value of CONTAINER_HOST is not a valid url: %s", uri) } - // Now we setup the http Client to use the connection above + // Now we set up the http Client to use the connection above var connection Connection switch _url.Scheme { case "ssh": @@ -164,7 +164,7 @@ func pingNewConnection(ctx context.Context) (*semver.Version, error) { if response.StatusCode == http.StatusOK { versionHdr := response.Header.Get("Libpod-API-Version") if versionHdr == "" { - logrus.Info("Service did not provide Libpod-API-Version Header") + logrus.Warn("Service did not provide Libpod-API-Version Header") return new(semver.Version), nil } versionSrv, err := semver.ParseTolerant(versionHdr) @@ -315,7 +315,8 @@ func unixClient(_url *url.URL) Connection { return connection } -// DoRequest assembles the http request and returns the response +// DoRequest assembles the http request and returns the response. +// The caller must close the response body. func (c *Connection) DoRequest(ctx context.Context, httpBody io.Reader, httpMethod, endpoint string, queryParams url.Values, headers http.Header, pathValues ...string) (*APIResponse, error) { var ( err error @@ -361,7 +362,7 @@ func (c *Connection) DoRequest(ctx context.Context, httpBody io.Reader, httpMeth // Give the Do three chances in the case of a comm/service hiccup for i := 1; i <= 3; i++ { - response, err = c.Client.Do(req) // nolint + response, err = c.Client.Do(req) //nolint:bodyclose // The caller has to close the body. if err == nil { break } diff --git a/pkg/bindings/containers/attach.go b/pkg/bindings/containers/attach.go index d84b47052..303fc65bd 100644 --- a/pkg/bindings/containers/attach.go +++ b/pkg/bindings/containers/attach.go @@ -54,8 +54,6 @@ func Attach(ctx context.Context, nameOrID string, stdin io.Reader, stdout io.Wri stderr = (io.Writer)(nil) } - logrus.Infof("Going to attach to container %q", nameOrID) - conn, err := bindings.GetClient(ctx) if err != nil { return err @@ -357,7 +355,7 @@ func attachHandleResize(ctx, winCtx context.Context, winChange chan os.Signal, i resizeErr = ResizeContainerTTY(ctx, id, new(ResizeTTYOptions).WithHeight(h).WithWidth(w)) } if resizeErr != nil { - logrus.Infof("Failed to resize TTY: %v", resizeErr) + logrus.Debugf("Failed to resize TTY: %v", resizeErr) } } diff --git a/pkg/bindings/containers/containers.go b/pkg/bindings/containers/containers.go index be421cc8b..ea01bc7d9 100644 --- a/pkg/bindings/containers/containers.go +++ b/pkg/bindings/containers/containers.go @@ -13,7 +13,6 @@ import ( "github.com/containers/podman/v4/pkg/domain/entities" "github.com/containers/podman/v4/pkg/domain/entities/reports" "github.com/pkg/errors" - "github.com/sirupsen/logrus" ) var ( @@ -25,7 +24,7 @@ var ( // the most recent number of containers. The pod and size booleans indicate that pod information and rootfs // size information should also be included. Finally, the sync bool synchronizes the OCI runtime and // container state. -func List(ctx context.Context, options *ListOptions) ([]entities.ListContainer, error) { // nolint:typecheck +func List(ctx context.Context, options *ListOptions) ([]entities.ListContainer, error) { if options == nil { options = new(ListOptions) } @@ -201,7 +200,6 @@ func Start(ctx context.Context, nameOrID string, options *StartOptions) error { if options == nil { options = new(StartOptions) } - logrus.Infof("Going to start container %q", nameOrID) conn, err := bindings.GetClient(ctx) if err != nil { return err @@ -339,7 +337,7 @@ func Unpause(ctx context.Context, nameOrID string, options *UnpauseOptions) erro // Wait blocks until the given container reaches a condition. If not provided, the condition will // default to stopped. If the condition is stopped, an exit code for the container will be provided. The // nameOrID can be a container name or a partial/full ID. -func Wait(ctx context.Context, nameOrID string, options *WaitOptions) (int32, error) { // nolint +func Wait(ctx context.Context, nameOrID string, options *WaitOptions) (int32, error) { if options == nil { options = new(WaitOptions) } diff --git a/pkg/bindings/images/build.go b/pkg/bindings/images/build.go index fe81dc662..f14f866dd 100644 --- a/pkg/bindings/images/build.go +++ b/pkg/bindings/images/build.go @@ -170,6 +170,11 @@ func Build(ctx context.Context, containerFiles []string, options entities.BuildO } else { params.Set("rm", "0") } + if options.CommonBuildOpts.OmitHistory { + params.Set("omithistory", "1") + } else { + params.Set("omithistory", "0") + } if len(options.From) > 0 { params.Set("from", options.From) } @@ -616,7 +621,7 @@ func nTar(excludes []string, sources ...string) (io.ReadCloser, error) { } name := filepath.ToSlash(strings.TrimPrefix(path, s+string(filepath.Separator))) - excluded, err := pm.Matches(name) // nolint:staticcheck + excluded, err := pm.Matches(name) //nolint:staticcheck if err != nil { return errors.Wrapf(err, "error checking if %q is excluded", name) } diff --git a/pkg/bindings/images/build_unix.go b/pkg/bindings/images/build_unix.go index 32e2ba9af..07bb8cbcd 100644 --- a/pkg/bindings/images/build_unix.go +++ b/pkg/bindings/images/build_unix.go @@ -11,7 +11,7 @@ import ( func checkHardLink(fi os.FileInfo) (devino, bool) { st := fi.Sys().(*syscall.Stat_t) return devino{ - Dev: uint64(st.Dev), // nolint: unconvert + Dev: uint64(st.Dev), //nolint: unconvert Ino: st.Ino, }, st.Nlink > 1 } diff --git a/pkg/bindings/manifests/types.go b/pkg/bindings/manifests/types.go index d0b0b2e71..e23ef798d 100644 --- a/pkg/bindings/manifests/types.go +++ b/pkg/bindings/manifests/types.go @@ -44,16 +44,18 @@ type RemoveOptions struct { type ModifyOptions struct { // Operation values are "update", "remove" and "annotate". This allows the service to // efficiently perform each update on a manifest list. - Operation *string - All *bool // All when true, operate on all images in a manifest list that may be included in Images - Annotations map[string]string // Annotations to add to manifest list - Arch *string // Arch overrides the architecture for the image - Features []string // Feature list for the image - Images []string // Images is an optional list of images to add/remove to/from manifest list depending on operation - OS *string // OS overrides the operating system for the image - OSFeatures []string // OS features for the image - OSVersion *string // OSVersion overrides the operating system for the image - Variant *string // Variant overrides the operating system variant for the image + Operation *string + All *bool // All when true, operate on all images in a manifest list that may be included in Images + Annotations map[string]string // Annotations to add to manifest list + Arch *string // Arch overrides the architecture for the image + Features []string // Feature list for the image + Images []string // Images is an optional list of images to add/remove to/from manifest list depending on operation + OS *string // OS overrides the operating system for the image + // OS features for the image + OSFeatures []string `json:"os_features" schema:"os_features"` + // OSVersion overrides the operating system for the image + OSVersion *string `json:"os_version" schema:"os_version"` + Variant *string // Variant overrides the operating system variant for the image Authfile *string Password *string Username *string diff --git a/pkg/bindings/manifests/types_modify_options.go b/pkg/bindings/manifests/types_modify_options.go index 9d2ed2613..ab00cb2c5 100644 --- a/pkg/bindings/manifests/types_modify_options.go +++ b/pkg/bindings/manifests/types_modify_options.go @@ -122,13 +122,13 @@ func (o *ModifyOptions) GetOS() string { return *o.OS } -// WithOSFeatures set oS features for the image +// WithOSFeatures set field OSFeatures to given value func (o *ModifyOptions) WithOSFeatures(value []string) *ModifyOptions { o.OSFeatures = value return o } -// GetOSFeatures returns value of oS features for the image +// GetOSFeatures returns value of field OSFeatures func (o *ModifyOptions) GetOSFeatures() []string { if o.OSFeatures == nil { var z []string @@ -137,13 +137,13 @@ func (o *ModifyOptions) GetOSFeatures() []string { return o.OSFeatures } -// WithOSVersion set oSVersion overrides the operating system for the image +// WithOSVersion set field OSVersion to given value func (o *ModifyOptions) WithOSVersion(value string) *ModifyOptions { o.OSVersion = &value return o } -// GetOSVersion returns value of oSVersion overrides the operating system for the image +// GetOSVersion returns value of field OSVersion func (o *ModifyOptions) GetOSVersion() string { if o.OSVersion == nil { var z string diff --git a/pkg/criu/criu.go b/pkg/criu/criu.go index 6570159d7..0b0bbff5d 100644 --- a/pkg/criu/criu.go +++ b/pkg/criu/criu.go @@ -1,51 +1,8 @@ -//go:build linux -// +build linux - package criu -import ( - "github.com/checkpoint-restore/go-criu/v5" - "github.com/checkpoint-restore/go-criu/v5/rpc" - - "google.golang.org/protobuf/proto" -) - // MinCriuVersion for Podman at least CRIU 3.11 is required const MinCriuVersion = 31100 // PodCriuVersion is the version of CRIU needed for // checkpointing and restoring containers out of and into Pods. const PodCriuVersion = 31600 - -// CheckForCriu uses CRIU's go bindings to check if the CRIU -// binary exists and if it at least the version Podman needs. -func CheckForCriu(version int) bool { - c := criu.MakeCriu() - result, err := c.IsCriuAtLeast(version) - if err != nil { - return false - } - return result -} - -func GetCriuVestion() (int, error) { - c := criu.MakeCriu() - return c.GetCriuVersion() -} - -func MemTrack() bool { - features, err := criu.MakeCriu().FeatureCheck( - &rpc.CriuFeatures{ - MemTrack: proto.Bool(true), - }, - ) - if err != nil { - return false - } - - if features == nil || features.MemTrack == nil { - return false - } - - return *features.MemTrack -} diff --git a/pkg/criu/criu_linux.go b/pkg/criu/criu_linux.go new file mode 100644 index 000000000..c28e23fd7 --- /dev/null +++ b/pkg/criu/criu_linux.go @@ -0,0 +1,44 @@ +//go:build linux +// +build linux + +package criu + +import ( + "github.com/checkpoint-restore/go-criu/v5" + "github.com/checkpoint-restore/go-criu/v5/rpc" + + "google.golang.org/protobuf/proto" +) + +// CheckForCriu uses CRIU's go bindings to check if the CRIU +// binary exists and if it at least the version Podman needs. +func CheckForCriu(version int) bool { + c := criu.MakeCriu() + result, err := c.IsCriuAtLeast(version) + if err != nil { + return false + } + return result +} + +func MemTrack() bool { + features, err := criu.MakeCriu().FeatureCheck( + &rpc.CriuFeatures{ + MemTrack: proto.Bool(true), + }, + ) + if err != nil { + return false + } + + if features == nil || features.MemTrack == nil { + return false + } + + return *features.MemTrack +} + +func GetCriuVersion() (int, error) { + c := criu.MakeCriu() + return c.GetCriuVersion() +} diff --git a/pkg/criu/criu_unsupported.go b/pkg/criu/criu_unsupported.go index 3e3ed9c6c..437482a0e 100644 --- a/pkg/criu/criu_unsupported.go +++ b/pkg/criu/criu_unsupported.go @@ -3,6 +3,14 @@ package criu +func CheckForCriu(version int) bool { + return false +} + func MemTrack() bool { return false } + +func GetCriuVersion() (int, error) { + return MinCriuVersion, nil +} diff --git a/pkg/ctime/ctime_linux.go b/pkg/ctime/ctime_linux.go index 7eb3caa6d..bf3cd5752 100644 --- a/pkg/ctime/ctime_linux.go +++ b/pkg/ctime/ctime_linux.go @@ -11,6 +11,6 @@ import ( func created(fi os.FileInfo) time.Time { st := fi.Sys().(*syscall.Stat_t) - //nolint + //nolint:unconvert // need to type cast on some cpu architectures return time.Unix(int64(st.Ctim.Sec), int64(st.Ctim.Nsec)) } diff --git a/pkg/domain/entities/containers.go b/pkg/domain/entities/containers.go index 750f49590..17408f12f 100644 --- a/pkg/domain/entities/containers.go +++ b/pkg/domain/entities/containers.go @@ -56,7 +56,7 @@ type WaitOptions struct { } type WaitReport struct { - Id string //nolint + Id string //nolint:revive,stylecheck Error error ExitCode int32 } @@ -76,7 +76,7 @@ type PauseUnPauseOptions struct { type PauseUnpauseReport struct { Err error - Id string //nolint + Id string //nolint:revive,stylecheck } type StopOptions struct { @@ -88,7 +88,7 @@ type StopOptions struct { type StopReport struct { Err error - Id string //nolint + Id string //nolint:revive,stylecheck RawInput string } @@ -110,7 +110,7 @@ type KillOptions struct { type KillReport struct { Err error - Id string //nolint + Id string //nolint:revive,stylecheck RawInput string } @@ -123,7 +123,7 @@ type RestartOptions struct { type RestartReport struct { Err error - Id string //nolint + Id string //nolint:revive,stylecheck } type RmOptions struct { @@ -170,7 +170,7 @@ type CopyOptions struct { } type CommitReport struct { - Id string //nolint + Id string //nolint:revive,stylecheck } type ContainerExportOptions struct { @@ -196,7 +196,7 @@ type CheckpointOptions struct { type CheckpointReport struct { Err error `json:"-"` - Id string `json:"Id` //nolint + Id string `json:"Id"` //nolint:revive,stylecheck RuntimeDuration int64 `json:"runtime_checkpoint_duration"` CRIUStatistics *define.CRIUCheckpointRestoreStatistics `json:"criu_statistics"` } @@ -222,13 +222,13 @@ type RestoreOptions struct { type RestoreReport struct { Err error `json:"-"` - Id string `json:"Id` //nolint + Id string `json:"Id"` //nolint:revive,stylecheck RuntimeDuration int64 `json:"runtime_restore_duration"` CRIUStatistics *define.CRIUCheckpointRestoreStatistics `json:"criu_statistics"` } type ContainerCreateReport struct { - Id string //nolint + Id string //nolint:revive,stylecheck } // AttachOptions describes the cli and other values @@ -307,7 +307,7 @@ type ContainerStartOptions struct { // ContainerStartReport describes the response from starting // containers from the cli type ContainerStartReport struct { - Id string //nolint + Id string //nolint:revive,stylecheck RawInput string Err error ExitCode int @@ -351,7 +351,7 @@ type ContainerRunOptions struct { // a container type ContainerRunReport struct { ExitCode int - Id string //nolint + Id string //nolint:revive,stylecheck } // ContainerCleanupOptions are the CLI values for the @@ -368,7 +368,7 @@ type ContainerCleanupOptions struct { // container cleanup type ContainerCleanupReport struct { CleanErr error - Id string //nolint + Id string //nolint:revive,stylecheck RmErr error RmiErr error } @@ -384,7 +384,7 @@ type ContainerInitOptions struct { // container init type ContainerInitReport struct { Err error - Id string //nolint + Id string //nolint:revive,stylecheck } // ContainerMountOptions describes the input values for mounting containers @@ -406,7 +406,7 @@ type ContainerUnmountOptions struct { // ContainerMountReport describes the response from container mount type ContainerMountReport struct { Err error - Id string //nolint + Id string //nolint:revive,stylecheck Name string Path string } @@ -414,7 +414,7 @@ type ContainerMountReport struct { // ContainerUnmountReport describes the response from umounting a container type ContainerUnmountReport struct { Err error - Id string //nolint + Id string //nolint:revive,stylecheck } // ContainerPruneOptions describes the options needed @@ -433,7 +433,7 @@ type ContainerPortOptions struct { // ContainerPortReport describes the output needed for // the CLI to output ports type ContainerPortReport struct { - Id string //nolint + Id string //nolint:revive,stylecheck Ports []nettypes.PortMapping } diff --git a/pkg/domain/entities/engine_container.go b/pkg/domain/entities/engine_container.go index 6b70a3452..df42876f6 100644 --- a/pkg/domain/entities/engine_container.go +++ b/pkg/domain/entities/engine_container.go @@ -71,6 +71,7 @@ type ContainerEngine interface { PlayKube(ctx context.Context, body io.Reader, opts PlayKubeOptions) (*PlayKubeReport, error) PlayKubeDown(ctx context.Context, body io.Reader, opts PlayKubeDownOptions) (*PlayKubeReport, error) PodCreate(ctx context.Context, specg PodSpec) (*PodCreateReport, error) + PodClone(ctx context.Context, podClone PodCloneOptions) (*PodCloneReport, error) PodExists(ctx context.Context, nameOrID string) (*BoolReport, error) PodInspect(ctx context.Context, options PodInspectOptions) (*PodInspectReport, error) PodKill(ctx context.Context, namesOrIds []string, options PodKillOptions) ([]*PodKillReport, error) diff --git a/pkg/domain/entities/images.go b/pkg/domain/entities/images.go index 2bb4ceb5b..11f6e8687 100644 --- a/pkg/domain/entities/images.go +++ b/pkg/domain/entities/images.go @@ -46,14 +46,14 @@ type Image struct { HealthCheck *manifest.Schema2HealthConfig `json:",omitempty"` } -func (i *Image) Id() string { // nolint +func (i *Image) Id() string { //nolint:revive,stylecheck return i.ID } // swagger:model LibpodImageSummary type ImageSummary struct { ID string `json:"Id"` - ParentId string // nolint + ParentId string //nolint:revive,stylecheck RepoTags []string RepoDigests []string Created int64 @@ -71,7 +71,7 @@ type ImageSummary struct { History []string `json:",omitempty"` } -func (i *ImageSummary) Id() string { // nolint +func (i *ImageSummary) Id() string { //nolint:revive,stylecheck return i.ID } @@ -290,7 +290,7 @@ type ImageImportOptions struct { } type ImageImportReport struct { - Id string // nolint + Id string //nolint:revive,stylecheck } // ImageSaveOptions provide options for saving images. @@ -397,7 +397,7 @@ type ImageUnmountOptions struct { // ImageMountReport describes the response from image mount type ImageMountReport struct { - Id string // nolint + Id string //nolint:revive,stylecheck Name string Repositories []string Path string @@ -406,5 +406,5 @@ type ImageMountReport struct { // ImageUnmountReport describes the response from umounting an image type ImageUnmountReport struct { Err error - Id string // nolint + Id string //nolint:revive,stylecheck } diff --git a/pkg/domain/entities/network.go b/pkg/domain/entities/network.go index 0f901c7f1..d375c2e20 100644 --- a/pkg/domain/entities/network.go +++ b/pkg/domain/entities/network.go @@ -22,7 +22,7 @@ type NetworkReloadOptions struct { // NetworkReloadReport describes the results of reloading a container network. type NetworkReloadReport struct { - // nolint:stylecheck,revive + //nolint:stylecheck,revive Id string Err error } diff --git a/pkg/domain/entities/pods.go b/pkg/domain/entities/pods.go index 9cbbe2bf1..14ce370c1 100644 --- a/pkg/domain/entities/pods.go +++ b/pkg/domain/entities/pods.go @@ -20,15 +20,15 @@ type PodKillOptions struct { type PodKillReport struct { Errs []error - Id string // nolint + Id string //nolint:revive,stylecheck } type ListPodsReport struct { Cgroup string Containers []*ListPodContainer Created time.Time - Id string // nolint - InfraId string // nolint + Id string //nolint:revive,stylecheck + InfraId string //nolint:revive,stylecheck Name string Namespace string // Network names connected to infra container @@ -38,7 +38,7 @@ type ListPodsReport struct { } type ListPodContainer struct { - Id string // nolint + Id string //nolint:revive,stylecheck Names string Status string } @@ -50,7 +50,7 @@ type PodPauseOptions struct { type PodPauseReport struct { Errs []error - Id string // nolint + Id string //nolint:revive,stylecheck } type PodunpauseOptions struct { @@ -60,7 +60,7 @@ type PodunpauseOptions struct { type PodUnpauseReport struct { Errs []error - Id string // nolint + Id string //nolint:revive,stylecheck } type PodStopOptions struct { @@ -72,7 +72,7 @@ type PodStopOptions struct { type PodStopReport struct { Errs []error - Id string // nolint + Id string //nolint:revive,stylecheck } type PodRestartOptions struct { @@ -82,7 +82,7 @@ type PodRestartOptions struct { type PodRestartReport struct { Errs []error - Id string // nolint + Id string //nolint:revive,stylecheck } type PodStartOptions struct { @@ -92,7 +92,7 @@ type PodStartOptions struct { type PodStartReport struct { Errs []error - Id string // nolint + Id string //nolint:revive,stylecheck } type PodRmOptions struct { @@ -105,7 +105,7 @@ type PodRmOptions struct { type PodRmReport struct { Err error - Id string // nolint + Id string //nolint:revive,stylecheck } // PddSpec is an abstracted version of PodSpecGen designed to eventually accept options @@ -154,6 +154,16 @@ type PodLogsOptions struct { Color bool } +// PodCloneOptions contains options for cloning an existing pod +type PodCloneOptions struct { + ID string + Destroy bool + CreateOpts PodCreateOptions + InfraOptions ContainerCreateOptions + PerContainerOptions ContainerCreateOptions + Start bool +} + type ContainerCreateOptions struct { Annotation []string Attach []string @@ -287,7 +297,11 @@ func NewInfraContainerCreateOptions() ContainerCreateOptions { } type PodCreateReport struct { - Id string // nolint + Id string //nolint:revive,stylecheck +} + +type PodCloneReport struct { + Id string //nolint:revive,stylecheck } func (p *PodCreateOptions) CPULimits() *specs.LinuxCPU { @@ -389,7 +403,7 @@ type PodPruneOptions struct { type PodPruneReport struct { Err error - Id string // nolint + Id string //nolint:revive,stylecheck } type PodTopOptions struct { diff --git a/pkg/domain/entities/reports/containers.go b/pkg/domain/entities/reports/containers.go index 54bcd092b..db9a66012 100644 --- a/pkg/domain/entities/reports/containers.go +++ b/pkg/domain/entities/reports/containers.go @@ -1,7 +1,7 @@ package reports type RmReport struct { - Id string `json:"Id"` //nolint + Id string `json:"Id"` //nolint:revive,stylecheck Err error `json:"Err,omitempty"` } diff --git a/pkg/domain/entities/reports/prune.go b/pkg/domain/entities/reports/prune.go index 497e5d606..ac3d8e7ce 100644 --- a/pkg/domain/entities/reports/prune.go +++ b/pkg/domain/entities/reports/prune.go @@ -1,7 +1,7 @@ package reports type PruneReport struct { - Id string `json:"Id"` //nolint + Id string `json:"Id"` //nolint:revive,stylecheck Err error `json:"Err,omitempty"` Size uint64 `json:"Size"` } diff --git a/pkg/domain/entities/system.go b/pkg/domain/entities/system.go index 21026477d..331d2bcdc 100644 --- a/pkg/domain/entities/system.go +++ b/pkg/domain/entities/system.go @@ -28,6 +28,7 @@ type SystemPruneReport struct { PodPruneReport []*PodPruneReport ContainerPruneReports []*reports.PruneReport ImagePruneReports []*reports.PruneReport + NetworkPruneReports []*reports.PruneReport VolumePruneReports []*reports.PruneReport ReclaimedSpace uint64 } diff --git a/pkg/domain/entities/types.go b/pkg/domain/entities/types.go index 3e6e54e7d..44df66498 100644 --- a/pkg/domain/entities/types.go +++ b/pkg/domain/entities/types.go @@ -21,7 +21,7 @@ type Volume struct { } type Report struct { - Id []string // nolint + Id []string //nolint:revive,stylecheck Err map[string]error } diff --git a/pkg/domain/entities/volumes.go b/pkg/domain/entities/volumes.go index 84f85b83f..556df16c1 100644 --- a/pkg/domain/entities/volumes.go +++ b/pkg/domain/entities/volumes.go @@ -33,7 +33,7 @@ type VolumeRmOptions struct { type VolumeRmReport struct { Err error - Id string // nolint + Id string //nolint:revive,stylecheck } type VolumeInspectReport struct { @@ -61,7 +61,7 @@ type VolumeListReport struct { // VolumeMountReport describes the response from volume mount type VolumeMountReport struct { Err error - Id string // nolint + Id string //nolint:revive,stylecheck Name string Path string } @@ -69,5 +69,5 @@ type VolumeMountReport struct { // VolumeUnmountReport describes the response from umounting a volume type VolumeUnmountReport struct { Err error - Id string // nolint + Id string //nolint:revive,stylecheck } diff --git a/pkg/domain/infra/abi/containers.go b/pkg/domain/infra/abi/containers.go index 8bd84a310..281e448f6 100644 --- a/pkg/domain/infra/abi/containers.go +++ b/pkg/domain/infra/abi/containers.go @@ -16,7 +16,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" @@ -38,7 +37,7 @@ import ( ) // 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{} @@ -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 } @@ -488,7 +487,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, errors.Wrap(err, "unable to look up requested container") } // Run Top. @@ -635,13 +634,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) @@ -939,6 +938,7 @@ func (ic *ContainerEngine) ContainerStart(ctx context.Context, namesOrIds []stri } return reports, errors.Wrapf(err, "unable to start container %s", ctr.ID()) } + exitCode = ic.GetContainerExitCode(ctx, ctr) reports = append(reports, &entities.ContainerStartReport{ Id: ctr.ID(), @@ -1099,25 +1099,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 +1180,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 = errors.Wrapf(err, "failed to clean up and remove container %v", ctr.ID()) } } else { err := ctr.Cleanup(ctx) if err != nil { - report.CleanErr = errors.Wrapf(err, "failed to cleanup container %v", ctr.ID()) + report.CleanErr = errors.Wrapf(err, "failed to clean up container %v", ctr.ID()) } } @@ -1593,6 +1579,11 @@ func (ic *ContainerEngine) ContainerClone(ctx context.Context, ctrCloneOpts enti return nil, err } + conf := c.Config() + if conf.Spec != nil && conf.Spec.Process != nil && conf.Spec.Process.Terminal { // if we do not pass term, running ctrs exit + spec.Terminal = true + } + // Print warnings if len(out) > 0 { for _, w := range out { @@ -1612,8 +1603,8 @@ func (ic *ContainerEngine) ContainerClone(ctx context.Context, ctrCloneOpts enti switch { case strings.Contains(n, "-clone"): ind := strings.Index(n, "-clone") + 6 - num, _ := strconv.Atoi(n[ind:]) - if num == 0 { // clone1 is hard to get with this logic, just check for it here. + num, err := strconv.Atoi(n[ind:]) + if num == 0 && err != nil { // clone1 is hard to get with this logic, just check for it here. _, err = ic.Libpod.LookupContainer(n + "1") if err != nil { spec.Name = n + "1" diff --git a/pkg/domain/infra/abi/images.go b/pkg/domain/infra/abi/images.go index d469fa0ca..d63de2424 100644 --- a/pkg/domain/infra/abi/images.go +++ b/pkg/domain/infra/abi/images.go @@ -593,7 +593,7 @@ func (ir *ImageEngine) Remove(ctx context.Context, images []string, opts entitie rmErrors = libimageErrors - return //nolint + return } // Shutdown Libpod engine diff --git a/pkg/domain/infra/abi/network.go b/pkg/domain/infra/abi/network.go index 47f7917f4..8b95607f4 100644 --- a/pkg/domain/infra/abi/network.go +++ b/pkg/domain/infra/abi/network.go @@ -2,6 +2,7 @@ package abi import ( "context" + "strconv" "github.com/containers/common/libnetwork/types" netutil "github.com/containers/common/libnetwork/util" @@ -12,10 +13,39 @@ import ( ) 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, errors.Errorf("got no values for filter key \"dangling\"") + case 1: + var err error + wantDangling, err = strconv.ParseBool(val[0]) + if err != nil { + return nil, errors.Errorf("invalid dangling filter value \"%v\"", val[0]) + } + delete(options.Filters, "dangling") + default: + return nil, errors.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 } @@ -142,8 +172,35 @@ func (ic *ContainerEngine) NetworkExists(ctx context.Context, networkname string }, nil } -// Network prune removes unused cni networks +// 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 +220,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/play.go b/pkg/domain/infra/abi/play.go index e04ab3a1a..e14a819fa 100644 --- a/pkg/domain/infra/abi/play.go +++ b/pkg/domain/infra/abi/play.go @@ -31,7 +31,7 @@ import ( "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 @@ -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 @@ -804,7 +804,7 @@ func splitMultiDocYAML(yamlContent []byte) ([][]byte, error) { 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") } diff --git a/pkg/domain/infra/abi/pods.go b/pkg/domain/infra/abi/pods.go index 32deb20e0..3e9cb7f5e 100644 --- a/pkg/domain/infra/abi/pods.go +++ b/pkg/domain/infra/abi/pods.go @@ -2,12 +2,15 @@ package abi import ( "context" + "strconv" + "strings" "github.com/containers/podman/v4/libpod" "github.com/containers/podman/v4/libpod/define" "github.com/containers/podman/v4/pkg/domain/entities" dfilters "github.com/containers/podman/v4/pkg/domain/filters" "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" @@ -295,6 +298,88 @@ func (ic *ContainerEngine) PodCreate(ctx context.Context, specg entities.PodSpec return &entities.PodCreateReport{Id: pod.ID()}, nil } +func (ic *ContainerEngine) PodClone(ctx context.Context, podClone entities.PodCloneOptions) (*entities.PodCloneReport, error) { + spec := specgen.NewPodSpecGenerator() + p, err := generate.PodConfigToSpec(ic.Libpod, spec, &podClone.InfraOptions, podClone.ID) + if err != nil { + return nil, err + } + + if len(podClone.CreateOpts.Name) > 0 { + spec.Name = podClone.CreateOpts.Name + } else { + n := p.Name() + _, err := ic.Libpod.LookupPod(n + "-clone") + if err == nil { + n += "-clone" + } + switch { + case strings.Contains(n, "-clone"): // meaning this name is taken! + ind := strings.Index(n, "-clone") + 6 + num, err := strconv.Atoi(n[ind:]) + if num == 0 && err != nil { // meaning invalid + _, err = ic.Libpod.LookupPod(n + "1") + if err != nil { + spec.Name = n + "1" + break + } + } else { // else we already have a number + n = n[0:ind] + } + err = nil + count := num + for err == nil { // until we cannot find a pod w/ this name, increment num and try again + count++ + tempN := n + strconv.Itoa(count) + _, err = ic.Libpod.LookupPod(tempN) + } + n += strconv.Itoa(count) + spec.Name = n + default: + spec.Name = p.Name() + "-clone" + } + } + + podSpec := entities.PodSpec{PodSpecGen: *spec} + pod, err := generate.MakePod(&podSpec, ic.Libpod) + if err != nil { + return nil, err + } + + ctrs, err := p.AllContainers() + if err != nil { + return nil, err + } + for _, ctr := range ctrs { + if ctr.IsInfra() { + continue // already copied infra + } + + podClone.PerContainerOptions.Pod = pod.ID() + _, err := ic.ContainerClone(ctx, entities.ContainerCloneOptions{ID: ctr.ID(), CreateOpts: podClone.PerContainerOptions}) + if err != nil { + return nil, err + } + } + + if podClone.Destroy { + var timeout *uint + err = ic.Libpod.RemovePod(ctx, p, true, true, timeout) + if err != nil { + return &entities.PodCloneReport{Id: pod.ID()}, err + } + } + + if podClone.Start { + _, err := ic.PodStart(ctx, []string{pod.ID()}, entities.PodStartOptions{}) + if err != nil { + return &entities.PodCloneReport{Id: pod.ID()}, err + } + } + + return &entities.PodCloneReport{Id: pod.ID()}, nil +} + func (ic *ContainerEngine) PodTop(ctx context.Context, options entities.PodTopOptions) (*entities.StringSliceReport, error) { var ( pod *libpod.Pod @@ -308,7 +393,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, errors.Wrap(err, "unable to look up requested container") } // Run Top. @@ -409,7 +494,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, errors.Wrap(err, "unable to look up requested container") } inspect, err := pod.Inspect() if err != nil { diff --git a/pkg/domain/infra/abi/system.go b/pkg/domain/infra/abi/system.go index 762f0d79a..6e26026d4 100644 --- a/pkg/domain/infra/abi/system.go +++ b/pkg/domain/infra/abi/system.go @@ -125,8 +125,14 @@ 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")) os.Exit(1) @@ -137,7 +143,7 @@ func (ic *ContainerEngine) SetupRootless(_ context.Context, noMoveProcess bool) return nil } -// SystemPrune removes unused data from the system. Pruning pods, containers, volumes and images. +// SystemPrune removes unused data from the system. Pruning pods, containers, networks, volumes and images. func (ic *ContainerEngine) SystemPrune(ctx context.Context, options entities.SystemPruneOptions) (*entities.SystemPruneReport, error) { var systemPruneReport = new(entities.SystemPruneReport) filters := []string{} @@ -148,6 +154,9 @@ func (ic *ContainerEngine) SystemPrune(ctx context.Context, options entities.Sys found := true for found { found = false + + // TODO: Figure out cleaner way to handle all of the different PruneOptions + // Remove all unused pods. podPruneReport, err := ic.prunePodHelper(ctx) if err != nil { return nil, err @@ -155,9 +164,10 @@ func (ic *ContainerEngine) SystemPrune(ctx context.Context, options entities.Sys if len(podPruneReport) > 0 { found = true } + systemPruneReport.PodPruneReport = append(systemPruneReport.PodPruneReport, podPruneReport...) - // TODO: Figure out cleaner way to handle all of the different PruneOptions + // Remove all unused containers. containerPruneOptions := entities.ContainerPruneOptions{} containerPruneOptions.Filters = (url.Values)(options.Filters) @@ -165,16 +175,18 @@ func (ic *ContainerEngine) SystemPrune(ctx context.Context, options entities.Sys if err != nil { return nil, err } + reclaimedSpace += reports.PruneReportsSize(containerPruneReports) systemPruneReport.ContainerPruneReports = append(systemPruneReport.ContainerPruneReports, containerPruneReports...) + + // Remove all unused images. imagePruneOptions := entities.ImagePruneOptions{ All: options.All, Filter: filters, } + imageEngine := ImageEngine{Libpod: ic.Libpod} imagePruneReports, err := imageEngine.Prune(ctx, imagePruneOptions) - reclaimedSpace += reports.PruneReportsSize(imagePruneReports) - if err != nil { return nil, err } @@ -182,10 +194,33 @@ func (ic *ContainerEngine) SystemPrune(ctx context.Context, options entities.Sys found = true } + reclaimedSpace += reports.PruneReportsSize(imagePruneReports) systemPruneReport.ImagePruneReports = append(systemPruneReport.ImagePruneReports, imagePruneReports...) + + // Remove all unused networks. + networkPruneOptions := entities.NetworkPruneOptions{} + networkPruneOptions.Filters = options.Filters + + networkPruneReport, err := ic.NetworkPrune(ctx, networkPruneOptions) + if err != nil { + return nil, err + } + if len(networkPruneReport) > 0 { + found = true + } + for _, net := range networkPruneReport { + systemPruneReport.NetworkPruneReports = append(systemPruneReport.NetworkPruneReports, &reports.PruneReport{ + Id: net.Name, + Err: net.Error, + Size: 0, + }) + } + + // Remove unused volume data. if options.Volume { volumePruneOptions := entities.VolumePruneOptions{} volumePruneOptions.Filters = (url.Values)(options.Filters) + volumePruneReport, err := ic.VolumePrune(ctx, volumePruneOptions) if err != nil { return nil, err @@ -193,6 +228,7 @@ func (ic *ContainerEngine) SystemPrune(ctx context.Context, options entities.Sys if len(volumePruneReport) > 0 { found = true } + reclaimedSpace += reports.PruneReportsSize(volumePruneReport) systemPruneReport.VolumePruneReports = append(systemPruneReport.VolumePruneReports, volumePruneReport...) } @@ -368,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 fe2c268c0..e02c0532c 100644 --- a/pkg/domain/infra/abi/terminal/sigproxy_linux.go +++ b/pkg/domain/infra/abi/terminal/sigproxy_linux.go @@ -20,7 +20,7 @@ const signalBufferSize = 2048 func ProxySignals(ctr *libpod.Container) { // Stop catching the shutdown signals (SIGINT, SIGTERM) - they're going // to the container now. - shutdown.Stop() // nolint: errcheck + shutdown.Stop() //nolint: errcheck sigBuffer := make(chan os.Signal, signalBufferSize) signal.CatchAll(sigBuffer) diff --git a/pkg/domain/infra/abi/volumes.go b/pkg/domain/infra/abi/volumes.go index f59f11e20..a9c53c140 100644 --- a/pkg/domain/infra/abi/volumes.go +++ b/pkg/domain/infra/abi/volumes.go @@ -172,7 +172,7 @@ func (ic *ContainerEngine) VolumeMounted(ctx context.Context, nameOrID string) ( mountCount, err := vol.MountCount() if err != nil { // FIXME: this error should probably be returned - return &entities.BoolReport{Value: false}, nil // nolint: nilerr + return &entities.BoolReport{Value: false}, nil //nolint: nilerr } if mountCount > 0 { return &entities.BoolReport{Value: true}, 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 b68bc46d4..fb0be629c 100644 --- a/pkg/domain/infra/tunnel/containers.go +++ b/pkg/domain/infra/tunnel/containers.go @@ -570,7 +570,7 @@ func (ic *ContainerEngine) ContainerExecDetached(ctx context.Context, nameOrID s return sessionID, nil } -func startAndAttach(ic *ContainerEngine, name string, detachKeys *string, input, output, errput *os.File) error { //nolint +func startAndAttach(ic *ContainerEngine, name string, detachKeys *string, input, output, errput *os.File) error { attachErr := make(chan error) attachReady := make(chan bool) options := new(containers.AttachOptions).WithStream(true) @@ -863,7 +863,7 @@ func (ic *ContainerEngine) ContainerRun(ctx context.Context, opts entities.Conta if eventsErr != nil || lastEvent == nil { logrus.Errorf("Cannot get exit code: %v", err) report.ExitCode = define.ExecErrorCodeNotFound - return &report, nil // nolint: nilerr + return &report, nil //nolint: nilerr } report.ExitCode = lastEvent.ContainerExitCode 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/pods.go b/pkg/domain/infra/tunnel/pods.go index 2dbdfcf80..7b1fa231f 100644 --- a/pkg/domain/infra/tunnel/pods.go +++ b/pkg/domain/infra/tunnel/pods.go @@ -195,6 +195,10 @@ func (ic *ContainerEngine) PodCreate(ctx context.Context, specg entities.PodSpec return pods.CreatePodFromSpec(ic.ClientCtx, &specg) } +func (ic *ContainerEngine) PodClone(ctx context.Context, podClone entities.PodCloneOptions) (*entities.PodCloneReport, error) { + return nil, nil +} + func (ic *ContainerEngine) PodTop(ctx context.Context, opts entities.PodTopOptions) (*entities.StringSliceReport, error) { switch { case opts.Latest: diff --git a/pkg/errorhandling/errorhandling.go b/pkg/errorhandling/errorhandling.go index 6ee1e7e86..fc6772c08 100644 --- a/pkg/errorhandling/errorhandling.go +++ b/pkg/errorhandling/errorhandling.go @@ -86,7 +86,7 @@ func Contains(err error, sub error) bool { // PodConflictErrorModel is used in remote connections with podman type PodConflictErrorModel struct { Errs []string - Id string // nolint + Id string //nolint:revive,stylecheck } // ErrorModel is used in remote connections with podman diff --git a/pkg/hooks/exec/runtimeconfigfilter_test.go b/pkg/hooks/exec/runtimeconfigfilter_test.go index f4b6cf86a..5c13a76e1 100644 --- a/pkg/hooks/exec/runtimeconfigfilter_test.go +++ b/pkg/hooks/exec/runtimeconfigfilter_test.go @@ -13,7 +13,7 @@ import ( ) func TestRuntimeConfigFilter(t *testing.T) { - unexpectedEndOfJSONInput := json.Unmarshal([]byte("{\n"), nil) //nolint + unexpectedEndOfJSONInput := json.Unmarshal([]byte("{\n"), nil) //nolint:govet // this should force the error fileMode := os.FileMode(0600) rootUint32 := uint32(0) binUser := int(1) diff --git a/pkg/k8s.io/apimachinery/pkg/api/resource/quantity.go b/pkg/k8s.io/apimachinery/pkg/api/resource/quantity.go index dcc5df219..588a189bf 100644 --- a/pkg/k8s.io/apimachinery/pkg/api/resource/quantity.go +++ b/pkg/k8s.io/apimachinery/pkg/api/resource/quantity.go @@ -138,7 +138,6 @@ const ( var ( // Errors that could happen while parsing a string. - //nolint:revive ErrFormatWrong = errors.New("quantities must match the regular expression '" + splitREString + "'") ErrNumeric = errors.New("unable to parse numeric part of quantity") ErrSuffix = errors.New("unable to parse quantity's suffix") @@ -258,7 +257,7 @@ Suffix: // we encountered a non decimal in the Suffix loop, but the last character // was not a valid exponent err = ErrFormatWrong - // nolint:nakedret + //nolint:nakedret return } @@ -579,9 +578,9 @@ func (q Quantity) MarshalJSON() ([]byte, error) { // if CanonicalizeBytes needed more space than our slice provided, we may need to allocate again so use // append result = result[:1] - result = append(result, number...) // nolint: makezero - result = append(result, suffix...) // nolint: makezero - result = append(result, '"') // nolint: makezero + result = append(result, number...) //nolint: makezero + result = append(result, suffix...) //nolint: makezero + result = append(result, '"') //nolint: makezero return result, nil } diff --git a/pkg/machine/e2e/list_test.go b/pkg/machine/e2e/list_test.go index 1c8c6ac81..e2121e7bf 100644 --- a/pkg/machine/e2e/list_test.go +++ b/pkg/machine/e2e/list_test.go @@ -29,7 +29,7 @@ var _ = Describe("podman machine list", func() { firstList, err := mb.setCmd(list).run() Expect(err).NotTo(HaveOccurred()) Expect(firstList).Should(Exit(0)) - Expect(len(firstList.outputToStringSlice())).To(Equal(1)) // just the header + Expect(firstList.outputToStringSlice()).To(HaveLen(1)) // just the header i := new(initMachine) session, err := mb.setCmd(i.withImagePath(mb.imagePath)).run() @@ -39,7 +39,7 @@ var _ = Describe("podman machine list", func() { secondList, err := mb.setCmd(list).run() Expect(err).NotTo(HaveOccurred()) Expect(secondList).To(Exit(0)) - Expect(len(secondList.outputToStringSlice())).To(Equal(2)) // one machine and the header + Expect(secondList.outputToStringSlice()).To(HaveLen(2)) // one machine and the header }) It("list machines with quiet or noheading", func() { @@ -51,12 +51,12 @@ var _ = Describe("podman machine list", func() { firstList, err := mb.setCmd(list.withQuiet()).run() Expect(err).NotTo(HaveOccurred()) Expect(firstList).Should(Exit(0)) - Expect(len(firstList.outputToStringSlice())).To(Equal(0)) // No header with quiet + Expect(firstList.outputToStringSlice()).To(HaveLen(0)) // No header with quiet noheaderSession, err := mb.setCmd(list.withNoHeading()).run() // noheader Expect(err).NotTo(HaveOccurred()) Expect(noheaderSession).Should(Exit(0)) - Expect(len(noheaderSession.outputToStringSlice())).To(Equal(0)) + Expect(noheaderSession.outputToStringSlice()).To(HaveLen(0)) i := new(initMachine) session, err := mb.setName(name1).setCmd(i.withImagePath(mb.imagePath)).run() @@ -70,7 +70,7 @@ var _ = Describe("podman machine list", func() { secondList, err := mb.setCmd(list.withQuiet()).run() Expect(err).NotTo(HaveOccurred()) Expect(secondList).To(Exit(0)) - Expect(len(secondList.outputToStringSlice())).To(Equal(2)) // two machines, no header + Expect(secondList.outputToStringSlice()).To(HaveLen(2)) // two machines, no header listNames := secondList.outputToStringSlice() stripAsterisk(listNames) @@ -116,10 +116,10 @@ var _ = Describe("podman machine list", func() { // go format list := new(listMachine) - listSession, err := mb.setCmd(list.withFormat("{{.Name}}").withNoHeading()).run() + listSession, err := mb.setCmd(list.withFormat("{{.Name}}")).run() Expect(err).NotTo(HaveOccurred()) Expect(listSession).To(Exit(0)) - Expect(len(listSession.outputToStringSlice())).To(Equal(1)) + Expect(listSession.outputToStringSlice()).To(HaveLen(1)) listNames := listSession.outputToStringSlice() stripAsterisk(listNames) @@ -135,6 +135,15 @@ var _ = Describe("podman machine list", func() { var listResponse []*machine.ListReporter err = jsoniter.Unmarshal(listSession.Bytes(), &listResponse) Expect(err).To(BeNil()) + + // table format includes the header + list = new(listMachine) + listSession3, err3 := mb.setCmd(list.withFormat("table {{.Name}}")).run() + Expect(err3).NotTo(HaveOccurred()) + Expect(listSession3).To(Exit(0)) + listNames3 := listSession3.outputToStringSlice() + Expect(listNames3).To(HaveLen(2)) + Expect(listNames3).To(ContainSubstring("NAME")) }) }) diff --git a/pkg/machine/fcos.go b/pkg/machine/fcos.go index 77427139a..59ef6d975 100644 --- a/pkg/machine/fcos.go +++ b/pkg/machine/fcos.go @@ -139,7 +139,7 @@ func getStreamURL(streamType string) url2.URL { // This should get Exported and stay put as it will apply to all fcos downloads // getFCOS parses fedoraCoreOS's stream and returns the image download URL and the release version -func GetFCOSDownload(imageStream string) (*FcosDownloadInfo, error) { //nolint:staticcheck +func GetFCOSDownload(imageStream string) (*FcosDownloadInfo, error) { var ( fcosstable stream.Stream altMeta release.Release diff --git a/pkg/machine/ignition.go b/pkg/machine/ignition.go index 35a9a30cb..f4602cc95 100644 --- a/pkg/machine/ignition.go +++ b/pkg/machine/ignition.go @@ -93,7 +93,7 @@ func NewIgnitionFile(ign DynamicIgnition) error { tz string ) // local means the same as the host - // lookup where it is pointing to on the host + // look up where it is pointing to on the host if ign.TimeZone == "local" { tz, err = getLocalTimeZone() if err != nil { @@ -348,7 +348,7 @@ Delegate=memory pids cpu io }, }) - // Setup /etc/subuid and /etc/subgid + // Set up /etc/subuid and /etc/subgid for _, sub := range []string{"/etc/subuid", "/etc/subgid"} { files = append(files, File{ Node: Node{ diff --git a/pkg/machine/keys.go b/pkg/machine/keys.go index 15c1f73d8..45d9801cc 100644 --- a/pkg/machine/keys.go +++ b/pkg/machine/keys.go @@ -59,7 +59,16 @@ func generatekeysPrefix(dir string, file string, passThru bool, prefix ...string args := append([]string{}, prefix[1:]...) args = append(args, sshCommand...) args = append(args, file) - cmd := exec.Command(prefix[0], args...) + + binary, err := exec.LookPath(prefix[0]) + if err != nil { + return err + } + binary, err = filepath.Abs(binary) + if err != nil { + return err + } + cmd := exec.Command(binary, args...) cmd.Dir = dir if passThru { cmd.Stdin = os.Stdin diff --git a/pkg/machine/qemu/machine.go b/pkg/machine/qemu/machine.go index 1b0d63986..5094345ea 100644 --- a/pkg/machine/qemu/machine.go +++ b/pkg/machine/qemu/machine.go @@ -16,9 +16,11 @@ import ( "net/url" "os" "os/exec" + "os/signal" "path/filepath" "strconv" "strings" + "syscall" "time" "github.com/containers/common/pkg/config" @@ -207,7 +209,7 @@ func migrateVM(configPath string, config []byte, vm *MachineVM) error { vm.Rootful = old.Rootful vm.UID = old.UID - // Backup the original config file + // Back up the original config file if err := os.Rename(configPath, configPath+".orig"); err != nil { return err } @@ -484,12 +486,26 @@ func (v *MachineVM) Start(name string, _ machine.StartOptions) error { if err := v.writeConfig(); err != nil { return fmt.Errorf("writing JSON file: %w", err) } - defer func() { + doneStarting := func() { v.Starting = false if err := v.writeConfig(); err != nil { logrus.Errorf("Writing JSON file: %v", err) } + } + defer doneStarting() + + c := make(chan os.Signal, 1) + signal.Notify(c, os.Interrupt, syscall.SIGTERM) + go func() { + _, ok := <-c + if !ok { + return + } + doneStarting() + os.Exit(1) }() + defer close(c) + if v.isIncompatible() { logrus.Errorf("machine %q is incompatible with this release of podman and needs to be recreated, starting for recovery only", v.Name) } @@ -564,7 +580,7 @@ func (v *MachineVM) Start(name string, _ machine.StartOptions) error { if !errors.Is(err, os.ErrNotExist) { return err } - // lookup qemu again maybe the path was changed, https://github.com/containers/podman/issues/13394 + // look up qemu again maybe the path was changed, https://github.com/containers/podman/issues/13394 cfg, err := config.Default() if err != nil { return err @@ -770,7 +786,7 @@ func (v *MachineVM) Stop(_ string, _ machine.StopOptions) error { if err := qmpMonitor.Disconnect(); err != nil { // FIXME: this error should probably be returned - return nil // nolint: nilerr + return nil //nolint: nilerr } disconnected = true @@ -1126,7 +1142,7 @@ func (p *Provider) CheckExclusiveActiveVM() (bool, string, error) { } // startHostNetworking runs a binary on the host system that allows users -// to setup port forwarding to the podman virtual machine +// to set up port forwarding to the podman virtual machine func (v *MachineVM) startHostNetworking() (string, apiForwardingState, error) { cfg, err := config.Default() if err != nil { diff --git a/pkg/machine/qemu/options_darwin_arm64.go b/pkg/machine/qemu/options_darwin_arm64.go index 4c954af00..d75237938 100644 --- a/pkg/machine/qemu/options_darwin_arm64.go +++ b/pkg/machine/qemu/options_darwin_arm64.go @@ -4,6 +4,8 @@ import ( "os" "os/exec" "path/filepath" + + "github.com/containers/common/pkg/config" ) var ( @@ -15,8 +17,8 @@ func (v *MachineVM) addArchOptions() []string { opts := []string{ "-accel", "hvf", "-accel", "tcg", - "-cpu", "cortex-a57", - "-M", "virt,highmem=off", + "-cpu", "host", + "-M", "virt,highmem=on", "-drive", "file=" + getEdk2CodeFd("edk2-aarch64-code.fd") + ",if=pflash,format=raw,readonly=on", "-drive", "file=" + ovmfDir + ",if=pflash,format=raw"} return opts @@ -38,6 +40,22 @@ func getOvmfDir(imagePath, vmName string) string { } /* + * When QEmu is installed in a non-default location in the system + * we can use the qemu-system-* binary path to figure the install + * location for Qemu and use it to look for edk2-code-fd + */ +func getEdk2CodeFdPathFromQemuBinaryPath() string { + cfg, err := config.Default() + if err == nil { + execPath, err := cfg.FindHelperBinary(QemuCommand, true) + if err == nil { + return filepath.Clean(filepath.Join(filepath.Dir(execPath), "..", "share", "qemu")) + } + } + return "" +} + +/* * QEmu can be installed in multiple locations on MacOS, especially on * Apple Silicon systems. A build from source will likely install it in * /usr/local/bin, whereas Homebrew package management standard is to @@ -45,6 +63,7 @@ func getOvmfDir(imagePath, vmName string) string { */ func getEdk2CodeFd(name string) string { dirs := []string{ + getEdk2CodeFdPathFromQemuBinaryPath(), "/opt/homebrew/opt/podman/libexec/share/qemu", "/usr/local/share/qemu", "/opt/homebrew/share/qemu", diff --git a/pkg/namespaces/namespaces.go b/pkg/namespaces/namespaces.go index c95f8e275..8eacb8da7 100644 --- a/pkg/namespaces/namespaces.go +++ b/pkg/namespaces/namespaces.go @@ -112,7 +112,7 @@ func (n UsernsMode) IsDefaultValue() bool { return n == "" || n == defaultType } -// GetAutoOptions returns a AutoUserNsOptions with the settings to setup automatically +// GetAutoOptions returns a AutoUserNsOptions with the settings to automatically set up // a user namespace. func (n UsernsMode) GetAutoOptions() (*types.AutoUserNsOptions, error) { parts := strings.SplitN(string(n), ":", 2) diff --git a/pkg/rootless/rootless.go b/pkg/rootless/rootless.go index d7143f549..94535f45e 100644 --- a/pkg/rootless/rootless.go +++ b/pkg/rootless/rootless.go @@ -50,7 +50,7 @@ func TryJoinPauseProcess(pausePidPath string) (bool, int, error) { if err != nil { // It is still failing. We can safely remove it. os.Remove(pausePidPath) - return false, -1, nil // nolint: nilerr + return false, -1, nil //nolint: nilerr } return became, ret, err } diff --git a/pkg/rootless/rootless_linux.c b/pkg/rootless/rootless_linux.c index 94bd40f86..3588313c6 100644 --- a/pkg/rootless/rootless_linux.c +++ b/pkg/rootless/rootless_linux.c @@ -178,7 +178,7 @@ get_cmd_line_args () char *tmp = realloc (buffer, allocated); if (tmp == NULL) return NULL; - buffer = tmp; + buffer = tmp; } } @@ -243,7 +243,7 @@ can_use_shortcut () } if (argv[argc+1] != NULL && (strcmp (argv[argc], "container") == 0 || - strcmp (argv[argc], "image") == 0) && + strcmp (argv[argc], "image") == 0) && (strcmp (argv[argc+1], "mount") == 0 || strcmp (argv[argc+1], "scp") == 0)) { ret = false; @@ -512,7 +512,9 @@ create_pause_process (const char *pause_pid_file_path, char **argv) r = TEMP_FAILURE_RETRY (read (p[0], &b, 1)); close (p[0]); - reexec_in_user_namespace_wait (pid, 0); + r = reexec_in_user_namespace_wait (pid, 0); + if (r != 0) + return -1; return r == 1 && b == '0' ? 0 : -1; } @@ -757,6 +759,7 @@ reexec_userns_join (int pid_to_join, char *pause_pid_file_path) } execvp (argv[0], argv); + fprintf (stderr, "failed to execvp %s: %m\n", argv[0]); _exit (EXIT_FAILURE); } @@ -788,7 +791,10 @@ copy_file_to_fd (const char *file_to_read, int outfd) fd = open (file_to_read, O_RDONLY); if (fd < 0) - return fd; + { + fprintf (stderr, "open `%s`: %m\n", file_to_read); + return fd; + } for (;;) { @@ -796,7 +802,10 @@ copy_file_to_fd (const char *file_to_read, int outfd) r = TEMP_FAILURE_RETRY (read (fd, buf, sizeof buf)); if (r < 0) - return r; + { + fprintf (stderr, "read from `%s`: %m\n", file_to_read); + return r; + } if (r == 0) break; @@ -805,7 +814,10 @@ copy_file_to_fd (const char *file_to_read, int outfd) { w = TEMP_FAILURE_RETRY (write (outfd, &buf[t], r - t)); if (w < 0) - return w; + { + fprintf (stderr, "write file to output fd `%s`: %m\n", file_to_read); + return w; + } t += w; } } diff --git a/pkg/rootless/rootless_linux.go b/pkg/rootless/rootless_linux.go index 5af9a978b..fde621b72 100644 --- a/pkg/rootless/rootless_linux.go +++ b/pkg/rootless/rootless_linux.go @@ -154,7 +154,7 @@ func tryMappingTool(uid bool, pid int, hostID int, mappings []idtools.IDMap) err if output, err := cmd.CombinedOutput(); err != nil { logrus.Errorf("running `%s`: %s", strings.Join(args, " "), output) - errorStr := fmt.Sprintf("cannot setup namespace using %q", path) + errorStr := fmt.Sprintf("cannot set up namespace using %q", path) if isSet, err := unshare.IsSetID(cmd.Path, mode, cap); err != nil { logrus.Errorf("Failed to check for %s on %s: %v", idtype, path, err) } else if !isSet { @@ -182,7 +182,7 @@ func joinUserAndMountNS(pid uint, pausePid string) (bool, int, error) { pidC := C.reexec_userns_join(C.int(pid), cPausePid) if int(pidC) < 0 { - return false, -1, errors.Errorf("cannot re-exec process") + return false, -1, errors.Errorf("cannot re-exec process to join the existing user namespace") } ret := C.reexec_in_user_namespace_wait(pidC, 0) @@ -303,7 +303,7 @@ func becomeRootInUserNS(pausePid, fileToRead string, fileOutput *os.File) (_ boo if retErr != nil && pid > 0 { if err := unix.Kill(pid, unix.SIGKILL); err != nil { if err != unix.ESRCH { - logrus.Errorf("Failed to cleanup process %d: %v", pid, err) + logrus.Errorf("Failed to clean up process %d: %v", pid, err) } } C.reexec_in_user_namespace_wait(C.int(pid), 0) @@ -461,13 +461,8 @@ func BecomeRootInUserNS(pausePid string) (bool, int, error) { // different uidmap and the unprivileged user has no way to read the // file owned by the root in the container. func TryJoinFromFilePaths(pausePidPath string, needNewNamespace bool, paths []string) (bool, int, error) { - if len(paths) == 0 { - return BecomeRootInUserNS(pausePidPath) - } - var lastErr error var pausePid int - foundProcess := false for _, path := range paths { if !needNewNamespace { @@ -479,12 +474,9 @@ func TryJoinFromFilePaths(pausePidPath string, needNewNamespace bool, paths []st pausePid, err = strconv.Atoi(string(data)) if err != nil { - lastErr = errors.Wrapf(err, "cannot parse file %s", path) + lastErr = errors.Wrapf(err, "cannot parse file %q", path) continue } - - lastErr = nil - break } else { r, w, err := os.Pipe() if err != nil { @@ -511,26 +503,29 @@ func TryJoinFromFilePaths(pausePidPath string, needNewNamespace bool, paths []st n, err := r.Read(b) if err != nil { - lastErr = errors.Wrapf(err, "cannot read %s\n", path) + lastErr = errors.Wrapf(err, "cannot read %q", path) continue } pausePid, err = strconv.Atoi(string(b[:n])) - if err == nil && unix.Kill(pausePid, 0) == nil { - foundProcess = true - lastErr = nil - break + if err != nil { + lastErr = err + continue } } - } - if !foundProcess && pausePidPath != "" { - return BecomeRootInUserNS(pausePidPath) + + if pausePid > 0 && unix.Kill(pausePid, 0) == nil { + joined, pid, err := joinUserAndMountNS(uint(pausePid), pausePidPath) + if err == nil { + return joined, pid, nil + } + lastErr = err + } } if lastErr != nil { return false, 0, lastErr } - - return joinUserAndMountNS(uint(pausePid), pausePidPath) + return false, 0, errors.Wrapf(unix.ESRCH, "could not find any running process") } // ReadMappingsProc parses and returns the ID mappings at the specified path. diff --git a/pkg/signal/signal_common.go b/pkg/signal/signal_common.go index fe5a76dae..fc1ecc04d 100644 --- a/pkg/signal/signal_common.go +++ b/pkg/signal/signal_common.go @@ -2,6 +2,8 @@ package signal import ( "fmt" + "os" + "os/signal" "strconv" "strings" "syscall" @@ -39,3 +41,18 @@ func ParseSignalNameOrNumber(rawSignal string) (syscall.Signal, error) { } return -1, fmt.Errorf("invalid signal: %s", basename) } + +// CatchAll catches all signals and relays them to the specified channel. +func CatchAll(sigc chan os.Signal) { + handledSigs := make([]os.Signal, 0, len(SignalMap)) + for _, s := range SignalMap { + handledSigs = append(handledSigs, s) + } + signal.Notify(sigc, handledSigs...) +} + +// StopCatch stops catching the signals and closes the specified channel. +func StopCatch(sigc chan os.Signal) { + signal.Stop(sigc) + close(sigc) +} diff --git a/pkg/signal/signal_linux.go b/pkg/signal/signal_linux.go index a114ea019..5103b6033 100644 --- a/pkg/signal/signal_linux.go +++ b/pkg/signal/signal_linux.go @@ -9,8 +9,6 @@ package signal // NOTE: this package has originally been copied from github.com/docker/docker. import ( - "os" - "os/signal" "syscall" "golang.org/x/sys/unix" @@ -91,18 +89,3 @@ var SignalMap = map[string]syscall.Signal{ "RTMAX-1": sigrtmax - 1, "RTMAX": sigrtmax, } - -// CatchAll catches all signals and relays them to the specified channel. -func CatchAll(sigc chan os.Signal) { - handledSigs := make([]os.Signal, 0, len(SignalMap)) - for _, s := range SignalMap { - handledSigs = append(handledSigs, s) - } - signal.Notify(sigc, handledSigs...) -} - -// StopCatch stops catching the signals and closes the specified channel. -func StopCatch(sigc chan os.Signal) { - signal.Stop(sigc) - close(sigc) -} diff --git a/pkg/signal/signal_linux_mipsx.go b/pkg/signal/signal_linux_mipsx.go index 9021a10e7..cdf9ad4c5 100644 --- a/pkg/signal/signal_linux_mipsx.go +++ b/pkg/signal/signal_linux_mipsx.go @@ -10,8 +10,6 @@ package signal // NOTE: this package has originally been copied from github.com/docker/docker. import ( - "os" - "os/signal" "syscall" "golang.org/x/sys/unix" @@ -92,18 +90,3 @@ var SignalMap = map[string]syscall.Signal{ "RTMAX-1": sigrtmax - 1, "RTMAX": sigrtmax, } - -// CatchAll catches all signals and relays them to the specified channel. -func CatchAll(sigc chan os.Signal) { - handledSigs := make([]os.Signal, 0, len(SignalMap)) - for _, s := range SignalMap { - handledSigs = append(handledSigs, s) - } - signal.Notify(sigc, handledSigs...) -} - -// StopCatch stops catching the signals and closes the specified channel. -func StopCatch(sigc chan os.Signal) { - signal.Stop(sigc) - close(sigc) -} diff --git a/pkg/signal/signal_unix.go b/pkg/signal/signal_unix.go index 0f43e21b7..7919e3670 100644 --- a/pkg/signal/signal_unix.go +++ b/pkg/signal/signal_unix.go @@ -5,7 +5,6 @@ package signal import ( - "os" "syscall" ) @@ -88,13 +87,3 @@ var SignalMap = map[string]syscall.Signal{ "RTMAX-1": sigrtmax - 1, "RTMAX": sigrtmax, } - -// CatchAll catches all signals and relays them to the specified channel. -func CatchAll(sigc chan os.Signal) { - panic("Unsupported on non-linux platforms") -} - -// StopCatch stops catching the signals and closes the specified channel. -func StopCatch(sigc chan os.Signal) { - panic("Unsupported on non-linux platforms") -} diff --git a/pkg/signal/signal_unsupported.go b/pkg/signal/signal_unsupported.go index 9d0cee317..19ae93a61 100644 --- a/pkg/signal/signal_unsupported.go +++ b/pkg/signal/signal_unsupported.go @@ -5,7 +5,6 @@ package signal import ( - "os" "syscall" ) @@ -88,13 +87,3 @@ var SignalMap = map[string]syscall.Signal{ "RTMAX-1": sigrtmax - 1, "RTMAX": sigrtmax, } - -// CatchAll catches all signals and relays them to the specified channel. -func CatchAll(sigc chan os.Signal) { - panic("Unsupported on non-linux platforms") -} - -// StopCatch stops catching the signals and closes the specified channel. -func StopCatch(sigc chan os.Signal) { - panic("Unsupported on non-linux platforms") -} diff --git a/pkg/specgen/generate/config_linux.go b/pkg/specgen/generate/config_linux.go index 4c3748e67..60d87a8fd 100644 --- a/pkg/specgen/generate/config_linux.go +++ b/pkg/specgen/generate/config_linux.go @@ -164,7 +164,7 @@ func addDevice(g *generate.Generator, device string) error { } // ParseDevice parses device mapping string to a src, dest & permissions string -func ParseDevice(device string) (string, string, string, error) { //nolint +func ParseDevice(device string) (string, string, string, error) { var src string var dst string permissions := "rwm" diff --git a/pkg/specgen/generate/container.go b/pkg/specgen/generate/container.go index cc376125f..30c759495 100644 --- a/pkg/specgen/generate/container.go +++ b/pkg/specgen/generate/container.go @@ -303,8 +303,8 @@ func FinishThrottleDevices(s *specgen.SpecGenerator) error { if err := unix.Stat(k, &statT); err != nil { return err } - v.Major = (int64(unix.Major(uint64(statT.Rdev)))) // nolint: unconvert - v.Minor = (int64(unix.Minor(uint64(statT.Rdev)))) // nolint: unconvert + v.Major = (int64(unix.Major(uint64(statT.Rdev)))) //nolint: unconvert + v.Minor = (int64(unix.Minor(uint64(statT.Rdev)))) //nolint: unconvert if s.ResourceLimits.BlockIO == nil { s.ResourceLimits.BlockIO = new(spec.LinuxBlockIO) } @@ -317,8 +317,8 @@ func FinishThrottleDevices(s *specgen.SpecGenerator) error { if err := unix.Stat(k, &statT); err != nil { return err } - v.Major = (int64(unix.Major(uint64(statT.Rdev)))) // nolint: unconvert - v.Minor = (int64(unix.Minor(uint64(statT.Rdev)))) // nolint: unconvert + v.Major = (int64(unix.Major(uint64(statT.Rdev)))) //nolint: unconvert + v.Minor = (int64(unix.Minor(uint64(statT.Rdev)))) //nolint: unconvert s.ResourceLimits.BlockIO.ThrottleWriteBpsDevice = append(s.ResourceLimits.BlockIO.ThrottleWriteBpsDevice, v) } } @@ -328,8 +328,8 @@ func FinishThrottleDevices(s *specgen.SpecGenerator) error { if err := unix.Stat(k, &statT); err != nil { return err } - v.Major = (int64(unix.Major(uint64(statT.Rdev)))) // nolint: unconvert - v.Minor = (int64(unix.Minor(uint64(statT.Rdev)))) // nolint: unconvert + v.Major = (int64(unix.Major(uint64(statT.Rdev)))) //nolint: unconvert + v.Minor = (int64(unix.Minor(uint64(statT.Rdev)))) //nolint: unconvert s.ResourceLimits.BlockIO.ThrottleReadIOPSDevice = append(s.ResourceLimits.BlockIO.ThrottleReadIOPSDevice, v) } } @@ -339,8 +339,8 @@ func FinishThrottleDevices(s *specgen.SpecGenerator) error { if err := unix.Stat(k, &statT); err != nil { return err } - v.Major = (int64(unix.Major(uint64(statT.Rdev)))) // nolint: unconvert - v.Minor = (int64(unix.Minor(uint64(statT.Rdev)))) // nolint: unconvert + v.Major = (int64(unix.Major(uint64(statT.Rdev)))) //nolint: unconvert + v.Minor = (int64(unix.Minor(uint64(statT.Rdev)))) //nolint: unconvert s.ResourceLimits.BlockIO.ThrottleWriteIOPSDevice = append(s.ResourceLimits.BlockIO.ThrottleWriteIOPSDevice, v) } } @@ -450,7 +450,7 @@ func ConfigToSpec(rt *libpod.Runtime, specg *specgen.SpecGenerator, contaierID s specg.IpcNS = specgen.Namespace{NSMode: specgen.Default} // default } case "uts": - specg.UtsNS = specgen.Namespace{NSMode: specgen.Default} // default + specg.UtsNS = specgen.Namespace{NSMode: specgen.Private} // default case "user": if conf.AddCurrentUserPasswdEntry { specg.UserNS = specgen.Namespace{NSMode: specgen.KeepID} @@ -506,6 +506,7 @@ func ConfigToSpec(rt *libpod.Runtime, specg *specgen.SpecGenerator, contaierID s specg.Mounts = mounts specg.HostDeviceList = conf.DeviceHostSrc specg.Networks = conf.Networks + specg.ShmSize = &conf.ShmSize mapSecurityConfig(conf, specg) diff --git a/pkg/specgen/generate/container_create.go b/pkg/specgen/generate/container_create.go index 7faf13465..6b2e90b22 100644 --- a/pkg/specgen/generate/container_create.go +++ b/pkg/specgen/generate/container_create.go @@ -180,10 +180,23 @@ func MakeContainer(ctx context.Context, rt *libpod.Runtime, s *specgen.SpecGener if err != nil { return nil, nil, nil, err } + resources := runtimeSpec.Linux.Resources + + // resources get overwrritten similarly to pod inheritance, manually assign here if there is a new value + marshalRes, err := json.Marshal(resources) + if err != nil { + return nil, nil, nil, err + } + err = json.Unmarshal(out, runtimeSpec.Linux) if err != nil { return nil, nil, nil, err } + + err = json.Unmarshal(marshalRes, runtimeSpec.Linux.Resources) + if err != nil { + return nil, nil, nil, err + } } if s.ResourceLimits != nil { switch { @@ -564,5 +577,10 @@ func Inherit(infra libpod.Container, s *specgen.SpecGenerator, rt *libpod.Runtim if err != nil { return nil, nil, nil, err } + + // this causes errors when shmSize is the default value, it will still get passed down unless we manually override. + if s.IpcNS.NSMode == specgen.Host && (compatibleOptions.ShmSize != nil && compatibleOptions.IsDefaultShmSize()) { + s.ShmSize = nil + } return options, infraSpec, compatibleOptions, nil } diff --git a/pkg/specgen/generate/oci.go b/pkg/specgen/generate/oci.go index 716960024..1044854f4 100644 --- a/pkg/specgen/generate/oci.go +++ b/pkg/specgen/generate/oci.go @@ -298,8 +298,7 @@ func SpecGenToOCI(ctx context.Context, s *specgen.SpecGenerator, rt *libpod.Runt g.AddAnnotation(key, val) } - switch { - case compatibleOptions.InfraResources == nil && s.ResourceLimits != nil: + if s.ResourceLimits != nil { out, err := json.Marshal(s.ResourceLimits) if err != nil { return nil, err @@ -308,29 +307,9 @@ func SpecGenToOCI(ctx context.Context, s *specgen.SpecGenerator, rt *libpod.Runt if err != nil { return nil, err } - case s.ResourceLimits != nil: // if we have predefined resource limits we need to make sure we keep the infra and container limits - originalResources, err := json.Marshal(s.ResourceLimits) - if err != nil { - return nil, err - } - infraResources, err := json.Marshal(compatibleOptions.InfraResources) - if err != nil { - return nil, err - } - err = json.Unmarshal(infraResources, s.ResourceLimits) // put infra's resource limits in the container - if err != nil { - return nil, err - } - err = json.Unmarshal(originalResources, s.ResourceLimits) // make sure we did not override anything - if err != nil { - return nil, err - } g.Config.Linux.Resources = s.ResourceLimits - default: - g.Config.Linux.Resources = compatibleOptions.InfraResources } // Devices - // set the default rule at the beginning of device configuration if !inUserNS && !s.Privileged { g.AddLinuxResourcesDevice(false, "", nil, nil, "rwm") @@ -371,7 +350,7 @@ func SpecGenToOCI(ctx context.Context, s *specgen.SpecGenerator, rt *libpod.Runt if err := unix.Stat(k, &statT); err != nil { return nil, errors.Wrapf(err, "failed to inspect '%s' in --blkio-weight-device", k) } - g.AddLinuxResourcesBlockIOWeightDevice((int64(unix.Major(uint64(statT.Rdev)))), (int64(unix.Minor(uint64(statT.Rdev)))), *v.Weight) // nolint: unconvert + g.AddLinuxResourcesBlockIOWeightDevice((int64(unix.Major(uint64(statT.Rdev)))), (int64(unix.Minor(uint64(statT.Rdev)))), *v.Weight) //nolint: unconvert } BlockAccessToKernelFilesystems(s.Privileged, s.PidNS.IsHost(), s.Mask, s.Unmask, &g) diff --git a/pkg/specgen/generate/pod_create.go b/pkg/specgen/generate/pod_create.go index d4f281a11..4ac8a0aa2 100644 --- a/pkg/specgen/generate/pod_create.go +++ b/pkg/specgen/generate/pod_create.go @@ -2,12 +2,17 @@ package generate import ( "context" + "fmt" "net" + "os" + "strconv" + "strings" "github.com/containers/podman/v4/libpod" "github.com/containers/podman/v4/libpod/define" "github.com/containers/podman/v4/pkg/domain/entities" "github.com/containers/podman/v4/pkg/specgen" + "github.com/containers/podman/v4/pkg/specgenutil" "github.com/pkg/errors" "github.com/sirupsen/logrus" ) @@ -210,3 +215,88 @@ func MapSpec(p *specgen.PodSpecGenerator) (*specgen.SpecGenerator, error) { p.InfraContainerSpec.Image = p.InfraImage return p.InfraContainerSpec, nil } + +func PodConfigToSpec(rt *libpod.Runtime, spec *specgen.PodSpecGenerator, infraOptions *entities.ContainerCreateOptions, id string) (p *libpod.Pod, err error) { + pod, err := rt.LookupPod(id) + if err != nil { + return nil, err + } + + infraSpec := &specgen.SpecGenerator{} + if pod.HasInfraContainer() { + infraID, err := pod.InfraContainerID() + if err != nil { + return nil, err + } + _, _, err = ConfigToSpec(rt, infraSpec, infraID) + if err != nil { + return nil, err + } + + infraSpec.Hostname = "" + infraSpec.CgroupParent = "" + infraSpec.Pod = "" // remove old pod... + infraOptions.IsClone = true + infraOptions.IsInfra = true + + n := infraSpec.Name + _, err = rt.LookupContainer(n + "-clone") + if err == nil { // if we found a ctr with this name, set it so the below switch can tell + n += "-clone" + } + + switch { + case strings.Contains(n, "-clone"): + ind := strings.Index(n, "-clone") + 6 + num, err := strconv.Atoi(n[ind:]) + if num == 0 && err != nil { // clone1 is hard to get with this logic, just check for it here. + _, err = rt.LookupContainer(n + "1") + if err != nil { + infraSpec.Name = n + "1" + break + } + } else { + n = n[0:ind] + } + err = nil + count := num + for err == nil { + count++ + tempN := n + strconv.Itoa(count) + _, err = rt.LookupContainer(tempN) + } + n += strconv.Itoa(count) + infraSpec.Name = n + default: + infraSpec.Name = n + "-clone" + } + + err = specgenutil.FillOutSpecGen(infraSpec, infraOptions, []string{}) + if err != nil { + return nil, err + } + + out, err := CompleteSpec(context.Background(), rt, infraSpec) + if err != nil { + return nil, err + } + + // Print warnings + if len(out) > 0 { + for _, w := range out { + fmt.Println("Could not properly complete the spec as expected:") + fmt.Fprintf(os.Stderr, "%s\n", w) + } + } + + spec.InfraContainerSpec = infraSpec + } + + // need to reset hostname, name etc of both pod and infra + spec.Hostname = "" + + if len(spec.InfraContainerSpec.Image) > 0 { + spec.InfraImage = spec.InfraContainerSpec.Image + } + return pod, nil +} diff --git a/pkg/specgen/podspecgen.go b/pkg/specgen/podspecgen.go index 777097ac5..02ba06be1 100644 --- a/pkg/specgen/podspecgen.go +++ b/pkg/specgen/podspecgen.go @@ -183,6 +183,10 @@ type PodStorageConfig struct { // comma-separated options. Valid options are 'ro', 'rw', and 'z'. // Options will be used for all volumes sourced from the container. VolumesFrom []string `json:"volumes_from,omitempty"` + // ShmSize is the size of the tmpfs to mount in at /dev/shm, in bytes. + // Conflicts with ShmSize if IpcNS is not private. + // Optional. + ShmSize *int64 `json:"shm_size,omitempty"` } // PodCgroupConfig contains configuration options about a pod's cgroups. diff --git a/pkg/specgen/volumes.go b/pkg/specgen/volumes.go index a7a1022b0..f272a5c11 100644 --- a/pkg/specgen/volumes.go +++ b/pkg/specgen/volumes.go @@ -1,6 +1,7 @@ package specgen import ( + "path/filepath" "strings" "github.com/containers/common/pkg/parse" @@ -56,7 +57,6 @@ func GenVolumeMounts(volumeFlag []string) (map[string]spec.Mount, map[string]*Na overlayVolumes := make(map[string]*OverlayVolume) volumeFormatErr := errors.Errorf("incorrect volume format, should be [host-dir:]ctr-dir[:option]") - for _, vol := range volumeFlag { var ( options []string @@ -71,6 +71,20 @@ func GenVolumeMounts(volumeFlag []string) (map[string]spec.Mount, map[string]*Na } src = splitVol[0] + + // Support relative paths beginning with ./ + if strings.HasPrefix(src, "./") { + path, err := filepath.EvalSymlinks(src) + if err != nil { + return nil, nil, nil, err + } + src, err = filepath.Abs(path) + if err != nil { + return nil, nil, nil, err + } + splitVol[0] = src + } + if len(splitVol) == 1 { // This is an anonymous named volume. Only thing given // is destination. diff --git a/pkg/specgenutil/specgen.go b/pkg/specgenutil/specgen.go index 6d70af106..ab45a8d47 100644 --- a/pkg/specgenutil/specgen.go +++ b/pkg/specgenutil/specgen.go @@ -312,7 +312,7 @@ func FillOutSpecGen(s *specgen.SpecGenerator, c *entities.ContainerCreateOptions s.PublishExposedPorts = c.PublishAll } - if len(s.Pod) == 0 { + if len(s.Pod) == 0 || len(c.Pod) > 0 { s.Pod = c.Pod } diff --git a/pkg/systemd/generate/containers.go b/pkg/systemd/generate/containers.go index d552e21ed..e953a1f1f 100644 --- a/pkg/systemd/generate/containers.go +++ b/pkg/systemd/generate/containers.go @@ -204,7 +204,7 @@ func generateContainerInfo(ctr *libpod.Container, options entities.GenerateSyste } else { runRoot = ctr.Runtime().RunRoot() if runRoot == "" { - return nil, errors.Errorf("could not lookup container's runroot: got empty string") + return nil, errors.Errorf("could not look up container's runroot: got empty string") } } diff --git a/pkg/util/utils_linux.go b/pkg/util/utils_linux.go index 871303f64..bc522361f 100644 --- a/pkg/util/utils_linux.go +++ b/pkg/util/utils_linux.go @@ -176,7 +176,7 @@ func DeviceFromPath(path string) (*spec.LinuxDevice, error) { var ( devType string mode = stat.Mode - devNumber = uint64(stat.Rdev) // nolint: unconvert + devNumber = uint64(stat.Rdev) //nolint: unconvert m = os.FileMode(mode) ) |