diff options
Diffstat (limited to 'pkg')
39 files changed, 520 insertions, 371 deletions
diff --git a/pkg/api/handlers/compat/containers_create.go b/pkg/api/handlers/compat/containers_create.go index 162a98135..0b5cbd343 100644 --- a/pkg/api/handlers/compat/containers_create.go +++ b/pkg/api/handlers/compat/containers_create.go @@ -62,7 +62,7 @@ func CreateContainer(w http.ResponseWriter, r *http.Request) { } // Take body structure and convert to cliopts - cliOpts, args, err := common.ContainerCreateToContainerCLIOpts(body, rtc.Engine.CgroupManager) + cliOpts, args, err := common.ContainerCreateToContainerCLIOpts(body, rtc) if err != nil { utils.Error(w, "Something went wrong.", http.StatusInternalServerError, errors.Wrap(err, "make cli opts()")) return @@ -71,13 +71,12 @@ func CreateContainer(w http.ResponseWriter, r *http.Request) { imgNameOrID := newImage.ID() // if the img had multi names with the same sha256 ID, should use the InputName, not the ID if len(newImage.Names()) > 1 { - imageRef, err := utils.ParseDockerReference(resolvedName) - if err != nil { + if err := utils.IsRegistryReference(resolvedName); err != nil { utils.Error(w, http.StatusText(http.StatusBadRequest), http.StatusBadRequest, err) return } // maybe the InputName has no tag, so use full name to display - imgNameOrID = imageRef.DockerReference().String() + imgNameOrID = resolvedName } sg := specgen.NewSpecGenerator(imgNameOrID, cliOpts.RootFS) diff --git a/pkg/api/handlers/compat/events.go b/pkg/api/handlers/compat/events.go index 405e616c5..9fbac91e0 100644 --- a/pkg/api/handlers/compat/events.go +++ b/pkg/api/handlers/compat/events.go @@ -75,7 +75,7 @@ func GetEvents(w http.ResponseWriter, r *http.Request) { coder := json.NewEncoder(w) coder.SetEscapeHTML(true) - for stream := true; stream; stream = query.Stream { + for { select { case err := <-errorChannel: if err != nil { diff --git a/pkg/api/handlers/compat/images.go b/pkg/api/handlers/compat/images.go index 6c8af66c3..ac212474b 100644 --- a/pkg/api/handlers/compat/images.go +++ b/pkg/api/handlers/compat/images.go @@ -1,7 +1,6 @@ package compat import ( - "context" "encoding/json" "fmt" "io/ioutil" @@ -13,12 +12,12 @@ import ( "github.com/containers/common/libimage" "github.com/containers/common/pkg/config" "github.com/containers/image/v5/manifest" + "github.com/containers/image/v5/pkg/shortnames" "github.com/containers/image/v5/types" "github.com/containers/podman/v3/libpod" "github.com/containers/podman/v3/pkg/api/handlers" "github.com/containers/podman/v3/pkg/api/handlers/utils" "github.com/containers/podman/v3/pkg/auth" - "github.com/containers/podman/v3/pkg/channel" "github.com/containers/podman/v3/pkg/domain/entities" "github.com/containers/podman/v3/pkg/domain/infra/abi" "github.com/containers/storage" @@ -213,6 +212,11 @@ func CreateImageFromSrc(w http.ResponseWriter, r *http.Request) { }) } +type pullResult struct { + images []*libimage.Image + err error +} + func CreateImageFromImage(w http.ResponseWriter, r *http.Request) { // 200 no error // 404 repo does not exist or no read access @@ -234,6 +238,14 @@ func CreateImageFromImage(w http.ResponseWriter, r *http.Request) { fromImage := mergeNameAndTagOrDigest(query.FromImage, query.Tag) + // without this early check this function would return 200 but reported error via body stream soon after + // it's better to let caller know early via HTTP status code that request cannot be processed + _, err := shortnames.Resolve(runtime.SystemContext(), fromImage) + if err != nil { + utils.Error(w, "Something went wrong.", http.StatusBadRequest, errors.Wrap(err, "failed to resolve image name")) + return + } + authConf, authfile, key, err := auth.GetCredentials(r) if err != nil { utils.Error(w, "failed to retrieve repository credentials", http.StatusBadRequest, errors.Wrapf(err, "failed to parse %q header for %s", key, r.URL.String())) @@ -274,26 +286,14 @@ func CreateImageFromImage(w http.ResponseWriter, r *http.Request) { } pullOptions.Writer = os.Stderr // allows for debugging on the server - stderr := channel.NewWriter(make(chan []byte)) - defer stderr.Close() - progress := make(chan types.ProgressProperties) + pullOptions.Progress = progress - var img string - runCtx, cancel := context.WithCancel(context.Background()) + pullResChan := make(chan pullResult) go func() { - defer cancel() - pulledImages, err := runtime.LibimageRuntime().Pull(runCtx, fromImage, config.PullPolicyAlways, pullOptions) - if err != nil { - stderr.Write([]byte(err.Error() + "\n")) - } else { - if len(pulledImages) == 0 { - utils.Error(w, "Something went wrong.", http.StatusBadRequest, errors.New("internal error: no images pulled")) - return - } - img = pulledImages[0].ID() - } + pulledImages, err := runtime.LibimageRuntime().Pull(r.Context(), fromImage, config.PullPolicyAlways, pullOptions) + pullResChan <- pullResult{images: pulledImages, err: err} }() flush := func() { @@ -308,7 +308,6 @@ func CreateImageFromImage(w http.ResponseWriter, r *http.Request) { enc := json.NewEncoder(w) enc.SetEscapeHTML(true) - var failed bool loop: // break out of for/select infinite loop for { @@ -338,32 +337,31 @@ loop: // break out of for/select infinite loop } report.Id = e.Artifact.Digest.Encoded()[0:12] if err := enc.Encode(report); err != nil { - stderr.Write([]byte(err.Error())) - } - flush() - case e := <-stderr.Chan(): - failed = true - report.Error = string(e) - if err := enc.Encode(report); err != nil { logrus.Warnf("Failed to json encode error %q", err.Error()) } flush() - case <-runCtx.Done(): - if !failed { - if utils.IsLibpodRequest(r) { - report.Status = "Pull complete" + case pullRes := <-pullResChan: + err := pullRes.err + pulledImages := pullRes.images + if err != nil { + report.Error = err.Error() + } else { + if len(pulledImages) > 0 { + img := pulledImages[0].ID() + if utils.IsLibpodRequest(r) { + report.Status = "Pull complete" + } else { + report.Status = "Download complete" + } + report.Id = img[0:12] } else { - report.Status = "Download complete" - } - report.Id = img[0:12] - if err := enc.Encode(report); err != nil { - logrus.Warnf("Failed to json encode error %q", err.Error()) + report.Error = "internal error: no images pulled" } - flush() } - break loop // break out of for/select infinite loop - case <-r.Context().Done(): - // Client has closed connection + if err := enc.Encode(report); err != nil { + logrus.Warnf("Failed to json encode error %q", err.Error()) + } + flush() break loop // break out of for/select infinite loop } } diff --git a/pkg/api/handlers/compat/images_push.go b/pkg/api/handlers/compat/images_push.go index db02af445..62f8cdc77 100644 --- a/pkg/api/handlers/compat/images_push.go +++ b/pkg/api/handlers/compat/images_push.go @@ -1,7 +1,6 @@ package compat import ( - "context" "encoding/json" "fmt" "io/ioutil" @@ -12,7 +11,6 @@ import ( "github.com/containers/podman/v3/libpod" "github.com/containers/podman/v3/pkg/api/handlers/utils" "github.com/containers/podman/v3/pkg/auth" - "github.com/containers/podman/v3/pkg/channel" "github.com/containers/podman/v3/pkg/domain/entities" "github.com/containers/podman/v3/pkg/domain/infra/abi" "github.com/containers/storage" @@ -101,46 +99,33 @@ func PushImage(w http.ResponseWriter, r *http.Request) { destination = imageName } - errorWriter := channel.NewWriter(make(chan []byte)) - defer errorWriter.Close() - - statusWriter := channel.NewWriter(make(chan []byte)) - defer statusWriter.Close() - - runCtx, cancel := context.WithCancel(context.Background()) - var failed bool - - go func() { - defer cancel() - - statusWriter.Write([]byte(fmt.Sprintf("The push refers to repository [%s]", imageName))) - - err := imageEngine.Push(runCtx, imageName, destination, options) - if err != nil { - if errors.Cause(err) != storage.ErrImageUnknown { - errorWriter.Write([]byte("An image does not exist locally with the tag: " + imageName)) - } else { - errorWriter.Write([]byte(err.Error())) - } - } - }() - - flush := func() { - if flusher, ok := w.(http.Flusher); ok { - flusher.Flush() - } + flush := func() {} + if flusher, ok := w.(http.Flusher); ok { + flush = flusher.Flush } w.WriteHeader(http.StatusOK) w.Header().Add("Content-Type", "application/json") flush() + var report jsonmessage.JSONMessage enc := json.NewEncoder(w) enc.SetEscapeHTML(true) + report.Status = fmt.Sprintf("The push refers to repository [%s]", imageName) + if err := enc.Encode(report); err != nil { + logrus.Warnf("Failed to json encode error %q", err.Error()) + } + flush() + + pushErrChan := make(chan error) + go func() { + pushErrChan <- imageEngine.Push(r.Context(), imageName, destination, options) + }() + loop: // break out of for/select infinite loop for { - var report jsonmessage.JSONMessage + report = jsonmessage.JSONMessage{} select { case e := <-options.Progress: @@ -160,43 +145,50 @@ loop: // break out of for/select infinite loop } report.ID = e.Artifact.Digest.Encoded()[0:12] if err := enc.Encode(report); err != nil { - errorWriter.Write([]byte(err.Error())) + logrus.Warnf("Failed to json encode error %q", err.Error()) } flush() - case e := <-statusWriter.Chan(): - report.Status = string(e) - if err := enc.Encode(report); err != nil { - errorWriter.Write([]byte(err.Error())) + case err := <-pushErrChan: + if err != nil { + var msg string + if errors.Cause(err) != storage.ErrImageUnknown { + msg = "An image does not exist locally with the tag: " + imageName + } else { + msg = err.Error() + } + report.Error = &jsonmessage.JSONError{ + Message: msg, + } + report.ErrorMessage = msg + if err := enc.Encode(report); err != nil { + logrus.Warnf("Failed to json encode error %q", err.Error()) + } + flush() + break loop } - flush() - case e := <-errorWriter.Chan(): - failed = true - report.Error = &jsonmessage.JSONError{ - Message: string(e), + + digestBytes, err := ioutil.ReadAll(digestFile) + if err != nil { + report.Error = &jsonmessage.JSONError{ + Message: err.Error(), + } + report.ErrorMessage = err.Error() + if err := enc.Encode(report); err != nil { + logrus.Warnf("Failed to json encode error %q", err.Error()) + } + flush() + break loop } - report.ErrorMessage = string(e) + tag := query.Tag + if tag == "" { + tag = "latest" + } + report.Status = fmt.Sprintf("%s: digest: %s", tag, string(digestBytes)) if err := enc.Encode(report); err != nil { logrus.Warnf("Failed to json encode error %q", err.Error()) } + flush() - case <-runCtx.Done(): - if !failed { - digestBytes, err := ioutil.ReadAll(digestFile) - if err == nil { - tag := query.Tag - if tag == "" { - tag = "latest" - } - report.Status = fmt.Sprintf("%s: digest: %s", tag, string(digestBytes)) - if err := enc.Encode(report); err != nil { - logrus.Warnf("Failed to json encode error %q", err.Error()) - } - flush() - } - } - break loop // break out of for/select infinite loop - case <-r.Context().Done(): - // Client has closed connection break loop // break out of for/select infinite loop } } diff --git a/pkg/api/handlers/compat/resize.go b/pkg/api/handlers/compat/resize.go index 23ed33a22..f65e313fc 100644 --- a/pkg/api/handlers/compat/resize.go +++ b/pkg/api/handlers/compat/resize.go @@ -46,20 +46,13 @@ func ResizeTTY(w http.ResponseWriter, r *http.Request) { utils.ContainerNotFound(w, name, err) return } - if state, err := ctnr.State(); err != nil { - utils.InternalServerError(w, errors.Wrapf(err, "cannot obtain container state")) - return - } else if state != define.ContainerStateRunning && !query.IgnoreNotRunning { - utils.Error(w, "Container not running", http.StatusConflict, - fmt.Errorf("container %q in wrong state %q", name, state.String())) - return - } - // If container is not running, ignore since this can be a race condition, and is expected if err := ctnr.AttachResize(sz); err != nil { - if errors.Cause(err) != define.ErrCtrStateInvalid || !query.IgnoreNotRunning { + if errors.Cause(err) != define.ErrCtrStateInvalid { utils.InternalServerError(w, errors.Wrapf(err, "cannot resize container")) - return + } else { + utils.Error(w, "Container not running", http.StatusConflict, err) } + return } // This is not a 204, even though we write nothing, for compatibility // reasons. diff --git a/pkg/api/handlers/libpod/images.go b/pkg/api/handlers/libpod/images.go index a90408bfd..fc6ab4b4c 100644 --- a/pkg/api/handlers/libpod/images.go +++ b/pkg/api/handlers/libpod/images.go @@ -482,7 +482,7 @@ func PushImage(w http.ResponseWriter, r *http.Request) { destination = source } - if _, err := utils.ParseDockerReference(destination); err != nil { + if err := utils.IsRegistryReference(destination); err != nil { utils.Error(w, http.StatusText(http.StatusBadRequest), http.StatusBadRequest, err) return } diff --git a/pkg/api/handlers/libpod/images_pull.go b/pkg/api/handlers/libpod/images_pull.go index 7545ba235..e88b53a4b 100644 --- a/pkg/api/handlers/libpod/images_pull.go +++ b/pkg/api/handlers/libpod/images_pull.go @@ -48,7 +48,7 @@ func ImagesPull(w http.ResponseWriter, r *http.Request) { } // Make sure that the reference has no transport or the docker one. - if _, err := utils.ParseDockerReference(query.Reference); err != nil { + if err := utils.IsRegistryReference(query.Reference); err != nil { utils.Error(w, http.StatusText(http.StatusBadRequest), http.StatusBadRequest, err) return } @@ -85,7 +85,7 @@ func ImagesPull(w http.ResponseWriter, r *http.Request) { var pulledImages []*libimage.Image var pullError error - runCtx, cancel := context.WithCancel(context.Background()) + runCtx, cancel := context.WithCancel(r.Context()) go func() { defer cancel() pulledImages, pullError = runtime.LibimageRuntime().Pull(runCtx, query.Reference, config.PullPolicyAlways, pullOptions) diff --git a/pkg/api/handlers/libpod/manifests.go b/pkg/api/handlers/libpod/manifests.go index f21eb2e80..2f36db583 100644 --- a/pkg/api/handlers/libpod/manifests.go +++ b/pkg/api/handlers/libpod/manifests.go @@ -169,7 +169,7 @@ func ManifestPush(w http.ResponseWriter, r *http.Request) { errors.Wrapf(err, "failed to parse parameters for %s", r.URL.String())) return } - if _, err := utils.ParseDockerReference(query.Destination); err != nil { + if err := utils.IsRegistryReference(query.Destination); err != nil { utils.Error(w, http.StatusText(http.StatusBadRequest), http.StatusBadRequest, err) return } diff --git a/pkg/api/handlers/libpod/swagger.go b/pkg/api/handlers/libpod/swagger.go index 9450a70d9..19eced986 100644 --- a/pkg/api/handlers/libpod/swagger.go +++ b/pkg/api/handlers/libpod/swagger.go @@ -95,7 +95,7 @@ type swagInfoResponse struct { // swagger:response NetworkRmReport type swagNetworkRmReport struct { // in:body - Body entities.NetworkRmReport + Body []entities.NetworkRmReport } // Network inspect diff --git a/pkg/api/handlers/utils/images.go b/pkg/api/handlers/utils/images.go index 2662cd368..2a1908d63 100644 --- a/pkg/api/handlers/utils/images.go +++ b/pkg/api/handlers/utils/images.go @@ -15,22 +15,19 @@ import ( "github.com/pkg/errors" ) -// ParseDockerReference parses the specified image name to a -// `types.ImageReference` and enforces it to refer to a docker-transport -// reference. -func ParseDockerReference(name string) (types.ImageReference, error) { - dockerPrefix := fmt.Sprintf("%s://", docker.Transport.Name()) +// IsRegistryReference checks if the specified name points to the "docker://" +// transport. If it points to no supported transport, we'll assume a +// non-transport reference pointing to an image (e.g., "fedora:latest"). +func IsRegistryReference(name string) error { imageRef, err := alltransports.ParseImageName(name) - if err == nil && imageRef.Transport().Name() != docker.Transport.Name() { - return nil, errors.Errorf("reference %q must be a docker reference", name) - } else if err != nil { - origErr := err - imageRef, err = alltransports.ParseImageName(fmt.Sprintf("%s%s", dockerPrefix, name)) - if err != nil { - return nil, errors.Wrapf(origErr, "reference %q must be a docker reference", name) - } + if err != nil { + // No supported transport -> assume a docker-stype reference. + return nil } - return imageRef, nil + if imageRef.Transport().Name() == docker.Transport.Name() { + return nil + } + return errors.Errorf("unsupport transport %s in %q: only docker transport is supported", imageRef.Transport().Name(), name) } // ParseStorageReference parses the specified image name to a diff --git a/pkg/api/server/handler_api.go b/pkg/api/server/handler_api.go index 28b8706a8..becc674c0 100644 --- a/pkg/api/server/handler_api.go +++ b/pkg/api/server/handler_api.go @@ -63,6 +63,12 @@ func (s *APIServer) APIHandler(h http.HandlerFunc) http.HandlerFunc { w.Header().Set("Libpod-API-Version", lv) w.Header().Set("Server", "Libpod/"+lv+" ("+runtime.GOOS+")") + 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") + } + h(w, r) logrus.Debugf("APIHandler(%s) -- %s %s END", rid, r.Method, r.URL.String()) } diff --git a/pkg/api/server/register_containers.go b/pkg/api/server/register_containers.go index aa999905e..88ebb4df5 100644 --- a/pkg/api/server/register_containers.go +++ b/pkg/api/server/register_containers.go @@ -1364,6 +1364,8 @@ func (s *APIServer) registerContainersHandlers(r *mux.Router) error { // $ref: "#/responses/ok" // 404: // $ref: "#/responses/NoSuchContainer" + // 409: + // $ref: "#/responses/ConflictError" // 500: // $ref: "#/responses/InternalError" r.HandleFunc(VersionedPath("/libpod/containers/{name}/resize"), s.APIHandler(compat.ResizeTTY)).Methods(http.MethodPost) diff --git a/pkg/api/server/register_networks.go b/pkg/api/server/register_networks.go index 9a5ccb789..4d9806316 100644 --- a/pkg/api/server/register_networks.go +++ b/pkg/api/server/register_networks.go @@ -241,7 +241,9 @@ func (s *APIServer) registerNetworkHandlers(r *mux.Router) error { // tags: // - networks // summary: List networks - // description: Display summary of network configurations + // description: | + // Display summary of network configurations. + // - In a 200 response, all of the fields named Bytes are returned as a Base64 encoded string. // parameters: // - in: query // name: filters diff --git a/pkg/api/server/server.go b/pkg/api/server/server.go index 972541bc6..1e8faf8f5 100644 --- a/pkg/api/server/server.go +++ b/pkg/api/server/server.go @@ -34,10 +34,12 @@ type APIServer struct { context.CancelFunc // Stop APIServer idleTracker *idle.Tracker // Track connections to support idle shutdown pprof *http.Server // Sidecar http server for providing performance data + CorsHeaders string // Inject CORS headers to each request } // Number of seconds to wait for next request, if exceeded shutdown server const ( + DefaultCorsHeaders = "" DefaultServiceDuration = 300 * time.Second UnlimitedServiceDuration = 0 * time.Second ) @@ -45,17 +47,22 @@ const ( // shutdownOnce ensures Shutdown() may safely be called from several go routines var shutdownOnce sync.Once +type Options struct { + Timeout time.Duration + CorsHeaders string +} + // NewServer will create and configure a new API server with all defaults func NewServer(runtime *libpod.Runtime) (*APIServer, error) { - return newServer(runtime, DefaultServiceDuration, nil) + return newServer(runtime, DefaultServiceDuration, nil, DefaultCorsHeaders) } // NewServerWithSettings will create and configure a new API server using provided settings -func NewServerWithSettings(runtime *libpod.Runtime, duration time.Duration, listener *net.Listener) (*APIServer, error) { - return newServer(runtime, duration, listener) +func NewServerWithSettings(runtime *libpod.Runtime, listener *net.Listener, opts Options) (*APIServer, error) { + return newServer(runtime, opts.Timeout, listener, opts.CorsHeaders) } -func newServer(runtime *libpod.Runtime, duration time.Duration, listener *net.Listener) (*APIServer, error) { +func newServer(runtime *libpod.Runtime, duration time.Duration, listener *net.Listener, corsHeaders string) (*APIServer, error) { // If listener not provided try socket activation protocol if listener == nil { if _, found := os.LookupEnv("LISTEN_PID"); !found { @@ -71,6 +78,11 @@ func newServer(runtime *libpod.Runtime, duration time.Duration, listener *net.Li } listener = &listeners[0] } + if corsHeaders == "" { + logrus.Debug("CORS Headers were not set") + } else { + logrus.Debugf("CORS Headers were set to %s", corsHeaders) + } logrus.Infof("API server listening on %q", (*listener).Addr()) router := mux.NewRouter().UseEncodedPath() @@ -88,6 +100,7 @@ func newServer(runtime *libpod.Runtime, duration time.Duration, listener *net.Li idleTracker: idle, Listener: *listener, Runtime: runtime, + CorsHeaders: corsHeaders, } router.NotFoundHandler = http.HandlerFunc( diff --git a/pkg/bindings/containers/attach.go b/pkg/bindings/containers/attach.go index fd8a7011d..adef1e7c8 100644 --- a/pkg/bindings/containers/attach.go +++ b/pkg/bindings/containers/attach.go @@ -138,7 +138,7 @@ func Attach(ctx context.Context, nameOrID string, stdin io.Reader, stdout io.Wri winCtx, winCancel := context.WithCancel(ctx) defer winCancel() - go attachHandleResize(ctx, winCtx, winChange, false, nameOrID, file) + attachHandleResize(ctx, winCtx, winChange, false, nameOrID, file) } // If we are attaching around a start, we need to "signal" @@ -327,32 +327,38 @@ func (f *rawFormatter) Format(entry *logrus.Entry) ([]byte, error) { return append(buffer, '\r'), nil } -// This is intended to be run as a goroutine, handling resizing for a container -// or exec session. +// This is intended to not be run as a goroutine, handling resizing for a container +// or exec session. It will call resize once and then starts a goroutine which calls resize on winChange func attachHandleResize(ctx, winCtx context.Context, winChange chan os.Signal, isExec bool, id string, file *os.File) { - // Prime the pump, we need one reset to ensure everything is ready - winChange <- sig.SIGWINCH - for { - select { - case <-winCtx.Done(): - return - case <-winChange: - w, h, err := terminal.GetSize(int(file.Fd())) - if err != nil { - logrus.Warnf("failed to obtain TTY size: %v", err) - } + resize := func() { + w, h, err := terminal.GetSize(int(file.Fd())) + if err != nil { + logrus.Warnf("failed to obtain TTY size: %v", err) + } - var resizeErr error - if isExec { - resizeErr = ResizeExecTTY(ctx, id, new(ResizeExecTTYOptions).WithHeight(h).WithWidth(w)) - } else { - resizeErr = ResizeContainerTTY(ctx, id, new(ResizeTTYOptions).WithHeight(h).WithWidth(w)) - } - if resizeErr != nil { - logrus.Warnf("failed to resize TTY: %v", resizeErr) - } + var resizeErr error + if isExec { + resizeErr = ResizeExecTTY(ctx, id, new(ResizeExecTTYOptions).WithHeight(h).WithWidth(w)) + } else { + resizeErr = ResizeContainerTTY(ctx, id, new(ResizeTTYOptions).WithHeight(h).WithWidth(w)) + } + if resizeErr != nil { + logrus.Warnf("failed to resize TTY: %v", resizeErr) } } + + resize() + + go func() { + for { + select { + case <-winCtx.Done(): + return + case <-winChange: + resize() + } + } + }() } // Configure the given terminal for raw mode @@ -457,7 +463,7 @@ func ExecStartAndAttach(ctx context.Context, sessionID string, options *ExecStar winCtx, winCancel := context.WithCancel(ctx) defer winCancel() - go attachHandleResize(ctx, winCtx, winChange, true, sessionID, terminalFile) + attachHandleResize(ctx, winCtx, winChange, true, sessionID, terminalFile) } if options.GetAttachInput() { diff --git a/pkg/bindings/images/build.go b/pkg/bindings/images/build.go index b56afbceb..346d55c47 100644 --- a/pkg/bindings/images/build.go +++ b/pkg/bindings/images/build.go @@ -450,7 +450,7 @@ func nTar(excludes []string, sources ...string) (io.ReadCloser, error) { hdr.Typeflag = tar.TypeLink hdr.Linkname = orig hdr.Size = 0 - + hdr.Name = name return tw.WriteHeader(hdr) } f, err := os.Open(path) diff --git a/pkg/checkpoint/checkpoint_restore.go b/pkg/checkpoint/checkpoint_restore.go index 7a8f71c66..0d45cab5f 100644 --- a/pkg/checkpoint/checkpoint_restore.go +++ b/pkg/checkpoint/checkpoint_restore.go @@ -11,6 +11,7 @@ import ( "github.com/containers/podman/v3/libpod" "github.com/containers/podman/v3/pkg/domain/entities" "github.com/containers/podman/v3/pkg/errorhandling" + "github.com/containers/podman/v3/pkg/specgen/generate" "github.com/containers/storage/pkg/archive" spec "github.com/opencontainers/runtime-spec/specs-go" "github.com/pkg/errors" @@ -95,6 +96,14 @@ func CRImportCheckpoint(ctx context.Context, runtime *libpod.Runtime, restoreOpt newName = true } + if len(restoreOptions.PublishPorts) > 0 { + ports, _, _, err := generate.ParsePortMapping(restoreOptions.PublishPorts) + if err != nil { + return nil, err + } + ctrConfig.PortMappings = ports + } + pullOptions := &libimage.PullOptions{} pullOptions.Writer = os.Stderr if _, err := runtime.LibimageRuntime().Pull(ctx, ctrConfig.RootfsImageName, config.PullPolicyMissing, pullOptions); err != nil { diff --git a/pkg/domain/entities/containers.go b/pkg/domain/entities/containers.go index eacc14d50..8ed9b9b61 100644 --- a/pkg/domain/entities/containers.go +++ b/pkg/domain/entities/containers.go @@ -9,6 +9,7 @@ import ( "github.com/containers/image/v5/types" "github.com/containers/podman/v3/libpod/define" "github.com/containers/podman/v3/pkg/specgen" + "github.com/containers/storage/pkg/archive" "github.com/cri-o/ocicni/pkg/ocicni" ) @@ -178,6 +179,7 @@ type CheckpointOptions struct { TCPEstablished bool PreCheckPoint bool WithPrevious bool + Compression archive.Compression } type CheckpointReport struct { @@ -197,6 +199,7 @@ type RestoreOptions struct { Name string TCPEstablished bool ImportPrevious string + PublishPorts []specgen.PortMapping } type RestoreReport struct { diff --git a/pkg/domain/entities/events.go b/pkg/domain/entities/events.go index 930ca53ae..5e7cc9ad1 100644 --- a/pkg/domain/entities/events.go +++ b/pkg/domain/entities/events.go @@ -30,29 +30,41 @@ func ConvertToLibpodEvent(e Event) *libpodEvents.Event { if err != nil { return nil } + image := e.Actor.Attributes["image"] + name := e.Actor.Attributes["name"] + details := e.Actor.Attributes + delete(details, "image") + delete(details, "name") + delete(details, "containerExitCode") return &libpodEvents.Event{ ContainerExitCode: exitCode, ID: e.Actor.ID, - Image: e.Actor.Attributes["image"], - Name: e.Actor.Attributes["name"], + Image: image, + Name: name, Status: status, Time: time.Unix(e.Time, e.TimeNano), Type: t, + Details: libpodEvents.Details{ + Attributes: details, + }, } } // ConvertToEntitiesEvent converts a libpod event to an entities one. func ConvertToEntitiesEvent(e libpodEvents.Event) *Event { + attributes := e.Details.Attributes + if attributes == nil { + attributes = make(map[string]string) + } + attributes["image"] = e.Image + attributes["name"] = e.Name + attributes["containerExitCode"] = strconv.Itoa(e.ContainerExitCode) return &Event{dockerEvents.Message{ Type: e.Type.String(), Action: e.Status.String(), Actor: dockerEvents.Actor{ - ID: e.ID, - Attributes: map[string]string{ - "image": e.Image, - "name": e.Name, - "containerExitCode": strconv.Itoa(e.ContainerExitCode), - }, + ID: e.ID, + Attributes: attributes, }, Scope: "local", Time: e.Time.Unix(), diff --git a/pkg/domain/entities/system.go b/pkg/domain/entities/system.go index 31a6185dc..cca4bf44e 100644 --- a/pkg/domain/entities/system.go +++ b/pkg/domain/entities/system.go @@ -11,9 +11,10 @@ import ( // ServiceOptions provides the input for starting an API Service type ServiceOptions struct { - URI string // Path to unix domain socket service should listen on - Timeout time.Duration // duration of inactivity the service should wait before shutting down - Command *cobra.Command // CLI command provided. Used in V1 code + URI string // Path to unix domain socket service should listen on + Timeout time.Duration // duration of inactivity the service should wait before shutting down + Command *cobra.Command // CLI command provided. Used in V1 code + CorsHeaders string // CORS headers } // SystemPruneOptions provides options to prune system. diff --git a/pkg/domain/infra/abi/containers.go b/pkg/domain/infra/abi/containers.go index 237a43441..4908e72f6 100644 --- a/pkg/domain/infra/abi/containers.go +++ b/pkg/domain/infra/abi/containers.go @@ -483,6 +483,7 @@ func (ic *ContainerEngine) ContainerCheckpoint(ctx context.Context, namesOrIds [ KeepRunning: options.LeaveRunning, PreCheckPoint: options.PreCheckPoint, WithPrevious: options.WithPrevious, + Compression: options.Compression, } if options.All { diff --git a/pkg/domain/infra/abi/network.go b/pkg/domain/infra/abi/network.go index 33ab280e5..7900caaa6 100644 --- a/pkg/domain/infra/abi/network.go +++ b/pkg/domain/infra/abi/network.go @@ -11,7 +11,7 @@ import ( ) func (ic *ContainerEngine) NetworkList(ctx context.Context, options entities.NetworkListOptions) ([]*entities.NetworkListReport, error) { - var reports []*entities.NetworkListReport + reports := make([]*entities.NetworkListReport, 0) config, err := ic.Libpod.GetConfig() if err != nil { diff --git a/pkg/machine/config.go b/pkg/machine/config.go index 652229963..58794ce42 100644 --- a/pkg/machine/config.go +++ b/pkg/machine/config.go @@ -32,6 +32,7 @@ var ( ErrVMAlreadyExists = errors.New("VM already exists") ErrVMAlreadyRunning = errors.New("VM already running") ErrMultipleActiveVM = errors.New("only one VM can be active at a time") + ForwarderBinaryName = "gvproxy" ) type Download struct { diff --git a/pkg/machine/ignition.go b/pkg/machine/ignition.go index 00068a136..a5c7210af 100644 --- a/pkg/machine/ignition.go +++ b/pkg/machine/ignition.go @@ -118,6 +118,7 @@ func getDirs(usrName string) []Directory { // in one swoop, then the leading dirs are creates as root. newDirs := []string{ "/home/" + usrName + "/.config", + "/home/" + usrName + "/.config/containers", "/home/" + usrName + "/.config/systemd", "/home/" + usrName + "/.config/systemd/user", "/home/" + usrName + "/.config/systemd/user/default.target.wants", @@ -159,6 +160,22 @@ func getFiles(usrName string) []File { }, }) + // Set containers.conf up for core user to use cni networks + // by default + files = append(files, File{ + Node: Node{ + Group: getNodeGrp(usrName), + Path: "/home/" + usrName + "/.config/containers/containers.conf", + User: getNodeUsr(usrName), + }, + FileEmbedded1: FileEmbedded1{ + Append: nil, + Contents: Resource{ + Source: strToPtr("data:,%5Bcontainers%5D%0D%0Anetns%3D%22bridge%22%0D%0Arootless_networking%3D%22cni%22"), + }, + Mode: intToPtr(484), + }, + }) // Add a file into linger files = append(files, File{ Node: Node{ diff --git a/pkg/machine/qemu/machine.go b/pkg/machine/qemu/machine.go index 269a2a2da..31c355d4a 100644 --- a/pkg/machine/qemu/machine.go +++ b/pkg/machine/qemu/machine.go @@ -13,6 +13,8 @@ import ( "strings" "time" + "github.com/containers/podman/v3/pkg/rootless" + "github.com/containers/podman/v3/pkg/machine" "github.com/containers/podman/v3/utils" "github.com/containers/storage/pkg/homedir" @@ -82,9 +84,10 @@ func NewMachine(opts machine.InitOptions) (machine.VM, error) { cmd = append(cmd, []string{"-qmp", monitor.Network + ":/" + monitor.Address + ",server=on,wait=off"}...) // Add network - cmd = append(cmd, "-nic", "user,model=virtio,hostfwd=tcp::"+strconv.Itoa(vm.Port)+"-:22") - - socketPath, err := getSocketDir() + // Right now the mac address is hardcoded so that the host networking gives it a specific IP address. This is + // why we can only run one vm at a time right now + cmd = append(cmd, []string{"-netdev", "socket,id=vlan,fd=3", "-device", "virtio-net-pci,netdev=vlan,mac=5a:94:ef:e4:0c:ee"}...) + socketPath, err := getRuntimeDir() if err != nil { return nil, err } @@ -235,12 +238,35 @@ func (v *MachineVM) Init(opts machine.InitOptions) error { // Start executes the qemu command line and forks it func (v *MachineVM) Start(name string, _ machine.StartOptions) error { var ( - conn net.Conn - err error - wait time.Duration = time.Millisecond * 500 + conn net.Conn + err error + qemuSocketConn net.Conn + wait time.Duration = time.Millisecond * 500 ) + if err := v.startHostNetworking(); err != nil { + return errors.Errorf("unable to start host networking: %q", err) + } + qemuSocketPath, _, err := v.getSocketandPid() + + for i := 0; i < 6; i++ { + qemuSocketConn, err = net.Dial("unix", qemuSocketPath) + if err == nil { + break + } + time.Sleep(wait) + wait++ + } + if err != nil { + return err + } + + fd, err := qemuSocketConn.(*net.UnixConn).File() + if err != nil { + return err + } + attr := new(os.ProcAttr) - files := []*os.File{os.Stdin, os.Stdout, os.Stderr} + files := []*os.File{os.Stdin, os.Stdout, os.Stderr, fd} attr.Files = files logrus.Debug(v.CmdLine) cmd := v.CmdLine @@ -256,7 +282,7 @@ func (v *MachineVM) Start(name string, _ machine.StartOptions) error { return err } fmt.Println("Waiting for VM ...") - socketPath, err := getSocketDir() + socketPath, err := getRuntimeDir() if err != nil { return err } @@ -309,16 +335,42 @@ func (v *MachineVM) Stop(name string, _ machine.StopOptions) error { logrus.Error(err) } }() - _, err = qmpMonitor.Run(input) - return err + if _, err = qmpMonitor.Run(input); err != nil { + return err + } + _, pidFile, err := v.getSocketandPid() + if err != nil { + return err + } + if _, err := os.Stat(pidFile); os.IsNotExist(err) { + logrus.Infof("pid file %s does not exist", pidFile) + return nil + } + pidString, err := ioutil.ReadFile(pidFile) + if err != nil { + return err + } + pidNum, err := strconv.Atoi(string(pidString)) + if err != nil { + return err + } + + p, err := os.FindProcess(pidNum) + if p == nil && err != nil { + return err + } + return p.Kill() } // NewQMPMonitor creates the monitor subsection of our vm func NewQMPMonitor(network, name string, timeout time.Duration) (Monitor, error) { - rtDir, err := getSocketDir() + rtDir, err := getRuntimeDir() if err != nil { return Monitor{}, err } + if !rootless.IsRootless() { + rtDir = "/run" + } rtDir = filepath.Join(rtDir, "podman") if _, err := os.Stat(filepath.Join(rtDir)); os.IsNotExist(err) { // TODO 0644 is fine on linux but macos is weird @@ -408,7 +460,7 @@ func (v *MachineVM) SSH(name string, opts machine.SSHOptions) error { sshDestination := v.RemoteUsername + "@localhost" port := strconv.Itoa(v.Port) - args := []string{"-i", v.IdentityPath, "-p", port, sshDestination} + args := []string{"-i", v.IdentityPath, "-p", port, sshDestination, "-o", "UserKnownHostsFile /dev/null", "-o", "StrictHostKeyChecking no"} if len(opts.Args) > 0 { args = append(args, opts.Args...) } else { @@ -533,3 +585,47 @@ func CheckActiveVM() (bool, string, error) { } return false, "", nil } + +// startHostNetworking runs a binary on the host system that allows users +// to setup port forwarding to the podman virtual machine +func (v *MachineVM) startHostNetworking() error { + binary, err := exec.LookPath(machine.ForwarderBinaryName) + if err != nil { + return err + } + // Listen on all at port 7777 for setting up and tearing + // down forwarding + listenSocket := "tcp://0.0.0.0:7777" + qemuSocket, pidFile, err := v.getSocketandPid() + if err != nil { + return err + } + attr := new(os.ProcAttr) + // Pass on stdin, stdout, stderr + files := []*os.File{os.Stdin, os.Stdout, os.Stderr} + attr.Files = files + cmd := []string{binary} + cmd = append(cmd, []string{"-listen", listenSocket, "-listen-qemu", fmt.Sprintf("unix://%s", qemuSocket), "-pid-file", pidFile}...) + // Add the ssh port + cmd = append(cmd, []string{"-ssh-port", fmt.Sprintf("%d", v.Port)}...) + if logrus.GetLevel() == logrus.DebugLevel { + cmd = append(cmd, "--debug") + fmt.Println(cmd) + } + _, err = os.StartProcess(cmd[0], cmd, attr) + return err +} + +func (v *MachineVM) getSocketandPid() (string, string, error) { + rtPath, err := getRuntimeDir() + if err != nil { + return "", "", err + } + if !rootless.IsRootless() { + rtPath = "/run" + } + socketDir := filepath.Join(rtPath, "podman") + pidFile := filepath.Join(socketDir, fmt.Sprintf("%s.pid", v.Name)) + qemuSocket := filepath.Join(socketDir, fmt.Sprintf("qemu_%s.sock", v.Name)) + return qemuSocket, pidFile, nil +} diff --git a/pkg/machine/qemu/options_darwin.go b/pkg/machine/qemu/options_darwin.go index 46ccf24cb..440937131 100644 --- a/pkg/machine/qemu/options_darwin.go +++ b/pkg/machine/qemu/options_darwin.go @@ -6,7 +6,7 @@ import ( "github.com/pkg/errors" ) -func getSocketDir() (string, error) { +func getRuntimeDir() (string, error) { tmpDir, ok := os.LookupEnv("TMPDIR") if !ok { return "", errors.New("unable to resolve TMPDIR") diff --git a/pkg/machine/qemu/options_darwin_amd64.go b/pkg/machine/qemu/options_darwin_amd64.go index 69f7982b2..ee1036291 100644 --- a/pkg/machine/qemu/options_darwin_amd64.go +++ b/pkg/machine/qemu/options_darwin_amd64.go @@ -5,7 +5,7 @@ var ( ) func (v *MachineVM) addArchOptions() []string { - opts := []string{"-cpu", "host"} + opts := []string{"-machine", "q35,accel=hvf:tcg"} return opts } diff --git a/pkg/machine/qemu/options_linux.go b/pkg/machine/qemu/options_linux.go index 0a2e40d8f..c73a68cc6 100644 --- a/pkg/machine/qemu/options_linux.go +++ b/pkg/machine/qemu/options_linux.go @@ -1,7 +1,13 @@ package qemu -import "github.com/containers/podman/v3/pkg/util" +import ( + "github.com/containers/podman/v3/pkg/rootless" + "github.com/containers/podman/v3/pkg/util" +) -func getSocketDir() (string, error) { +func getRuntimeDir() (string, error) { + if !rootless.IsRootless() { + return "/run", nil + } return util.GetRuntimeDir() } diff --git a/pkg/rootless/rootless_linux.c b/pkg/rootless/rootless_linux.c index 918b9a7e6..0d1d6e93e 100644 --- a/pkg/rootless/rootless_linux.c +++ b/pkg/rootless/rootless_linux.c @@ -311,10 +311,10 @@ static void __attribute__((constructor)) init() do_socket_activation = true; saved_systemd_listen_pid = strdup(listen_pid); saved_systemd_listen_fds = strdup(listen_fds); - saved_systemd_listen_fdnames = strdup(listen_fdnames); + if (listen_fdnames != NULL) + saved_systemd_listen_fdnames = strdup(listen_fdnames); if (saved_systemd_listen_pid == NULL - || saved_systemd_listen_fds == NULL - || saved_systemd_listen_fdnames == NULL) + || saved_systemd_listen_fds == NULL) { fprintf (stderr, "save socket listen environments error: %s\n", strerror (errno)); _exit (EXIT_FAILURE); @@ -700,7 +700,9 @@ reexec_userns_join (int pid_to_join, char *pause_pid_file_path) sprintf (s, "%d", getpid()); setenv ("LISTEN_PID", s, true); setenv ("LISTEN_FDS", saved_systemd_listen_fds, true); - setenv ("LISTEN_FDNAMES", saved_systemd_listen_fdnames, true); + // Setting fdnames is optional for systemd_socket_activation + if (saved_systemd_listen_fdnames != NULL) + setenv ("LISTEN_FDNAMES", saved_systemd_listen_fdnames, true); } setenv ("_CONTAINERS_USERNS_CONFIGURED", "init", 1); @@ -896,7 +898,9 @@ reexec_in_user_namespace (int ready, char *pause_pid_file_path, char *file_to_re sprintf (s, "%d", getpid()); setenv ("LISTEN_PID", s, true); setenv ("LISTEN_FDS", saved_systemd_listen_fds, true); - setenv ("LISTEN_FDNAMES", saved_systemd_listen_fdnames, true); + // Setting fdnames is optional for systemd_socket_activation + if (saved_systemd_listen_fdnames != NULL) + setenv ("LISTEN_FDNAMES", saved_systemd_listen_fdnames, true); } setenv ("_CONTAINERS_USERNS_CONFIGURED", "init", 1); diff --git a/pkg/specgen/generate/kube/kube.go b/pkg/specgen/generate/kube/kube.go index 054388384..fb563f935 100644 --- a/pkg/specgen/generate/kube/kube.go +++ b/pkg/specgen/generate/kube/kube.go @@ -12,6 +12,7 @@ import ( "github.com/containers/common/pkg/secrets" ann "github.com/containers/podman/v3/pkg/annotations" "github.com/containers/podman/v3/pkg/specgen" + "github.com/containers/podman/v3/pkg/specgen/generate" "github.com/containers/podman/v3/pkg/util" spec "github.com/opencontainers/runtime-spec/specs-go" "github.com/pkg/errors" @@ -182,6 +183,19 @@ func ToSpecGen(ctx context.Context, opts *CtrSpecGenOptions) (*specgen.SpecGener if imageData.Config.WorkingDir != "" { s.WorkDir = imageData.Config.WorkingDir } + if s.User == "" { + s.User = imageData.Config.User + } + + exposed, err := generate.GenExposedPorts(imageData.Config.ExposedPorts) + if err != nil { + return nil, err + } + + for k, v := range s.Expose { + exposed[k] = v + } + s.Expose = exposed // Pull entrypoint and cmd from image s.Entrypoint = imageData.Config.Entrypoint s.Command = imageData.Config.Cmd diff --git a/pkg/specgen/generate/namespaces.go b/pkg/specgen/generate/namespaces.go index 278f35c22..f41186ae4 100644 --- a/pkg/specgen/generate/namespaces.go +++ b/pkg/specgen/generate/namespaces.go @@ -66,7 +66,7 @@ func GetDefaultNamespaceMode(nsType string, cfg *config.Config, pod *libpod.Pod) case "cgroup": return specgen.ParseCgroupNamespace(cfg.Containers.CgroupNS) case "net": - ns, _, err := specgen.ParseNetworkNamespace(cfg.Containers.NetNS) + ns, _, err := specgen.ParseNetworkNamespace(cfg.Containers.NetNS, cfg.Containers.RootlessNetworking == "cni") return ns, err } diff --git a/pkg/specgen/generate/pod_create.go b/pkg/specgen/generate/pod_create.go index 20151f016..07c56b799 100644 --- a/pkg/specgen/generate/pod_create.go +++ b/pkg/specgen/generate/pod_create.go @@ -125,7 +125,7 @@ func createPodOptions(p *specgen.PodSpecGenerator, rt *libpod.Runtime) ([]libpod options = append(options, libpod.WithPodUseImageHosts()) } if len(p.PortMappings) > 0 { - ports, _, _, err := parsePortMapping(p.PortMappings) + ports, _, _, err := ParsePortMapping(p.PortMappings) if err != nil { return nil, err } diff --git a/pkg/specgen/generate/ports.go b/pkg/specgen/generate/ports.go index 6832664a7..c00ad19fb 100644 --- a/pkg/specgen/generate/ports.go +++ b/pkg/specgen/generate/ports.go @@ -24,7 +24,7 @@ const ( // Parse port maps to OCICNI port mappings. // Returns a set of OCICNI port mappings, and maps of utilized container and // host ports. -func parsePortMapping(portMappings []specgen.PortMapping) ([]ocicni.PortMapping, map[string]map[string]map[uint16]uint16, map[string]map[string]map[uint16]uint16, error) { +func ParsePortMapping(portMappings []specgen.PortMapping) ([]ocicni.PortMapping, map[string]map[string]map[uint16]uint16, map[string]map[string]map[uint16]uint16, error) { // First, we need to validate the ports passed in the specgen, and then // convert them into CNI port mappings. type tempMapping struct { @@ -254,7 +254,7 @@ func parsePortMapping(portMappings []specgen.PortMapping) ([]ocicni.PortMapping, // Make final port mappings for the container func createPortMappings(ctx context.Context, s *specgen.SpecGenerator, imageData *libimage.ImageData) ([]ocicni.PortMapping, error) { - finalMappings, containerPortValidate, hostPortValidate, err := parsePortMapping(s.PortMappings) + finalMappings, containerPortValidate, hostPortValidate, err := ParsePortMapping(s.PortMappings) if err != nil { return nil, err } @@ -268,31 +268,18 @@ func createPortMappings(ctx context.Context, s *specgen.SpecGenerator, imageData logrus.Debugf("Adding exposed ports") - // We need to merge s.Expose into image exposed ports expose := make(map[uint16]string) - for k, v := range s.Expose { - expose[k] = v - } if imageData != nil { - for imgExpose := range imageData.Config.ExposedPorts { - // Expose format is portNumber[/protocol] - splitExpose := strings.SplitN(imgExpose, "/", 2) - num, err := strconv.Atoi(splitExpose[0]) - if err != nil { - return nil, errors.Wrapf(err, "unable to convert image EXPOSE statement %q to port number", imgExpose) - } - if num > 65535 || num < 1 { - return nil, errors.Errorf("%d from image EXPOSE statement %q is not a valid port number", num, imgExpose) - } - // No need to validate protocol, we'll do it below. - if len(splitExpose) == 1 { - expose[uint16(num)] = "tcp" - } else { - expose[uint16(num)] = splitExpose[1] - } + expose, err = GenExposedPorts(imageData.Config.ExposedPorts) + if err != nil { + return nil, err } } + // We need to merge s.Expose into image exposed ports + for k, v := range s.Expose { + expose[k] = v + } // There's been a request to expose some ports. Let's do that. // Start by figuring out what needs to be exposed. // This is a map of container port number to protocols to expose. @@ -417,3 +404,25 @@ func checkProtocol(protocol string, allowSCTP bool) ([]string, error) { return finalProto, nil } + +func GenExposedPorts(exposedPorts map[string]struct{}) (map[uint16]string, error) { + expose := make(map[uint16]string) + for imgExpose := range exposedPorts { + // Expose format is portNumber[/protocol] + splitExpose := strings.SplitN(imgExpose, "/", 2) + num, err := strconv.Atoi(splitExpose[0]) + if err != nil { + return nil, errors.Wrapf(err, "unable to convert image EXPOSE statement %q to port number", imgExpose) + } + if num > 65535 || num < 1 { + return nil, errors.Errorf("%d from image EXPOSE statement %q is not a valid port number", num, imgExpose) + } + // No need to validate protocol, we'll do it below. + if len(splitExpose) == 1 { + expose[uint16(num)] = "tcp" + } else { + expose[uint16(num)] = splitExpose[1] + } + } + return expose, nil +} diff --git a/pkg/specgen/namespaces.go b/pkg/specgen/namespaces.go index f665fc0be..80852930a 100644 --- a/pkg/specgen/namespaces.go +++ b/pkg/specgen/namespaces.go @@ -253,7 +253,7 @@ func ParseUserNamespace(ns string) (Namespace, error) { // ParseNetworkNamespace parses a network namespace specification in string // form. // Returns a namespace and (optionally) a list of CNI networks to join. -func ParseNetworkNamespace(ns string) (Namespace, []string, error) { +func ParseNetworkNamespace(ns string, rootlessDefaultCNI bool) (Namespace, []string, error) { toReturn := Namespace{} var cniNetworks []string // Net defaults to Slirp on rootless @@ -264,7 +264,11 @@ func ParseNetworkNamespace(ns string) (Namespace, []string, error) { toReturn.NSMode = FromPod case ns == "" || ns == string(Default) || ns == string(Private): if rootless.IsRootless() { - toReturn.NSMode = Slirp + if rootlessDefaultCNI { + toReturn.NSMode = Bridge + } else { + toReturn.NSMode = Slirp + } } else { toReturn.NSMode = Bridge } diff --git a/pkg/systemd/generate/common.go b/pkg/systemd/generate/common.go index 0f667e2f4..e183125a7 100644 --- a/pkg/systemd/generate/common.go +++ b/pkg/systemd/generate/common.go @@ -36,7 +36,7 @@ Description=Podman {{{{.ServiceName}}}}.service Documentation=man:podman-generate-systemd(1) Wants=network.target After=network-online.target -RequiresMountsFor={{{{.GraphRoot}}}} {{{{.RunRoot}}}} +RequiresMountsFor={{{{.RunRoot}}}} ` // filterPodFlags removes --pod, --pod-id-file and --infra-conmon-pidfile from the specified command. @@ -60,7 +60,7 @@ func filterPodFlags(command []string, argCount int) []string { return processed } -// filterCommonContainerFlags removes --conmon-pidfile, --cidfile and --cgroups from the specified command. +// 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{} @@ -68,11 +68,14 @@ func filterCommonContainerFlags(command []string, argCount int) []string { s := command[i] switch { - case s == "--conmon-pidfile", s == "--cidfile", s == "--cgroups": + case s == "--rm": + // Boolean flags support --flag and --flag={true,false}. + continue + case s == "--sdnotify", s == "--cgroups": i++ continue - case strings.HasPrefix(s, "--conmon-pidfile="), - strings.HasPrefix(s, "--cidfile="), + case strings.HasPrefix(s, "--sdnotify="), + strings.HasPrefix(s, "--rm="), strings.HasPrefix(s, "--cgroups="): continue } diff --git a/pkg/systemd/generate/common_test.go b/pkg/systemd/generate/common_test.go index fdcc9d21b..3e2ac015f 100644 --- a/pkg/systemd/generate/common_test.go +++ b/pkg/systemd/generate/common_test.go @@ -93,22 +93,22 @@ func TestFilterCommonContainerFlags(t *testing.T) { }, { []string{"podman", "run", "--conmon-pidfile", "foo", "alpine"}, - []string{"podman", "run", "alpine"}, + []string{"podman", "run", "--conmon-pidfile", "foo", "alpine"}, 1, }, { []string{"podman", "run", "--conmon-pidfile=foo", "alpine"}, - []string{"podman", "run", "alpine"}, + []string{"podman", "run", "--conmon-pidfile=foo", "alpine"}, 1, }, { []string{"podman", "run", "--cidfile", "foo", "alpine"}, - []string{"podman", "run", "alpine"}, + []string{"podman", "run", "--cidfile", "foo", "alpine"}, 1, }, { []string{"podman", "run", "--cidfile=foo", "alpine"}, - []string{"podman", "run", "alpine"}, + []string{"podman", "run", "--cidfile=foo", "alpine"}, 1, }, { @@ -122,25 +122,15 @@ func TestFilterCommonContainerFlags(t *testing.T) { 1, }, { - []string{"podman", "run", "--cgroups", "foo", "--conmon-pidfile", "foo", "--cidfile", "foo", "alpine"}, + []string{"podman", "run", "--cgroups=foo", "--rm", "alpine"}, []string{"podman", "run", "alpine"}, 1, }, { - []string{"podman", "run", "--cgroups=foo", "--conmon-pidfile=foo", "--cidfile=foo", "alpine"}, - []string{"podman", "run", "alpine"}, - 1, - }, - { - []string{"podman", "run", "--cgroups", "foo", "--conmon-pidfile", "foo", "--cidfile", "foo", "alpine", "--cgroups", "foo", "--conmon-pidfile", "foo", "--cidfile", "foo"}, - []string{"podman", "run", "alpine", "--cgroups", "foo", "--conmon-pidfile", "foo", "--cidfile", "foo"}, + []string{"podman", "run", "--cgroups", "--rm=bogus", "alpine", "--cgroups", "foo", "--conmon-pidfile", "foo", "--cidfile", "foo", "--rm"}, + []string{"podman", "run", "alpine", "--cgroups", "foo", "--conmon-pidfile", "foo", "--cidfile", "foo", "--rm"}, 7, }, - { - []string{"podman", "run", "--cgroups=foo", "--conmon-pidfile=foo", "--cidfile=foo", "alpine", "--cgroups=foo", "--conmon-pidfile=foo", "--cidfile=foo"}, - []string{"podman", "run", "alpine", "--cgroups=foo", "--conmon-pidfile=foo", "--cidfile=foo"}, - 4, - }, } for _, test := range tests { diff --git a/pkg/systemd/generate/containers.go b/pkg/systemd/generate/containers.go index eb1fb67ff..0e6e1b4df 100644 --- a/pkg/systemd/generate/containers.go +++ b/pkg/systemd/generate/containers.go @@ -25,6 +25,10 @@ type containerInfo struct { ServiceName string // Name or ID of the container. ContainerNameOrID string + // Type of the unit. + Type string + // NotifyAccess of the unit. + NotifyAccess string // StopTimeout sets the timeout Podman waits before killing the container // during service stop. StopTimeout uint @@ -102,10 +106,19 @@ TimeoutStopSec={{{{.TimeoutStopSec}}}} ExecStartPre={{{{.ExecStartPre}}}} {{{{- end}}}} ExecStart={{{{.ExecStart}}}} +{{{{- if .ExecStop}}}} ExecStop={{{{.ExecStop}}}} +{{{{- end}}}} +{{{{- if .ExecStopPost}}}} ExecStopPost={{{{.ExecStopPost}}}} +{{{{- end}}}} +{{{{- if .PIDFile}}}} PIDFile={{{{.PIDFile}}}} -Type=forking +{{{{- end}}}} +Type={{{{.Type}}}} +{{{{- if .NotifyAccess}}}} +NotifyAccess={{{{.NotifyAccess}}}} +{{{{- end}}}} [Install] WantedBy=multi-user.target default.target @@ -152,14 +165,14 @@ func generateContainerInfo(ctr *libpod.Container, options entities.GenerateSyste return nil, errors.Errorf("could not determine storage store for container") } - graphRoot := store.GraphRoot() - if graphRoot == "" { - return nil, errors.Errorf("could not lookup container's graphroot: got empty string") - } - - runRoot := store.RunRoot() - if runRoot == "" { - return nil, errors.Errorf("could not lookup container's runroot: got empty string") + var runRoot string + if options.New { + runRoot = "%t/containers" + } else { + runRoot = store.RunRoot() + if runRoot == "" { + return nil, errors.Errorf("could not lookup container's runroot: got empty string") + } } envs := config.Spec.Process.Env @@ -172,7 +185,6 @@ func generateContainerInfo(ctr *libpod.Container, options entities.GenerateSyste StopTimeout: timeout, GenerateTimestamp: true, CreateCommand: createCommand, - GraphRoot: graphRoot, RunRoot: runRoot, containerEnv: envs, } @@ -209,6 +221,7 @@ func executeContainerTemplate(info *containerInfo, options entities.GenerateSyst info.Executable = executable } + info.Type = "forking" info.EnvVariable = define.EnvVariable info.ExecStart = "{{{{.Executable}}}} start {{{{.ContainerNameOrID}}}}" info.ExecStop = "{{{{.Executable}}}} stop {{{{if (ge .StopTimeout 0)}}}}-t {{{{.StopTimeout}}}}{{{{end}}}} {{{{.ContainerNameOrID}}}}" @@ -222,8 +235,12 @@ func executeContainerTemplate(info *containerInfo, options entities.GenerateSyst // invalid `info.CreateCommand`. Hence, we're doing a best effort unit // generation and don't try aiming at completeness. if options.New { - info.PIDFile = "%t/" + info.ServiceName + ".pid" - info.ContainerIDFile = "%t/" + info.ServiceName + ".ctr-id" + info.Type = "notify" + info.NotifyAccess = "all" + info.PIDFile = "" + info.ContainerIDFile = "" + info.ExecStop = "" + info.ExecStopPost = "" // The create command must at least have three arguments: // /usr/bin/podman run $IMAGE index := 0 @@ -246,9 +263,9 @@ func executeContainerTemplate(info *containerInfo, options entities.GenerateSyst } startCommand = append(startCommand, "run", - "--conmon-pidfile", "{{{{.PIDFile}}}}", - "--cidfile", "{{{{.ContainerIDFile}}}}", + "--sdnotify=conmon", "--cgroups=no-conmon", + "--rm", ) remainingCmd := info.CreateCommand[index:] @@ -337,11 +354,7 @@ func executeContainerTemplate(info *containerInfo, options entities.GenerateSyst startCommand = append(startCommand, remainingCmd...) startCommand = escapeSystemdArguments(startCommand) - - info.ExecStartPre = "/bin/rm -f {{{{.PIDFile}}}} {{{{.ContainerIDFile}}}}" info.ExecStart = strings.Join(startCommand, " ") - info.ExecStop = "{{{{.Executable}}}} {{{{if .RootFlags}}}}{{{{ .RootFlags}}}} {{{{end}}}}stop --ignore --cidfile {{{{.ContainerIDFile}}}} {{{{if (ge .StopTimeout 0)}}}}-t {{{{.StopTimeout}}}}{{{{end}}}}" - info.ExecStopPost = "{{{{.Executable}}}} {{{{if .RootFlags}}}}{{{{ .RootFlags}}}} {{{{end}}}}rm --ignore -f --cidfile {{{{.ContainerIDFile}}}}" } info.TimeoutStopSec = minTimeoutStopSec + info.StopTimeout diff --git a/pkg/systemd/generate/containers_test.go b/pkg/systemd/generate/containers_test.go index 75b08526b..12a8f3004 100644 --- a/pkg/systemd/generate/containers_test.go +++ b/pkg/systemd/generate/containers_test.go @@ -48,7 +48,7 @@ Description=Podman container-639c53578af4d84b8800b4635fa4e680ee80fd67e0e6a2d4eea Documentation=man:podman-generate-systemd(1) Wants=network.target After=network-online.target -RequiresMountsFor=/var/lib/containers/storage /var/run/containers/storage +RequiresMountsFor=/var/run/containers/storage [Service] Environment=PODMAN_SYSTEMD_UNIT=%n @@ -74,7 +74,7 @@ Description=Podman container-foobar.service Documentation=man:podman-generate-systemd(1) Wants=network.target After=network-online.target -RequiresMountsFor=/var/lib/containers/storage /var/run/containers/storage +RequiresMountsFor=/var/run/containers/storage [Service] Environment=PODMAN_SYSTEMD_UNIT=%n @@ -98,7 +98,7 @@ Description=Podman container-foobar.service Documentation=man:podman-generate-systemd(1) Wants=network.target After=network-online.target -RequiresMountsFor=/var/lib/containers/storage /var/run/containers/storage +RequiresMountsFor=/var/run/containers/storage BindsTo=a.service b.service c.service pod.service After=a.service b.service c.service pod.service @@ -124,18 +124,15 @@ Description=Podman jadda-jadda.service Documentation=man:podman-generate-systemd(1) Wants=network.target After=network-online.target -RequiresMountsFor=/var/lib/containers/storage /var/run/containers/storage +RequiresMountsFor=/var/run/containers/storage [Service] Environment=PODMAN_SYSTEMD_UNIT=%n Restart=always TimeoutStopSec=70 -ExecStartPre=/bin/rm -f %t/jadda-jadda.pid %t/jadda-jadda.ctr-id -ExecStart=/usr/bin/podman container run --conmon-pidfile %t/jadda-jadda.pid --cidfile %t/jadda-jadda.ctr-id --cgroups=no-conmon -d --replace --name jadda-jadda --hostname hello-world awesome-image:latest command arg1 ... argN "foo=arg \"with \" space" -ExecStop=/usr/bin/podman container stop --ignore --cidfile %t/jadda-jadda.ctr-id -t 10 -ExecStopPost=/usr/bin/podman container rm --ignore -f --cidfile %t/jadda-jadda.ctr-id -PIDFile=%t/jadda-jadda.pid -Type=forking +ExecStart=/usr/bin/podman container run --sdnotify=conmon --cgroups=no-conmon --rm -d --replace --name jadda-jadda --hostname hello-world awesome-image:latest command arg1 ... argN "foo=arg \"with \" space" +Type=notify +NotifyAccess=all [Install] WantedBy=multi-user.target default.target @@ -149,18 +146,15 @@ Description=Podman jadda-jadda.service Documentation=man:podman-generate-systemd(1) Wants=network.target After=network-online.target -RequiresMountsFor=/var/lib/containers/storage /var/run/containers/storage +RequiresMountsFor=/var/run/containers/storage [Service] Environment=PODMAN_SYSTEMD_UNIT=%n Restart=always TimeoutStopSec=70 -ExecStartPre=/bin/rm -f %t/jadda-jadda.pid %t/jadda-jadda.ctr-id -ExecStart=/usr/bin/podman run --conmon-pidfile %t/jadda-jadda.pid --cidfile %t/jadda-jadda.ctr-id --cgroups=no-conmon --replace -d --name jadda-jadda --hostname hello-world awesome-image:latest command arg1 ... argN -ExecStop=/usr/bin/podman stop --ignore --cidfile %t/jadda-jadda.ctr-id -t 10 -ExecStopPost=/usr/bin/podman rm --ignore -f --cidfile %t/jadda-jadda.ctr-id -PIDFile=%t/jadda-jadda.pid -Type=forking +ExecStart=/usr/bin/podman run --sdnotify=conmon --cgroups=no-conmon --rm --replace -d --name jadda-jadda --hostname hello-world awesome-image:latest command arg1 ... argN +Type=notify +NotifyAccess=all [Install] WantedBy=multi-user.target default.target @@ -174,18 +168,15 @@ Description=Podman jadda-jadda.service Documentation=man:podman-generate-systemd(1) Wants=network.target After=network-online.target -RequiresMountsFor=/var/lib/containers/storage /var/run/containers/storage +RequiresMountsFor=/var/run/containers/storage [Service] Environment=PODMAN_SYSTEMD_UNIT=%n Restart=always TimeoutStopSec=70 -ExecStartPre=/bin/rm -f %t/jadda-jadda.pid %t/jadda-jadda.ctr-id -ExecStart=/usr/bin/podman run --conmon-pidfile %t/jadda-jadda.pid --cidfile %t/jadda-jadda.ctr-id --cgroups=no-conmon --pod-id-file %t/pod-foobar.pod-id-file --replace -d --name jadda-jadda --hostname hello-world awesome-image:latest command arg1 ... argN -ExecStop=/usr/bin/podman stop --ignore --cidfile %t/jadda-jadda.ctr-id -t 10 -ExecStopPost=/usr/bin/podman rm --ignore -f --cidfile %t/jadda-jadda.ctr-id -PIDFile=%t/jadda-jadda.pid -Type=forking +ExecStart=/usr/bin/podman run --sdnotify=conmon --cgroups=no-conmon --rm --pod-id-file %t/pod-foobar.pod-id-file --replace -d --name jadda-jadda --hostname hello-world awesome-image:latest command arg1 ... argN +Type=notify +NotifyAccess=all [Install] WantedBy=multi-user.target default.target @@ -199,18 +190,15 @@ Description=Podman jadda-jadda.service Documentation=man:podman-generate-systemd(1) Wants=network.target After=network-online.target -RequiresMountsFor=/var/lib/containers/storage /var/run/containers/storage +RequiresMountsFor=/var/run/containers/storage [Service] Environment=PODMAN_SYSTEMD_UNIT=%n Restart=always TimeoutStopSec=70 -ExecStartPre=/bin/rm -f %t/jadda-jadda.pid %t/jadda-jadda.ctr-id -ExecStart=/usr/bin/podman run --conmon-pidfile %t/jadda-jadda.pid --cidfile %t/jadda-jadda.ctr-id --cgroups=no-conmon --replace --detach --name jadda-jadda --hostname hello-world awesome-image:latest command arg1 ... argN -ExecStop=/usr/bin/podman stop --ignore --cidfile %t/jadda-jadda.ctr-id -t 10 -ExecStopPost=/usr/bin/podman rm --ignore -f --cidfile %t/jadda-jadda.ctr-id -PIDFile=%t/jadda-jadda.pid -Type=forking +ExecStart=/usr/bin/podman run --sdnotify=conmon --cgroups=no-conmon --rm --replace --detach --name jadda-jadda --hostname hello-world awesome-image:latest command arg1 ... argN +Type=notify +NotifyAccess=all [Install] WantedBy=multi-user.target default.target @@ -224,18 +212,15 @@ Description=Podman container-639c53578af4d84b8800b4635fa4e680ee80fd67e0e6a2d4eea Documentation=man:podman-generate-systemd(1) Wants=network.target After=network-online.target -RequiresMountsFor=/var/lib/containers/storage /var/run/containers/storage +RequiresMountsFor=/var/run/containers/storage [Service] Environment=PODMAN_SYSTEMD_UNIT=%n Restart=always TimeoutStopSec=70 -ExecStartPre=/bin/rm -f %t/container-639c53578af4d84b8800b4635fa4e680ee80fd67e0e6a2d4eea48d1e3230f401.pid %t/container-639c53578af4d84b8800b4635fa4e680ee80fd67e0e6a2d4eea48d1e3230f401.ctr-id -ExecStart=/usr/bin/podman run --conmon-pidfile %t/container-639c53578af4d84b8800b4635fa4e680ee80fd67e0e6a2d4eea48d1e3230f401.pid --cidfile %t/container-639c53578af4d84b8800b4635fa4e680ee80fd67e0e6a2d4eea48d1e3230f401.ctr-id --cgroups=no-conmon -d awesome-image:latest -ExecStop=/usr/bin/podman stop --ignore --cidfile %t/container-639c53578af4d84b8800b4635fa4e680ee80fd67e0e6a2d4eea48d1e3230f401.ctr-id -t 10 -ExecStopPost=/usr/bin/podman rm --ignore -f --cidfile %t/container-639c53578af4d84b8800b4635fa4e680ee80fd67e0e6a2d4eea48d1e3230f401.ctr-id -PIDFile=%t/container-639c53578af4d84b8800b4635fa4e680ee80fd67e0e6a2d4eea48d1e3230f401.pid -Type=forking +ExecStart=/usr/bin/podman run --sdnotify=conmon --cgroups=no-conmon --rm -d awesome-image:latest +Type=notify +NotifyAccess=all [Install] WantedBy=multi-user.target default.target @@ -250,20 +235,17 @@ Description=Podman jadda-jadda.service Documentation=man:podman-generate-systemd(1) Wants=network.target After=network-online.target -RequiresMountsFor=/var/lib/containers/storage /var/run/containers/storage +RequiresMountsFor=/var/run/containers/storage [Service] Environment=PODMAN_SYSTEMD_UNIT=%n Restart=always TimeoutStopSec=102 -ExecStartPre=/bin/rm -f %t/jadda-jadda.pid %t/jadda-jadda.ctr-id -ExecStart=/usr/bin/podman run --conmon-pidfile %t/jadda-jadda.pid --cidfile %t/jadda-jadda.ctr-id --cgroups=no-conmon ` + +ExecStart=/usr/bin/podman run --sdnotify=conmon --cgroups=no-conmon --rm ` + detachparam + ` awesome-image:latest -ExecStop=/usr/bin/podman stop --ignore --cidfile %t/jadda-jadda.ctr-id -t 42 -ExecStopPost=/usr/bin/podman rm --ignore -f --cidfile %t/jadda-jadda.ctr-id -PIDFile=%t/jadda-jadda.pid -Type=forking +Type=notify +NotifyAccess=all [Install] WantedBy=multi-user.target default.target @@ -279,18 +261,15 @@ Description=Podman jadda-jadda.service Documentation=man:podman-generate-systemd(1) Wants=network.target After=network-online.target -RequiresMountsFor=/var/lib/containers/storage /var/run/containers/storage +RequiresMountsFor=/var/run/containers/storage [Service] Environment=PODMAN_SYSTEMD_UNIT=%n Restart=always TimeoutStopSec=102 -ExecStartPre=/bin/rm -f %t/jadda-jadda.pid %t/jadda-jadda.ctr-id -ExecStart=/usr/bin/podman run --conmon-pidfile %t/jadda-jadda.pid --cidfile %t/jadda-jadda.ctr-id --cgroups=no-conmon -d --replace --name test -p 80:80 awesome-image:latest somecmd --detach=false -ExecStop=/usr/bin/podman stop --ignore --cidfile %t/jadda-jadda.ctr-id -t 42 -ExecStopPost=/usr/bin/podman rm --ignore -f --cidfile %t/jadda-jadda.ctr-id -PIDFile=%t/jadda-jadda.pid -Type=forking +ExecStart=/usr/bin/podman run --sdnotify=conmon --cgroups=no-conmon --rm -d --replace --name test -p 80:80 awesome-image:latest somecmd --detach=false +Type=notify +NotifyAccess=all [Install] WantedBy=multi-user.target default.target @@ -304,18 +283,15 @@ Description=Podman jadda-jadda.service Documentation=man:podman-generate-systemd(1) Wants=network.target After=network-online.target -RequiresMountsFor=/var/lib/containers/storage /var/run/containers/storage +RequiresMountsFor=/var/run/containers/storage [Service] Environment=PODMAN_SYSTEMD_UNIT=%n Restart=always TimeoutStopSec=102 -ExecStartPre=/bin/rm -f %t/jadda-jadda.pid %t/jadda-jadda.ctr-id -ExecStart=/usr/bin/podman --events-backend none --runroot /root run --conmon-pidfile %t/jadda-jadda.pid --cidfile %t/jadda-jadda.ctr-id --cgroups=no-conmon -d awesome-image:latest -ExecStop=/usr/bin/podman --events-backend none --runroot /root stop --ignore --cidfile %t/jadda-jadda.ctr-id -t 42 -ExecStopPost=/usr/bin/podman --events-backend none --runroot /root rm --ignore -f --cidfile %t/jadda-jadda.ctr-id -PIDFile=%t/jadda-jadda.pid -Type=forking +ExecStart=/usr/bin/podman --events-backend none --runroot /root run --sdnotify=conmon --cgroups=no-conmon --rm -d awesome-image:latest +Type=notify +NotifyAccess=all [Install] WantedBy=multi-user.target default.target @@ -329,18 +305,15 @@ Description=Podman jadda-jadda.service Documentation=man:podman-generate-systemd(1) Wants=network.target After=network-online.target -RequiresMountsFor=/var/lib/containers/storage /var/run/containers/storage +RequiresMountsFor=/var/run/containers/storage [Service] Environment=PODMAN_SYSTEMD_UNIT=%n Restart=always TimeoutStopSec=70 -ExecStartPre=/bin/rm -f %t/jadda-jadda.pid %t/jadda-jadda.ctr-id -ExecStart=/usr/bin/podman container run --conmon-pidfile %t/jadda-jadda.pid --cidfile %t/jadda-jadda.ctr-id --cgroups=no-conmon -d awesome-image:latest -ExecStop=/usr/bin/podman container stop --ignore --cidfile %t/jadda-jadda.ctr-id -t 10 -ExecStopPost=/usr/bin/podman container rm --ignore -f --cidfile %t/jadda-jadda.ctr-id -PIDFile=%t/jadda-jadda.pid -Type=forking +ExecStart=/usr/bin/podman container run --sdnotify=conmon --cgroups=no-conmon --rm -d awesome-image:latest +Type=notify +NotifyAccess=all [Install] WantedBy=multi-user.target default.target @@ -354,18 +327,15 @@ Description=Podman jadda-jadda.service Documentation=man:podman-generate-systemd(1) Wants=network.target After=network-online.target -RequiresMountsFor=/var/lib/containers/storage /var/run/containers/storage +RequiresMountsFor=/var/run/containers/storage [Service] Environment=PODMAN_SYSTEMD_UNIT=%n Restart=always TimeoutStopSec=70 -ExecStartPre=/bin/rm -f %t/jadda-jadda.pid %t/jadda-jadda.ctr-id -ExecStart=/usr/bin/podman run --conmon-pidfile %t/jadda-jadda.pid --cidfile %t/jadda-jadda.ctr-id --cgroups=no-conmon -d --replace --name test --log-driver=journald --log-opt=tag={{.Name}} awesome-image:latest -ExecStop=/usr/bin/podman stop --ignore --cidfile %t/jadda-jadda.ctr-id -t 10 -ExecStopPost=/usr/bin/podman rm --ignore -f --cidfile %t/jadda-jadda.ctr-id -PIDFile=%t/jadda-jadda.pid -Type=forking +ExecStart=/usr/bin/podman run --sdnotify=conmon --cgroups=no-conmon --rm -d --replace --name test --log-driver=journald --log-opt=tag={{.Name}} awesome-image:latest +Type=notify +NotifyAccess=all [Install] WantedBy=multi-user.target default.target @@ -379,18 +349,15 @@ Description=Podman jadda-jadda.service Documentation=man:podman-generate-systemd(1) Wants=network.target After=network-online.target -RequiresMountsFor=/var/lib/containers/storage /var/run/containers/storage +RequiresMountsFor=/var/run/containers/storage [Service] Environment=PODMAN_SYSTEMD_UNIT=%n Restart=always TimeoutStopSec=70 -ExecStartPre=/bin/rm -f %t/jadda-jadda.pid %t/jadda-jadda.ctr-id -ExecStart=/usr/bin/podman run --conmon-pidfile %t/jadda-jadda.pid --cidfile %t/jadda-jadda.ctr-id --cgroups=no-conmon -d --replace --name test awesome-image:latest sh -c "kill $$$$ && echo %%\\" -ExecStop=/usr/bin/podman stop --ignore --cidfile %t/jadda-jadda.ctr-id -t 10 -ExecStopPost=/usr/bin/podman rm --ignore -f --cidfile %t/jadda-jadda.ctr-id -PIDFile=%t/jadda-jadda.pid -Type=forking +ExecStart=/usr/bin/podman run --sdnotify=conmon --cgroups=no-conmon --rm -d --replace --name test awesome-image:latest sh -c "kill $$$$ && echo %%\\" +Type=notify +NotifyAccess=all [Install] WantedBy=multi-user.target default.target @@ -404,18 +371,15 @@ Description=Podman jadda-jadda.service Documentation=man:podman-generate-systemd(1) Wants=network.target After=network-online.target -RequiresMountsFor=/var/lib/containers/storage /var/run/containers/storage +RequiresMountsFor=/var/run/containers/storage [Service] Environment=PODMAN_SYSTEMD_UNIT=%n Restart=always TimeoutStopSec=70 -ExecStartPre=/bin/rm -f %t/jadda-jadda.pid %t/jadda-jadda.ctr-id -ExecStart=/usr/bin/podman run --conmon-pidfile %t/jadda-jadda.pid --cidfile %t/jadda-jadda.ctr-id --cgroups=no-conmon -d awesome-image:latest podman run --cgroups=foo --conmon-pidfile=foo --cidfile=foo alpine -ExecStop=/usr/bin/podman stop --ignore --cidfile %t/jadda-jadda.ctr-id -t 10 -ExecStopPost=/usr/bin/podman rm --ignore -f --cidfile %t/jadda-jadda.ctr-id -PIDFile=%t/jadda-jadda.pid -Type=forking +ExecStart=/usr/bin/podman run --sdnotify=conmon --cgroups=no-conmon --rm -d --conmon-pidfile=foo --cidfile=foo awesome-image:latest podman run --cgroups=foo --conmon-pidfile=foo --cidfile=foo alpine +Type=notify +NotifyAccess=all [Install] WantedBy=multi-user.target default.target @@ -429,18 +393,15 @@ Description=Podman jadda-jadda.service Documentation=man:podman-generate-systemd(1) Wants=network.target After=network-online.target -RequiresMountsFor=/var/lib/containers/storage /var/run/containers/storage +RequiresMountsFor=/var/run/containers/storage [Service] Environment=PODMAN_SYSTEMD_UNIT=%n Restart=always TimeoutStopSec=70 -ExecStartPre=/bin/rm -f %t/jadda-jadda.pid %t/jadda-jadda.ctr-id -ExecStart=/usr/bin/podman run --conmon-pidfile %t/jadda-jadda.pid --cidfile %t/jadda-jadda.ctr-id --cgroups=no-conmon --pod-id-file %t/pod-foobar.pod-id-file -d awesome-image:latest podman run --cgroups=foo --conmon-pidfile=foo --cidfile=foo --pod-id-file /tmp/pod-foobar.pod-id-file alpine -ExecStop=/usr/bin/podman stop --ignore --cidfile %t/jadda-jadda.ctr-id -t 10 -ExecStopPost=/usr/bin/podman rm --ignore -f --cidfile %t/jadda-jadda.ctr-id -PIDFile=%t/jadda-jadda.pid -Type=forking +ExecStart=/usr/bin/podman run --sdnotify=conmon --cgroups=no-conmon --rm --pod-id-file %t/pod-foobar.pod-id-file -d --conmon-pidfile=foo --cidfile=foo awesome-image:latest podman run --cgroups=foo --conmon-pidfile=foo --cidfile=foo --pod-id-file /tmp/pod-foobar.pod-id-file alpine +Type=notify +NotifyAccess=all [Install] WantedBy=multi-user.target default.target @@ -454,19 +415,16 @@ Description=Podman jadda-jadda.service Documentation=man:podman-generate-systemd(1) Wants=network.target After=network-online.target -RequiresMountsFor=/var/lib/containers/storage /var/run/containers/storage +RequiresMountsFor=/var/run/containers/storage [Service] Environment=PODMAN_SYSTEMD_UNIT=%n Environment=FOO=abc "BAR=my test" USER=%%a Restart=always TimeoutStopSec=70 -ExecStartPre=/bin/rm -f %t/jadda-jadda.pid %t/jadda-jadda.ctr-id -ExecStart=/usr/bin/podman run --conmon-pidfile %t/jadda-jadda.pid --cidfile %t/jadda-jadda.ctr-id --cgroups=no-conmon -d --env FOO --env=BAR --env=MYENV=2 -e USER awesome-image:latest -ExecStop=/usr/bin/podman stop --ignore --cidfile %t/jadda-jadda.ctr-id -t 10 -ExecStopPost=/usr/bin/podman rm --ignore -f --cidfile %t/jadda-jadda.ctr-id -PIDFile=%t/jadda-jadda.pid -Type=forking +ExecStart=/usr/bin/podman run --sdnotify=conmon --cgroups=no-conmon --rm -d --env FOO --env=BAR --env=MYENV=2 -e USER awesome-image:latest +Type=notify +NotifyAccess=all [Install] WantedBy=multi-user.target default.target @@ -929,10 +887,10 @@ WantedBy=multi-user.target default.target } got, err := executeContainerTemplate(&test.info, opts) if (err != nil) != test.wantErr { - t.Errorf("CreateContainerSystemdUnit() error = \n%v, wantErr \n%v", err, test.wantErr) + t.Errorf("CreateContainerSystemdUnit() %s error = \n%v, wantErr \n%v", test.name, err, test.wantErr) return } - assert.Equal(t, test.want, got) + assert.Equal(t, test.want, got, test.name) }) } } diff --git a/pkg/systemd/generate/pods_test.go b/pkg/systemd/generate/pods_test.go index 0e4d92c50..a11e1e11e 100644 --- a/pkg/systemd/generate/pods_test.go +++ b/pkg/systemd/generate/pods_test.go @@ -47,7 +47,7 @@ Description=Podman pod-123abc.service Documentation=man:podman-generate-systemd(1) Wants=network.target After=network-online.target -RequiresMountsFor=/var/lib/containers/storage /var/run/containers/storage +RequiresMountsFor=/var/run/containers/storage Requires=container-1.service container-2.service Before=container-1.service container-2.service @@ -75,7 +75,7 @@ Description=Podman pod-123abc.service Documentation=man:podman-generate-systemd(1) Wants=network.target After=network-online.target -RequiresMountsFor=/var/lib/containers/storage /var/run/containers/storage +RequiresMountsFor=/var/run/containers/storage Requires=container-1.service container-2.service Before=container-1.service container-2.service @@ -103,7 +103,7 @@ Description=Podman pod-123abc.service Documentation=man:podman-generate-systemd(1) Wants=network.target After=network-online.target -RequiresMountsFor=/var/lib/containers/storage /var/run/containers/storage +RequiresMountsFor=/var/run/containers/storage Requires=container-1.service container-2.service Before=container-1.service container-2.service @@ -131,7 +131,7 @@ Description=Podman pod-123abc.service Documentation=man:podman-generate-systemd(1) Wants=network.target After=network-online.target -RequiresMountsFor=/var/lib/containers/storage /var/run/containers/storage +RequiresMountsFor=/var/run/containers/storage Requires=container-1.service container-2.service Before=container-1.service container-2.service @@ -159,7 +159,7 @@ Description=Podman pod-123abc.service Documentation=man:podman-generate-systemd(1) Wants=network.target After=network-online.target -RequiresMountsFor=/var/lib/containers/storage /var/run/containers/storage +RequiresMountsFor=/var/run/containers/storage Requires=container-1.service container-2.service Before=container-1.service container-2.service |