diff options
Diffstat (limited to 'pkg')
-rw-r--r-- | pkg/api/server/handler_api.go | 95 | ||||
-rw-r--r-- | pkg/api/server/register_containers.go | 8 | ||||
-rw-r--r-- | pkg/api/server/register_events.go | 4 | ||||
-rw-r--r-- | pkg/api/server/register_images.go | 4 | ||||
-rw-r--r-- | pkg/domain/infra/abi/containers.go | 70 | ||||
-rw-r--r-- | pkg/domain/infra/abi/manifest.go | 3 | ||||
-rw-r--r-- | pkg/domain/infra/tunnel/containers.go | 53 | ||||
-rw-r--r-- | pkg/domain/infra/tunnel/helpers.go | 11 | ||||
-rw-r--r-- | pkg/domain/infra/tunnel/manifest.go | 10 | ||||
-rw-r--r-- | pkg/machine/e2e/init_test.go | 19 | ||||
-rw-r--r-- | pkg/machine/e2e/set_test.go | 20 | ||||
-rw-r--r-- | pkg/specgen/generate/container.go | 18 | ||||
-rw-r--r-- | pkg/systemd/generate/common.go | 24 | ||||
-rw-r--r-- | pkg/systemd/generate/containers.go | 12 | ||||
-rw-r--r-- | pkg/systemd/generate/containers_test.go | 143 |
15 files changed, 355 insertions, 139 deletions
diff --git a/pkg/api/server/handler_api.go b/pkg/api/server/handler_api.go index a3aa681d0..57cafa5f6 100644 --- a/pkg/api/server/handler_api.go +++ b/pkg/api/server/handler_api.go @@ -1,7 +1,10 @@ package server import ( + "bufio" + "errors" "fmt" + "net" "net/http" "runtime" @@ -9,34 +12,54 @@ import ( "github.com/sirupsen/logrus" ) +type BufferedResponseWriter struct { + b *bufio.Writer + w http.ResponseWriter +} + // APIHandler is a wrapper to enhance HandlerFunc's and remove redundant code func (s *APIServer) APIHandler(h http.HandlerFunc) http.HandlerFunc { return func(w http.ResponseWriter, r *http.Request) { // Wrapper to hide some boilerplate - fn := func(w http.ResponseWriter, r *http.Request) { - if err := r.ParseForm(); err != nil { - logrus.WithFields(logrus.Fields{ - "X-Reference-Id": r.Header.Get("X-Reference-Id"), - }).Info("Failed Request: unable to parse form: " + err.Error()) - } + s.apiWrapper(h, w, r, false) + } +} + +// An API Handler to help historical clients with broken parsing that expect +// streaming JSON payloads to be reliably messaged framed (full JSON record +// always fits in each read()) +func (s *APIServer) StreamBufferedAPIHandler(h http.HandlerFunc) http.HandlerFunc { + return func(w http.ResponseWriter, r *http.Request) { + // Wrapper to hide some boilerplate + s.apiWrapper(h, w, r, true) + } +} - cv := version.APIVersion[version.Compat][version.CurrentAPI] - w.Header().Set("API-Version", fmt.Sprintf("%d.%d", cv.Major, cv.Minor)) +func (s *APIServer) apiWrapper(h http.HandlerFunc, w http.ResponseWriter, r *http.Request, buffer bool) { + if err := r.ParseForm(); err != nil { + logrus.WithFields(logrus.Fields{ + "X-Reference-Id": r.Header.Get("X-Reference-Id"), + }).Info("Failed Request: unable to parse form: " + err.Error()) + } - lv := version.APIVersion[version.Libpod][version.CurrentAPI].String() - w.Header().Set("Libpod-API-Version", lv) - w.Header().Set("Server", "Libpod/"+lv+" ("+runtime.GOOS+")") + cv := version.APIVersion[version.Compat][version.CurrentAPI] + w.Header().Set("API-Version", fmt.Sprintf("%d.%d", cv.Major, cv.Minor)) - if s.CorsHeaders != "" { - w.Header().Set("Access-Control-Allow-Origin", s.CorsHeaders) - w.Header().Set("Access-Control-Allow-Headers", "Origin, X-Requested-With, Content-Type, Accept, X-Registry-Auth, Connection, Upgrade, X-Registry-Config") - w.Header().Set("Access-Control-Allow-Methods", "HEAD, GET, POST, DELETE, PUT, OPTIONS") - } + lv := version.APIVersion[version.Libpod][version.CurrentAPI].String() + w.Header().Set("Libpod-API-Version", lv) + w.Header().Set("Server", "Libpod/"+lv+" ("+runtime.GOOS+")") - h(w, r) - } - fn(w, r) + if s.CorsHeaders != "" { + w.Header().Set("Access-Control-Allow-Origin", s.CorsHeaders) + w.Header().Set("Access-Control-Allow-Headers", "Origin, X-Requested-With, Content-Type, Accept, X-Registry-Auth, Connection, Upgrade, X-Registry-Config") + w.Header().Set("Access-Control-Allow-Methods", "HEAD, GET, POST, DELETE, PUT, OPTIONS") } + + if buffer { + w = newBufferedResponseWriter(w) + } + + h(w, r) } // VersionedPath prepends the version parsing code @@ -44,3 +67,37 @@ func (s *APIServer) APIHandler(h http.HandlerFunc) http.HandlerFunc { func VersionedPath(p string) string { return "/v{version:[0-9][0-9A-Za-z.-]*}" + p } + +func (w *BufferedResponseWriter) Header() http.Header { + return w.w.Header() +} + +func (w *BufferedResponseWriter) Hijack() (net.Conn, *bufio.ReadWriter, error) { + _ = w.b.Flush() + if wrapped, ok := w.w.(http.Hijacker); ok { + return wrapped.Hijack() + } + + return nil, nil, errors.New("ResponseWriter does not support hijacking") +} + +func (w *BufferedResponseWriter) Write(b []byte) (int, error) { + return w.b.Write(b) +} + +func (w *BufferedResponseWriter) WriteHeader(statusCode int) { + w.w.WriteHeader(statusCode) +} + +func (w *BufferedResponseWriter) Flush() { + _ = w.b.Flush() + if wrapped, ok := w.w.(http.Flusher); ok { + wrapped.Flush() + } +} +func newBufferedResponseWriter(rw http.ResponseWriter) *BufferedResponseWriter { + return &BufferedResponseWriter{ + bufio.NewWriterSize(rw, 8192), + rw, + } +} diff --git a/pkg/api/server/register_containers.go b/pkg/api/server/register_containers.go index e2ecdb6af..b319fc14a 100644 --- a/pkg/api/server/register_containers.go +++ b/pkg/api/server/register_containers.go @@ -397,9 +397,9 @@ func (s *APIServer) registerContainersHandlers(r *mux.Router) error { // $ref: "#/responses/containerNotFound" // 500: // $ref: "#/responses/internalError" - r.HandleFunc(VersionedPath("/containers/{name}/stats"), s.APIHandler(compat.StatsContainer)).Methods(http.MethodGet) + r.HandleFunc(VersionedPath("/containers/{name}/stats"), s.StreamBufferedAPIHandler(compat.StatsContainer)).Methods(http.MethodGet) // Added non version path to URI to support docker non versioned paths - r.HandleFunc("/containers/{name}/stats", s.APIHandler(compat.StatsContainer)).Methods(http.MethodGet) + r.HandleFunc("/containers/{name}/stats", s.StreamBufferedAPIHandler(compat.StatsContainer)).Methods(http.MethodGet) // swagger:operation POST /containers/{name}/stop compat ContainerStop // --- // tags: @@ -455,9 +455,9 @@ func (s *APIServer) registerContainersHandlers(r *mux.Router) error { // $ref: "#/responses/containerNotFound" // 500: // $ref: "#/responses/internalError" - r.HandleFunc(VersionedPath("/containers/{name}/top"), s.APIHandler(compat.TopContainer)).Methods(http.MethodGet) + r.HandleFunc(VersionedPath("/containers/{name}/top"), s.StreamBufferedAPIHandler(compat.TopContainer)).Methods(http.MethodGet) // Added non version path to URI to support docker non versioned paths - r.HandleFunc("/containers/{name}/top", s.APIHandler(compat.TopContainer)).Methods(http.MethodGet) + r.HandleFunc("/containers/{name}/top", s.StreamBufferedAPIHandler(compat.TopContainer)).Methods(http.MethodGet) // swagger:operation POST /containers/{name}/unpause compat ContainerUnpause // --- // tags: diff --git a/pkg/api/server/register_events.go b/pkg/api/server/register_events.go index 76f9ec619..3442b3eab 100644 --- a/pkg/api/server/register_events.go +++ b/pkg/api/server/register_events.go @@ -34,9 +34,9 @@ func (s *APIServer) registerEventsHandlers(r *mux.Router) error { // description: returns a string of json data describing an event // 500: // "$ref": "#/responses/internalError" - r.Handle(VersionedPath("/events"), s.APIHandler(compat.GetEvents)).Methods(http.MethodGet) + r.Handle(VersionedPath("/events"), s.StreamBufferedAPIHandler(compat.GetEvents)).Methods(http.MethodGet) // Added non version path to URI to support docker non versioned paths - r.Handle("/events", s.APIHandler(compat.GetEvents)).Methods(http.MethodGet) + r.Handle("/events", s.StreamBufferedAPIHandler(compat.GetEvents)).Methods(http.MethodGet) // swagger:operation GET /libpod/events system SystemEventsLibpod // --- // tags: diff --git a/pkg/api/server/register_images.go b/pkg/api/server/register_images.go index 1bfedd77e..d71e0d470 100644 --- a/pkg/api/server/register_images.go +++ b/pkg/api/server/register_images.go @@ -702,9 +702,9 @@ func (s *APIServer) registerImagesHandlers(r *mux.Router) error { // $ref: "#/responses/badParamError" // 500: // $ref: "#/responses/internalError" - r.Handle(VersionedPath("/build"), s.APIHandler(compat.BuildImage)).Methods(http.MethodPost) + r.Handle(VersionedPath("/build"), s.StreamBufferedAPIHandler(compat.BuildImage)).Methods(http.MethodPost) // Added non version path to URI to support docker non versioned paths - r.Handle("/build", s.APIHandler(compat.BuildImage)).Methods(http.MethodPost) + r.Handle("/build", s.StreamBufferedAPIHandler(compat.BuildImage)).Methods(http.MethodPost) /* libpod endpoints */ diff --git a/pkg/domain/infra/abi/containers.go b/pkg/domain/infra/abi/containers.go index 04eb85504..f12e17c09 100644 --- a/pkg/domain/infra/abi/containers.go +++ b/pkg/domain/infra/abi/containers.go @@ -40,6 +40,7 @@ import ( // is specified. It also returns a list of the corresponding input name used to lookup each container. func getContainersAndInputByContext(all, latest bool, names []string, filters map[string][]string, runtime *libpod.Runtime) (ctrs []*libpod.Container, rawInput []string, err error) { var ctr *libpod.Container + var filteredCtrs []*libpod.Container ctrs = []*libpod.Container{} filterFuncs := make([]libpod.ContainerFilter, 0, len(filters)) @@ -58,7 +59,17 @@ func getContainersAndInputByContext(all, latest bool, names []string, filters ma } rawInput = []string{} for _, candidate := range ctrs { - rawInput = append(rawInput, candidate.ID()) + if len(names) > 0 { + for _, name := range names { + if candidate.ID() == name || candidate.Name() == name { + rawInput = append(rawInput, candidate.ID()) + filteredCtrs = append(filteredCtrs, candidate) + } + } + ctrs = filteredCtrs + } else { + rawInput = append(rawInput, candidate.ID()) + } } case all: ctrs, err = runtime.GetAllContainers() @@ -860,38 +871,7 @@ func (ic *ContainerEngine) ContainerExecDetached(ctx context.Context, nameOrID s func (ic *ContainerEngine) ContainerStart(ctx context.Context, namesOrIds []string, options entities.ContainerStartOptions) ([]*entities.ContainerStartReport, error) { reports := []*entities.ContainerStartReport{} var exitCode = define.ExecErrorCodeGeneric - containersNamesOrIds := namesOrIds - all := options.All - if len(options.Filters) > 0 { - all = false - filterFuncs := make([]libpod.ContainerFilter, 0, len(options.Filters)) - if len(options.Filters) > 0 { - for k, v := range options.Filters { - generatedFunc, err := dfilters.GenerateContainerFilterFuncs(k, v, ic.Libpod) - if err != nil { - return nil, err - } - filterFuncs = append(filterFuncs, generatedFunc) - } - } - candidates, err := ic.Libpod.GetContainers(filterFuncs...) - if err != nil { - return nil, err - } - containersNamesOrIds = []string{} - for _, candidate := range candidates { - if options.All { - containersNamesOrIds = append(containersNamesOrIds, candidate.ID()) - continue - } - for _, nameOrID := range namesOrIds { - if nameOrID == candidate.ID() || nameOrID == candidate.Name() { - containersNamesOrIds = append(containersNamesOrIds, nameOrID) - } - } - } - } - ctrs, rawInputs, err := getContainersAndInputByContext(all, options.Latest, containersNamesOrIds, options.Filters, ic.Libpod) + ctrs, rawInputs, err := getContainersAndInputByContext(options.All, options.Latest, namesOrIds, options.Filters, ic.Libpod) if err != nil { return nil, err } @@ -1035,6 +1015,15 @@ func (ic *ContainerEngine) Diff(ctx context.Context, namesOrIDs []string, opts e } func (ic *ContainerEngine) ContainerRun(ctx context.Context, opts entities.ContainerRunOptions) (*entities.ContainerRunReport, error) { + removeContainer := func(ctr *libpod.Container, force bool) error { + var timeout *uint + if err := ic.Libpod.RemoveContainer(ctx, ctr, force, true, timeout); err != nil { + logrus.Debugf("unable to remove container %s after failing to start and attach to it: %v", ctr.ID(), err) + return err + } + return nil + } + warn, err := generate.CompleteSpec(ctx, ic.Libpod, opts.Spec) if err != nil { return nil, err @@ -1055,6 +1044,8 @@ func (ic *ContainerEngine) ContainerRun(ctx context.Context, opts entities.Conta if opts.CIDFile != "" { if err := util.CreateCidFile(opts.CIDFile, ctr.ID()); err != nil { + // If you fail to create CIDFile then remove the container + _ = removeContainer(ctr, true) return nil, err } } @@ -1072,6 +1063,11 @@ func (ic *ContainerEngine) ContainerRun(ctx context.Context, opts entities.Conta if err := ctr.Start(ctx, true); err != nil { // This means the command did not exist report.ExitCode = define.ExitCode(err) + if opts.Rm { + if rmErr := removeContainer(ctr, true); rmErr != nil && !errors.Is(rmErr, define.ErrNoSuchCtr) { + logrus.Errorf("Container %s failed to be removed", ctr.ID()) + } + } return &report, err } @@ -1088,10 +1084,7 @@ func (ic *ContainerEngine) ContainerRun(ctx context.Context, opts entities.Conta return &report, nil } if opts.Rm { - var timeout *uint - if deleteError := ic.Libpod.RemoveContainer(ctx, ctr, true, false, timeout); deleteError != nil { - logrus.Debugf("unable to remove container %s after failing to start and attach to it", ctr.ID()) - } + _ = removeContainer(ctr, true) } if errors.Is(err, define.ErrWillDeadlock) { logrus.Debugf("Deadlock error on %q: %v", ctr.ID(), err) @@ -1103,8 +1096,7 @@ func (ic *ContainerEngine) ContainerRun(ctx context.Context, opts entities.Conta } report.ExitCode = ic.GetContainerExitCode(ctx, ctr) if opts.Rm && !ctr.ShouldRestart(ctx) { - var timeout *uint - if err := ic.Libpod.RemoveContainer(ctx, ctr, false, true, timeout); err != nil { + if err := removeContainer(ctr, false); err != nil { if errors.Is(err, define.ErrNoSuchCtr) || errors.Is(err, define.ErrCtrRemoved) { logrus.Infof("Container %s was already removed, skipping --rm", ctr.ID()) diff --git a/pkg/domain/infra/abi/manifest.go b/pkg/domain/infra/abi/manifest.go index 60f727234..6606b2cd0 100644 --- a/pkg/domain/infra/abi/manifest.go +++ b/pkg/domain/infra/abi/manifest.go @@ -334,7 +334,8 @@ func (ir *ImageEngine) ManifestPush(ctx context.Context, name, destination strin } if opts.Rm { - if _, rmErrors := ir.Libpod.LibimageRuntime().RemoveImages(ctx, []string{manifestList.ID()}, nil); len(rmErrors) > 0 { + rmOpts := &libimage.RemoveImagesOptions{LookupManifest: true} + if _, rmErrors := ir.Libpod.LibimageRuntime().RemoveImages(ctx, []string{manifestList.ID()}, rmOpts); len(rmErrors) > 0 { return "", fmt.Errorf("error removing manifest after push: %w", rmErrors[0]) } } diff --git a/pkg/domain/infra/tunnel/containers.go b/pkg/domain/infra/tunnel/containers.go index fcabff7c4..0bf792879 100644 --- a/pkg/domain/infra/tunnel/containers.go +++ b/pkg/domain/infra/tunnel/containers.go @@ -623,36 +623,7 @@ func logIfRmError(id string, err error, reports []*reports.RmReport) { func (ic *ContainerEngine) ContainerStart(ctx context.Context, namesOrIds []string, options entities.ContainerStartOptions) ([]*entities.ContainerStartReport, error) { reports := []*entities.ContainerStartReport{} var exitCode = define.ExecErrorCodeGeneric - containersNamesOrIds := namesOrIds - all := options.All - if len(options.Filters) > 0 { - all = false - containersNamesOrIds = []string{} - opts := new(containers.ListOptions).WithFilters(options.Filters).WithAll(true) - candidates, listErr := containers.List(ic.ClientCtx, opts) - if listErr != nil { - return nil, listErr - } - for _, candidate := range candidates { - if options.All { - containersNamesOrIds = append(containersNamesOrIds, candidate.ID) - continue - } - for _, nameOrID := range namesOrIds { - if nameOrID == candidate.ID { - containersNamesOrIds = append(containersNamesOrIds, nameOrID) - continue - } - for _, containerName := range candidate.Names { - if containerName == nameOrID { - containersNamesOrIds = append(containersNamesOrIds, nameOrID) - continue - } - } - } - } - } - ctrs, err := getContainersByContext(ic.ClientCtx, all, false, containersNamesOrIds) + ctrs, namesOrIds, err := getContainersAndInputByContext(ic.ClientCtx, options.All, false, namesOrIds, options.Filters) if err != nil { return nil, err } @@ -771,8 +742,17 @@ func (ic *ContainerEngine) ContainerRun(ctx context.Context, opts entities.Conta for _, w := range con.Warnings { fmt.Fprintf(os.Stderr, "%s\n", w) } + removeContainer := func(id string, force bool) error { + removeOptions := new(containers.RemoveOptions).WithVolumes(true).WithForce(force) + reports, err := containers.Remove(ic.ClientCtx, id, removeOptions) + logIfRmError(id, err, reports) + return err + } + if opts.CIDFile != "" { if err := util.CreateCidFile(opts.CIDFile, con.ID); err != nil { + // If you fail to create CIDFile then remove the container + _ = removeContainer(con.ID, true) return nil, err } } @@ -784,6 +764,11 @@ func (ic *ContainerEngine) ContainerRun(ctx context.Context, opts entities.Conta err := containers.Start(ic.ClientCtx, con.ID, new(containers.StartOptions).WithRecursive(true)) if err != nil { report.ExitCode = define.ExitCode(err) + if opts.Rm { + if rmErr := removeContainer(con.ID, true); rmErr != nil && !errors.Is(rmErr, define.ErrNoSuchCtr) { + logrus.Errorf("Container %s failed to be removed", con.ID) + } + } } return &report, err } @@ -796,10 +781,7 @@ func (ic *ContainerEngine) ContainerRun(ctx context.Context, opts entities.Conta report.ExitCode = define.ExitCode(err) if opts.Rm { - reports, rmErr := containers.Remove(ic.ClientCtx, con.ID, new(containers.RemoveOptions).WithForce(false).WithVolumes(true)) - if rmErr != nil || reports[0].Err != nil { - logrus.Debugf("unable to remove container %s after failing to start and attach to it", con.ID) - } + _ = removeContainer(con.ID, false) } return &report, err } @@ -815,8 +797,7 @@ func (ic *ContainerEngine) ContainerRun(ctx context.Context, opts entities.Conta } if !shouldRestart { - reports, err := containers.Remove(ic.ClientCtx, con.ID, new(containers.RemoveOptions).WithForce(false).WithVolumes(true)) - logIfRmError(con.ID, err, reports) + _ = removeContainer(con.ID, false) } }() } diff --git a/pkg/domain/infra/tunnel/helpers.go b/pkg/domain/infra/tunnel/helpers.go index 9ff1641f0..20ea07948 100644 --- a/pkg/domain/infra/tunnel/helpers.go +++ b/pkg/domain/infra/tunnel/helpers.go @@ -31,8 +31,17 @@ func getContainersAndInputByContext(contextWithConnection context.Context, all, rawInputs := []string{} switch { case len(filters) > 0: + namesOrIDs = nil for i := range allContainers { - namesOrIDs = append(namesOrIDs, allContainers[i].ID) + if len(namesOrIDs) > 0 { + for _, name := range namesOrIDs { + if name == allContainers[i].ID { + namesOrIDs = append(namesOrIDs, allContainers[i].ID) + } + } + } else { + namesOrIDs = append(namesOrIDs, allContainers[i].ID) + } } case all: for i := range allContainers { diff --git a/pkg/domain/infra/tunnel/manifest.go b/pkg/domain/infra/tunnel/manifest.go index d2554f198..4a3148fac 100644 --- a/pkg/domain/infra/tunnel/manifest.go +++ b/pkg/domain/infra/tunnel/manifest.go @@ -110,5 +110,15 @@ func (ir *ImageEngine) ManifestPush(ctx context.Context, name, destination strin } } digest, err := manifests.Push(ir.ClientCtx, name, destination, options) + if err != nil { + return "", fmt.Errorf("error adding to manifest list %s: %w", name, err) + } + + if opts.Rm { + if _, rmErrors := ir.Remove(ctx, []string{name}, entities.ImageRemoveOptions{LookupManifest: true}); len(rmErrors) > 0 { + return "", fmt.Errorf("error removing manifest after push: %w", rmErrors[0]) + } + } + return digest, err } diff --git a/pkg/machine/e2e/init_test.go b/pkg/machine/e2e/init_test.go index b246dc4da..859a3ca46 100644 --- a/pkg/machine/e2e/init_test.go +++ b/pkg/machine/e2e/init_test.go @@ -3,7 +3,7 @@ package e2e_test import ( "io/ioutil" "os" - "runtime" + "strconv" "time" "github.com/containers/podman/v4/pkg/machine" @@ -80,7 +80,7 @@ var _ = Describe("podman machine init", func() { It("machine init with cpus, disk size, memory, timezone", func() { name := randomString() i := new(initMachine) - session, err := mb.setName(name).setCmd(i.withImagePath(mb.imagePath).withCPUs(2).withDiskSize(102).withMemory(4000).withTimezone("Pacific/Honolulu")).run() + session, err := mb.setName(name).setCmd(i.withImagePath(mb.imagePath).withCPUs(2).withDiskSize(102).withMemory(4096).withTimezone("Pacific/Honolulu")).run() Expect(err).To(BeNil()) Expect(session).To(Exit(0)) @@ -102,18 +102,13 @@ var _ = Describe("podman machine init", func() { Expect(diskSession.outputToString()).To(ContainSubstring("102 GiB")) sshMemory := sshMachine{} - memorySession, err := mb.setName(name).setCmd(sshMemory.withSSHComand([]string{"cat", "/proc/meminfo", "|", "numfmt", "--field", "2", "--from-unit=Ki", "--to-unit=Mi", "|", "sed", "'s/ kB/M/g'", "|", "grep", "MemTotal"})).run() + memorySession, err := mb.setName(name).setCmd(sshMemory.withSSHComand([]string{"cat", "/proc/meminfo", "|", "grep", "-i", "'memtotal'", "|", "grep", "-o", "'[[:digit:]]*'"})).run() Expect(err).To(BeNil()) Expect(memorySession).To(Exit(0)) - switch runtime.GOOS { - // os's handle memory differently - case "linux": - Expect(memorySession.outputToString()).To(ContainSubstring("3822")) - case "darwin": - Expect(memorySession.outputToString()).To(ContainSubstring("3824")) - default: - // add windows when testing on that platform - } + foundMemory, err := strconv.Atoi(memorySession.outputToString()) + Expect(err).To(BeNil()) + Expect(foundMemory).To(BeNumerically(">", 3800000)) + Expect(foundMemory).To(BeNumerically("<", 4200000)) sshTimezone := sshMachine{} timezoneSession, err := mb.setName(name).setCmd(sshTimezone.withSSHComand([]string{"date"})).run() diff --git a/pkg/machine/e2e/set_test.go b/pkg/machine/e2e/set_test.go index 4839e33da..a32bb72f2 100644 --- a/pkg/machine/e2e/set_test.go +++ b/pkg/machine/e2e/set_test.go @@ -1,7 +1,7 @@ package e2e_test import ( - "runtime" + "strconv" . "github.com/onsi/ginkgo" . "github.com/onsi/gomega" @@ -29,7 +29,7 @@ var _ = Describe("podman machine set", func() { Expect(session).To(Exit(0)) set := setMachine{} - setSession, err := mb.setName(name).setCmd(set.withCPUs(2).withDiskSize(102).withMemory(4000)).run() + setSession, err := mb.setName(name).setCmd(set.withCPUs(2).withDiskSize(102).withMemory(4096)).run() Expect(err).To(BeNil()) Expect(setSession).To(Exit(0)) @@ -56,18 +56,14 @@ var _ = Describe("podman machine set", func() { Expect(diskSession.outputToString()).To(ContainSubstring("102 GiB")) sshMemory := sshMachine{} - memorySession, err := mb.setName(name).setCmd(sshMemory.withSSHComand([]string{"cat", "/proc/meminfo", "|", "numfmt", "--field", "2", "--from-unit=Ki", "--to-unit=Mi", "|", "sed", "'s/ kB/M/g'", "|", "grep", "MemTotal"})).run() + memorySession, err := mb.setName(name).setCmd(sshMemory.withSSHComand([]string{"cat", "/proc/meminfo", "|", "grep", "-i", "'memtotal'", "|", "grep", "-o", "'[[:digit:]]*'"})).run() Expect(err).To(BeNil()) Expect(memorySession).To(Exit(0)) - switch runtime.GOOS { - // it seems macos and linux handle memory differently - case "linux": - Expect(memorySession.outputToString()).To(ContainSubstring("3822")) - case "darwin": - Expect(memorySession.outputToString()).To(ContainSubstring("3824")) - default: - // windows can go here if we ever run tests there - } + foundMemory, err := strconv.Atoi(memorySession.outputToString()) + Expect(err).To(BeNil()) + Expect(foundMemory).To(BeNumerically(">", 3800000)) + Expect(foundMemory).To(BeNumerically("<", 4200000)) + // Setting a running machine results in 125 runner, err := mb.setName(name).setCmd(set.withCPUs(4)).run() Expect(err).To(BeNil()) diff --git a/pkg/specgen/generate/container.go b/pkg/specgen/generate/container.go index 2248c9235..b6701b1ce 100644 --- a/pkg/specgen/generate/container.go +++ b/pkg/specgen/generate/container.go @@ -191,16 +191,24 @@ func CompleteSpec(ctx context.Context, r *libpod.Runtime, s *specgen.SpecGenerat // - "container" denotes the container should join the VM of the SandboxID // (the infra container) if len(s.Pod) > 0 { - annotations[ann.SandboxID] = s.Pod + p, err := r.LookupPod(s.Pod) + if err != nil { + return nil, err + } + sandboxID := p.ID() + if p.HasInfraContainer() { + infra, err := p.InfraContainer() + if err != nil { + return nil, err + } + sandboxID = infra.ID() + } + annotations[ann.SandboxID] = sandboxID annotations[ann.ContainerType] = ann.ContainerTypeContainer // Check if this is an init-ctr and if so, check if // the pod is running. we do not want to add init-ctrs to // a running pod because it creates confusion for us. if len(s.InitContainerType) > 0 { - p, err := r.LookupPod(s.Pod) - if err != nil { - return nil, err - } containerStatuses, err := p.Status() if err != nil { return nil, err diff --git a/pkg/systemd/generate/common.go b/pkg/systemd/generate/common.go index 60b0c4b52..b0a441d54 100644 --- a/pkg/systemd/generate/common.go +++ b/pkg/systemd/generate/common.go @@ -42,7 +42,7 @@ RequiresMountsFor={{{{.RunRoot}}}} // filterPodFlags removes --pod, --pod-id-file and --infra-conmon-pidfile from the specified command. // argCount is the number of last arguments which should not be filtered, e.g. the container entrypoint. func filterPodFlags(command []string, argCount int) []string { - processed := []string{} + processed := make([]string, 0, len(command)) for i := 0; i < len(command)-argCount; i++ { s := command[i] if s == "--pod" || s == "--pod-id-file" || s == "--infra-conmon-pidfile" { @@ -63,7 +63,7 @@ func filterPodFlags(command []string, argCount int) []string { // filterCommonContainerFlags removes --sdnotify, --rm and --cgroups from the specified command. // argCount is the number of last arguments which should not be filtered, e.g. the container entrypoint. func filterCommonContainerFlags(command []string, argCount int) []string { - processed := []string{} + processed := make([]string, 0, len(command)) for i := 0; i < len(command)-argCount; i++ { s := command[i] @@ -71,7 +71,7 @@ func filterCommonContainerFlags(command []string, argCount int) []string { case s == "--rm": // Boolean flags support --flag and --flag={true,false}. continue - case s == "--sdnotify", s == "--cgroups", s == "--cidfile", s == "--restart": + case s == "--cgroups", s == "--cidfile", s == "--restart": i++ continue case strings.HasPrefix(s, "--rm="), @@ -111,6 +111,24 @@ func escapeSystemdArg(arg string) string { return arg } +func removeSdNotifyArg(args []string, argCount int) []string { + processed := make([]string, 0, len(args)) + for i := 0; i < len(args)-argCount; i++ { + s := args[i] + + switch { + case s == "--sdnotify": + i++ + continue + case strings.HasPrefix(s, "--sdnotify="): + continue + } + processed = append(processed, s) + } + processed = append(processed, args[len(args)-argCount:]...) + return processed +} + func removeDetachArg(args []string, argCount int) []string { // "--detach=false" could also be in the container entrypoint // split them off so we do not remove it there diff --git a/pkg/systemd/generate/containers.go b/pkg/systemd/generate/containers.go index 6596ef73b..1f8c519b7 100644 --- a/pkg/systemd/generate/containers.go +++ b/pkg/systemd/generate/containers.go @@ -378,6 +378,9 @@ func executeContainerTemplate(info *containerInfo, options entities.GenerateSyst fs.StringArrayP("env", "e", nil, "") fs.String("sdnotify", "", "") fs.String("restart", "", "") + // have to define extra -h flag to prevent help error when parsing -h hostname + // https://github.com/containers/podman/issues/15124 + fs.StringP("help", "h", "", "") if err := fs.Parse(remainingCmd); err != nil { return "", fmt.Errorf("parsing remaining command-line arguments: %w", err) } @@ -403,8 +406,13 @@ func executeContainerTemplate(info *containerInfo, options entities.GenerateSyst // Default to --sdnotify=conmon unless already set by the // container. - hasSdnotifyParam := fs.Lookup("sdnotify").Changed - if !hasSdnotifyParam { + sdnotifyFlag := fs.Lookup("sdnotify") + if !sdnotifyFlag.Changed { + startCommand = append(startCommand, "--sdnotify=conmon") + } else if sdnotifyFlag.Value.String() == libpodDefine.SdNotifyModeIgnore { + // If ignore is set force conmon otherwise the unit with Type=notify will fail. + logrus.Infof("Forcing --sdnotify=conmon for container %s", info.ContainerNameOrID) + remainingCmd = removeSdNotifyArg(remainingCmd, fs.NArg()) startCommand = append(startCommand, "--sdnotify=conmon") } diff --git a/pkg/systemd/generate/containers_test.go b/pkg/systemd/generate/containers_test.go index 640aa298e..873cbfbb3 100644 --- a/pkg/systemd/generate/containers_test.go +++ b/pkg/systemd/generate/containers_test.go @@ -2,6 +2,7 @@ package generate import ( "fmt" + "strings" "testing" "github.com/containers/podman/v4/pkg/domain/entities" @@ -317,6 +318,39 @@ NotifyAccess=all WantedBy=default.target ` + goodWithNameAndSdnotifyIgnore := `# jadda-jadda.service +# autogenerated by Podman CI + +[Unit] +Description=Podman jadda-jadda.service +Documentation=man:podman-generate-systemd(1) +Wants=network-online.target +After=network-online.target +RequiresMountsFor=/var/run/containers/storage + +[Service] +Environment=PODMAN_SYSTEMD_UNIT=%n +Restart=on-failure +TimeoutStopSec=70 +ExecStartPre=/bin/rm -f %t/%n.ctr-id +ExecStart=/usr/bin/podman container run \ + --cidfile=%t/%n.ctr-id \ + --cgroups=no-conmon \ + --rm \ + --sdnotify=conmon \ + -d \ + --replace \ + --name jadda-jadda \ + --hostname hello-world awesome-image:latest command arg1 ... argN "foo=arg \"with \" space" +ExecStop=/usr/bin/podman stop --ignore --cidfile=%t/%n.ctr-id +ExecStopPost=/usr/bin/podman rm -f --ignore --cidfile=%t/%n.ctr-id +Type=notify +NotifyAccess=all + +[Install] +WantedBy=default.target +` + goodWithExplicitShortDetachParam := `# jadda-jadda.service # autogenerated by Podman CI @@ -781,6 +815,37 @@ NotifyAccess=all WantedBy=default.target ` + goodNewWithHostname := `# jadda-jadda.service +# autogenerated by Podman CI + +[Unit] +Description=Podman jadda-jadda.service +Documentation=man:podman-generate-systemd(1) +Wants=network-online.target +After=network-online.target +RequiresMountsFor=/var/run/containers/storage + +[Service] +Environment=PODMAN_SYSTEMD_UNIT=%n +Restart=on-failure +TimeoutStopSec=70 +ExecStartPre=/bin/rm -f %t/%n.ctr-id +ExecStart=/usr/bin/podman run \ + --cidfile=%t/%n.ctr-id \ + --cgroups=no-conmon \ + --rm \ + --sdnotify=conmon \ + -d \ + -h hostname awesome-image:latest +ExecStop=/usr/bin/podman stop --ignore --cidfile=%t/%n.ctr-id +ExecStopPost=/usr/bin/podman rm -f --ignore --cidfile=%t/%n.ctr-id +Type=notify +NotifyAccess=all + +[Install] +WantedBy=default.target +` + templateGood := `# container-foo@.service # autogenerated by Podman CI @@ -992,7 +1057,7 @@ WantedBy=default.target false, false, }, - {"good with name and sdnotify", + {"good with name and --sdnotify=container", containerInfo{ Executable: "/usr/bin/podman", ServiceName: "jadda-jadda", @@ -1011,6 +1076,63 @@ WantedBy=default.target false, false, }, + {"good with name and --sdnotify container", + containerInfo{ + Executable: "/usr/bin/podman", + ServiceName: "jadda-jadda", + ContainerNameOrID: "jadda-jadda", + PIDFile: "/run/containers/storage/overlay-containers/639c53578af4d84b8800b4635fa4e680ee80fd67e0e6a2d4eea48d1e3230f401/userdata/conmon.pid", + StopTimeout: 10, + PodmanVersion: "CI", + CreateCommand: []string{"I'll get stripped", "container", "run", "--sdnotify", "container", "--name", "jadda-jadda", "--hostname", "hello-world", "awesome-image:latest", "command", "arg1", "...", "argN", "foo=arg \"with \" space"}, + EnvVariable: define.EnvVariable, + GraphRoot: "/var/lib/containers/storage", + RunRoot: "/var/run/containers/storage", + }, + strings.ReplaceAll(goodWithNameAndSdnotify, "--sdnotify=container", "--sdnotify container"), + true, + false, + false, + false, + }, + {"good with name and --sdnotify=ignore", + containerInfo{ + Executable: "/usr/bin/podman", + ServiceName: "jadda-jadda", + ContainerNameOrID: "jadda-jadda", + PIDFile: "/run/containers/storage/overlay-containers/639c53578af4d84b8800b4635fa4e680ee80fd67e0e6a2d4eea48d1e3230f401/userdata/conmon.pid", + StopTimeout: 10, + PodmanVersion: "CI", + CreateCommand: []string{"I'll get stripped", "container", "run", "--sdnotify=ignore", "--name", "jadda-jadda", "--hostname", "hello-world", "awesome-image:latest", "command", "arg1", "...", "argN", "foo=arg \"with \" space"}, + EnvVariable: define.EnvVariable, + GraphRoot: "/var/lib/containers/storage", + RunRoot: "/var/run/containers/storage", + }, + goodWithNameAndSdnotifyIgnore, + true, + false, + false, + false, + }, + {"good with name and --sdnotify ignore", + containerInfo{ + Executable: "/usr/bin/podman", + ServiceName: "jadda-jadda", + ContainerNameOrID: "jadda-jadda", + PIDFile: "/run/containers/storage/overlay-containers/639c53578af4d84b8800b4635fa4e680ee80fd67e0e6a2d4eea48d1e3230f401/userdata/conmon.pid", + StopTimeout: 10, + PodmanVersion: "CI", + CreateCommand: []string{"I'll get stripped", "container", "run", "--sdnotify", "ignore", "--name", "jadda-jadda", "--hostname", "hello-world", "awesome-image:latest", "command", "arg1", "...", "argN", "foo=arg \"with \" space"}, + EnvVariable: define.EnvVariable, + GraphRoot: "/var/lib/containers/storage", + RunRoot: "/var/run/containers/storage", + }, + goodWithNameAndSdnotifyIgnore, + true, + false, + false, + false, + }, {"good with explicit short detach param", containerInfo{ Executable: "/usr/bin/podman", @@ -1341,6 +1463,25 @@ WantedBy=default.target false, false, }, + {"good with -h hostname", + containerInfo{ + Executable: "/usr/bin/podman", + ServiceName: "jadda-jadda", + ContainerNameOrID: "jadda-jadda", + PIDFile: "/var/run/containers/storage/overlay-containers/639c53578af4d84b8800b4635fa4e680ee80fd67e0e6a2d4eea48d1e3230f401/userdata/conmon.pid", + StopTimeout: 10, + PodmanVersion: "CI", + GraphRoot: "/var/lib/containers/storage", + RunRoot: "/var/run/containers/storage", + CreateCommand: []string{"I'll get stripped", "create", "-h", "hostname", "awesome-image:latest"}, + EnvVariable: define.EnvVariable, + }, + goodNewWithHostname, + true, + false, + false, + false, + }, {"good template", containerInfo{ Executable: "/usr/bin/podman", |