diff options
Diffstat (limited to 'pkg')
58 files changed, 498 insertions, 213 deletions
diff --git a/pkg/api/handlers/compat/containers.go b/pkg/api/handlers/compat/containers.go index a15fdb553..18005e24a 100644 --- a/pkg/api/handlers/compat/containers.go +++ b/pkg/api/handlers/compat/containers.go @@ -410,11 +410,11 @@ func LibpodToContainerJSON(l *libpod.Container, sz bool) (*types.ContainerJSON, if l.HasHealthCheck() && state.Status != "created" { state.Health = &types.Health{ - Status: inspect.State.Healthcheck.Status, - FailingStreak: inspect.State.Healthcheck.FailingStreak, + Status: inspect.State.Health.Status, + FailingStreak: inspect.State.Health.FailingStreak, } - log := inspect.State.Healthcheck.Log + log := inspect.State.Health.Log for _, item := range log { res := &types.HealthcheckResult{} diff --git a/pkg/api/handlers/compat/containers_start.go b/pkg/api/handlers/compat/containers_start.go index ca2b5d84c..fb68389bc 100644 --- a/pkg/api/handlers/compat/containers_start.go +++ b/pkg/api/handlers/compat/containers_start.go @@ -25,7 +25,7 @@ func StartContainer(w http.ResponseWriter, r *http.Request) { } if len(query.DetachKeys) > 0 { // TODO - start does not support adding detach keys - logrus.Info("the detach keys parameter is not supported on start container") + logrus.Info("The detach keys parameter is not supported on start container") } runtime := r.Context().Value(api.RuntimeKey).(*libpod.Runtime) name := utils.GetName(r) diff --git a/pkg/api/handlers/compat/containers_stats.go b/pkg/api/handlers/compat/containers_stats.go index e872f885a..0051e7235 100644 --- a/pkg/api/handlers/compat/containers_stats.go +++ b/pkg/api/handlers/compat/containers_stats.go @@ -227,10 +227,10 @@ func toBlkioStatEntry(entries []cgroups.BlkIOEntry) []docker.BlkioStatEntry { for i, e := range entries { bits, err := json.Marshal(e) if err != nil { - logrus.Errorf("unable to marshal blkio stats: %q", err) + logrus.Errorf("Unable to marshal blkio stats: %q", err) } if err := json.Unmarshal(bits, &results[i]); err != nil { - logrus.Errorf("unable to unmarshal blkio stats: %q", err) + logrus.Errorf("Unable to unmarshal blkio stats: %q", err) } } return results diff --git a/pkg/api/handlers/compat/events.go b/pkg/api/handlers/compat/events.go index a79b33ecc..901acdac4 100644 --- a/pkg/api/handlers/compat/events.go +++ b/pkg/api/handlers/compat/events.go @@ -94,7 +94,7 @@ func GetEvents(w http.ResponseWriter, r *http.Request) { } if err := coder.Encode(e); err != nil { - logrus.Errorf("unable to write json: %q", err) + logrus.Errorf("Unable to write json: %q", err) } flush() case <-r.Context().Done(): diff --git a/pkg/api/handlers/libpod/images.go b/pkg/api/handlers/libpod/images.go index b4f08a746..1c6cc917c 100644 --- a/pkg/api/handlers/libpod/images.go +++ b/pkg/api/handlers/libpod/images.go @@ -150,7 +150,8 @@ func PruneImages(w http.ResponseWriter, r *http.Request) { runtime := r.Context().Value(api.RuntimeKey).(*libpod.Runtime) decoder := r.Context().Value(api.DecoderKey).(*schema.Decoder) query := struct { - All bool `schema:"all"` + All bool `schema:"all"` + External bool `schema:"external"` }{ // override any golang type defaults } @@ -190,8 +191,9 @@ func PruneImages(w http.ResponseWriter, r *http.Request) { imageEngine := abi.ImageEngine{Libpod: runtime} pruneOptions := entities.ImagePruneOptions{ - All: query.All, - Filter: libpodFilters, + All: query.All, + External: query.External, + Filter: libpodFilters, } imagePruneReports, err := imageEngine.Prune(r.Context(), pruneOptions) if err != nil { @@ -289,9 +291,10 @@ func ExportImages(w http.ResponseWriter, r *http.Request) { runtime := r.Context().Value(api.RuntimeKey).(*libpod.Runtime) decoder := r.Context().Value(api.DecoderKey).(*schema.Decoder) query := struct { - Compress bool `schema:"compress"` - Format string `schema:"format"` - References []string `schema:"references"` + Compress bool `schema:"compress"` + Format string `schema:"format"` + OciAcceptUncompressedLayers bool `schema:"ociAcceptUncompressedLayers"` + References []string `schema:"references"` }{ Format: define.OCIArchive, } @@ -353,11 +356,11 @@ func ExportImages(w http.ResponseWriter, r *http.Request) { // Use the ABI image engine to share as much code as possible. opts := entities.ImageSaveOptions{ - Compress: query.Compress, - Format: query.Format, - MultiImageArchive: len(query.References) > 1, - Output: output, - RemoveSignatures: true, + Compress: query.Compress, + Format: query.Format, + MultiImageArchive: len(query.References) > 1, + OciAcceptUncompressedLayers: query.OciAcceptUncompressedLayers, + Output: output, } imageEngine := abi.ImageEngine{Libpod: runtime} diff --git a/pkg/api/handlers/libpod/pods.go b/pkg/api/handlers/libpod/pods.go index 9f46ecc52..7bd6d3dbf 100644 --- a/pkg/api/handlers/libpod/pods.go +++ b/pkg/api/handlers/libpod/pods.go @@ -186,9 +186,9 @@ func PodStop(w http.ResponseWriter, r *http.Request) { } // Try to clean up the pod - but only warn on failure, it's nonfatal. if cleanupCtrs, cleanupErr := pod.Cleanup(r.Context()); cleanupErr != nil { - logrus.Errorf("Error cleaning up pod %s: %v", pod.ID(), cleanupErr) + logrus.Errorf("Cleaning up pod %s: %v", pod.ID(), cleanupErr) for id, err := range cleanupCtrs { - logrus.Errorf("Error cleaning up pod %s container %s: %v", pod.ID(), id, err) + logrus.Errorf("Cleaning up pod %s container %s: %v", pod.ID(), id, err) } } diff --git a/pkg/api/handlers/utils/containers.go b/pkg/api/handlers/utils/containers.go index 5cdb31de1..6f875fc30 100644 --- a/pkg/api/handlers/utils/containers.go +++ b/pkg/api/handlers/utils/containers.go @@ -78,7 +78,7 @@ func WaitContainerDocker(w http.ResponseWriter, r *http.Request) { exitCode, err := waitDockerCondition(ctx, name, interval, condition) var errStruct *struct{ Message string } if err != nil { - logrus.Errorf("error while waiting on condition: %q", err) + logrus.Errorf("While waiting on condition: %q", err) errStruct = &struct { Message string }{ @@ -94,7 +94,7 @@ func WaitContainerDocker(w http.ResponseWriter, r *http.Request) { enc.SetEscapeHTML(true) err = enc.Encode(&responseData) if err != nil { - logrus.Errorf("unable to write json: %q", err) + logrus.Errorf("Unable to write json: %q", err) } } diff --git a/pkg/api/handlers/utils/handler.go b/pkg/api/handlers/utils/handler.go index 7625f9546..29139a98e 100644 --- a/pkg/api/handlers/utils/handler.go +++ b/pkg/api/handlers/utils/handler.go @@ -89,21 +89,21 @@ func WriteResponse(w http.ResponseWriter, code int, value interface{}) { w.WriteHeader(code) if _, err := fmt.Fprintln(w, v); err != nil { - logrus.Errorf("unable to send string response: %q", err) + logrus.Errorf("Unable to send string response: %q", err) } case *os.File: w.Header().Set("Content-Type", "application/octet; charset=us-ascii") w.WriteHeader(code) if _, err := io.Copy(w, v); err != nil { - logrus.Errorf("unable to copy to response: %q", err) + logrus.Errorf("Unable to copy to response: %q", err) } case io.Reader: w.Header().Set("Content-Type", "application/x-tar") w.WriteHeader(code) if _, err := io.Copy(w, v); err != nil { - logrus.Errorf("unable to copy to response: %q", err) + logrus.Errorf("Unable to copy to response: %q", err) } default: WriteJSON(w, code, value) @@ -162,7 +162,7 @@ func WriteJSON(w http.ResponseWriter, code int, value interface{}) { coder := json.NewEncoder(w) coder.SetEscapeHTML(true) if err := coder.Encode(value); err != nil { - logrus.Errorf("unable to write json: %q", err) + logrus.Errorf("Unable to write json: %q", err) } } diff --git a/pkg/api/server/register_images.go b/pkg/api/server/register_images.go index 95981226c..aa573eaa6 100644 --- a/pkg/api/server/register_images.go +++ b/pkg/api/server/register_images.go @@ -176,6 +176,7 @@ func (s *APIServer) registerImagesHandlers(r *mux.Router) error { // - in: query // name: limit // type: integer + // default: 25 // description: maximum number of results // - in: query // name: filters @@ -186,6 +187,11 @@ func (s *APIServer) registerImagesHandlers(r *mux.Router) error { // - `is-official=(true|false)` // - `stars=<number>` Matches images that has at least 'number' stars. // - in: query + // name: tlsVerify + // type: boolean + // default: false + // description: skip TLS verification for registries + // - in: query // name: listTags // type: boolean // description: list the available tags in the repository @@ -1044,6 +1050,12 @@ func (s *APIServer) registerImagesHandlers(r *mux.Router) error { // description: | // Remove all images not in use by containers, not just dangling ones // - in: query + // name: external + // default: false + // type: boolean + // description: | + // Remove images even when they are used by external containers (e.g, by build containers) + // - in: query // name: filters // type: string // description: | @@ -1075,6 +1087,7 @@ func (s *APIServer) registerImagesHandlers(r *mux.Router) error { // - in: query // name: limit // type: integer + // default: 25 // description: maximum number of results // - in: query // name: noTrunc @@ -1088,6 +1101,16 @@ func (s *APIServer) registerImagesHandlers(r *mux.Router) error { // - `is-automated=(true|false)` // - `is-official=(true|false)` // - `stars=<number>` Matches images that has at least 'number' stars. + // - in: query + // name: tlsVerify + // type: boolean + // default: false + // description: skip TLS verification for registries + // - in: query + // name: listTags + // type: boolean + // default: false + // description: list the available tags in the repository // produces: // - application/json // responses: @@ -1150,6 +1173,10 @@ func (s *APIServer) registerImagesHandlers(r *mux.Router) error { // name: compress // type: boolean // description: use compression on image + // - in: query + // name: ociAcceptUncompressedLayers + // type: boolean + // description: accept uncompressed layers when copying OCI images // produces: // - application/json // responses: diff --git a/pkg/api/server/server.go b/pkg/api/server/server.go index 34d0fa246..c7174775e 100644 --- a/pkg/api/server/server.go +++ b/pkg/api/server/server.go @@ -233,7 +233,7 @@ func (s *APIServer) Serve() error { s.pprof = &http.Server{Addr: "localhost:8888", Handler: pprofMux} err := s.pprof.ListenAndServe() if err != nil && err != http.ErrServerClosed { - logrus.Warn("API profiler service failed: " + err.Error()) + logrus.Warnf("API profiler service failed: %v", err) } }() } @@ -272,7 +272,7 @@ func (s *APIServer) Shutdown() error { go func() { defer cancel() if err := s.pprof.Shutdown(ctx); err != nil { - logrus.Warn("Failed to cleanly shutdown API pprof service: " + err.Error()) + logrus.Warnf("Failed to cleanly shutdown API pprof service: %v", err) } }() <-ctx.Done() diff --git a/pkg/auth/auth.go b/pkg/auth/auth.go index 6aff880f4..070e222ad 100644 --- a/pkg/auth/auth.go +++ b/pkg/auth/auth.go @@ -208,7 +208,7 @@ func RemoveAuthfile(authfile string) { return } if err := os.Remove(authfile); err != nil { - logrus.Errorf("Error removing temporary auth file %q: %v", authfile, err) + logrus.Errorf("Removing temporary auth file %q: %v", authfile, err) } } diff --git a/pkg/bindings/connection.go b/pkg/bindings/connection.go index 4127ad2f0..e2c46e481 100644 --- a/pkg/bindings/connection.go +++ b/pkg/bindings/connection.go @@ -112,12 +112,12 @@ func NewConnectionWithIdentity(ctx context.Context, uri string, identity string) return nil, errors.Errorf("unable to create connection. %q is not a supported schema", _url.Scheme) } if err != nil { - return nil, errors.Wrapf(err, "failed to create %sClient", _url.Scheme) + return nil, errors.Wrapf(err, "unable to connect to Podman. failed to create %sClient", _url.Scheme) } ctx = context.WithValue(ctx, clientKey, &connection) if err := pingNewConnection(ctx); err != nil { - return nil, errors.Wrap(err, "cannot connect to the Podman socket, please verify the connection to the Linux system, or use `podman machine` to create/start a Linux VM.") + return nil, errors.Wrap(err, "unable to connect to Podman socket") } return ctx, nil } diff --git a/pkg/bindings/containers/attach.go b/pkg/bindings/containers/attach.go index 6efbcb57b..abf58aaf9 100644 --- a/pkg/bindings/containers/attach.go +++ b/pkg/bindings/containers/attach.go @@ -102,7 +102,7 @@ func Attach(ctx context.Context, nameOrID string, stdin io.Reader, stdout io.Wri } defer func() { if err := terminal.Restore(int(file.Fd()), state); err != nil { - logrus.Errorf("unable to restore terminal: %q", err) + logrus.Errorf("Unable to restore terminal: %q", err) } logrus.SetFormatter(&logrus.TextFormatter{}) }() @@ -166,7 +166,7 @@ func Attach(ctx context.Context, nameOrID string, stdin io.Reader, stdout io.Wri _, err := utils.CopyDetachable(socket, stdin, detachKeysInBytes) if err != nil && err != define.ErrDetach { - logrus.Error("failed to write input to service: " + err.Error()) + logrus.Errorf("Failed to write input to service: %v", err) } stdinChan <- err @@ -349,7 +349,7 @@ func attachHandleResize(ctx, winCtx context.Context, winChange chan os.Signal, i resize := func() { w, h, err := terminal.GetSize(int(file.Fd())) if err != nil { - logrus.Warnf("failed to obtain TTY size: %v", err) + logrus.Warnf("Failed to obtain TTY size: %v", err) } var resizeErr error @@ -359,7 +359,7 @@ func attachHandleResize(ctx, winCtx context.Context, winChange chan os.Signal, i resizeErr = ResizeContainerTTY(ctx, id, new(ResizeTTYOptions).WithHeight(h).WithWidth(w)) } if resizeErr != nil { - logrus.Infof("failed to resize TTY: %v", resizeErr) + logrus.Infof("Failed to resize TTY: %v", resizeErr) } } @@ -443,13 +443,13 @@ func ExecStartAndAttach(ctx context.Context, sessionID string, options *ExecStar } defer func() { if err := terminal.Restore(int(terminalFile.Fd()), state); err != nil { - logrus.Errorf("unable to restore terminal: %q", err) + logrus.Errorf("Unable to restore terminal: %q", err) } logrus.SetFormatter(&logrus.TextFormatter{}) }() w, h, err := terminal.GetSize(int(terminalFile.Fd())) if err != nil { - logrus.Warnf("failed to obtain TTY size: %v", err) + logrus.Warnf("Failed to obtain TTY size: %v", err) } body.Width = uint16(w) body.Height = uint16(h) @@ -502,7 +502,7 @@ func ExecStartAndAttach(ctx context.Context, sessionID string, options *ExecStar logrus.Debugf("Copying STDIN to socket") _, err := utils.CopyDetachable(socket, options.InputStream, []byte{}) if err != nil { - logrus.Error("failed to write input to service: " + err.Error()) + logrus.Errorf("Failed to write input to service: %v", err) } if closeWrite, ok := socket.(CloseWriter); ok { diff --git a/pkg/bindings/images/build.go b/pkg/bindings/images/build.go index 9d5aad23b..403d90721 100644 --- a/pkg/bindings/images/build.go +++ b/pkg/bindings/images/build.go @@ -16,6 +16,7 @@ import ( "strconv" "strings" + "github.com/containers/buildah/define" "github.com/containers/podman/v3/pkg/auth" "github.com/containers/podman/v3/pkg/bindings" "github.com/containers/podman/v3/pkg/domain/entities" @@ -39,6 +40,10 @@ var ( // Build creates an image using a containerfile reference func Build(ctx context.Context, containerFiles []string, options entities.BuildOptions) (*entities.BuildReport, error) { + if options.CommonBuildOpts == nil { + options.CommonBuildOpts = new(define.CommonBuildOptions) + } + params := url.Values{} if caps := options.AddCapabilities; len(caps) > 0 { @@ -230,6 +235,9 @@ func Build(ctx context.Context, containerFiles []string, options entities.BuildO params.Add("platform", platform) } } + if contextDir, err := filepath.EvalSymlinks(options.ContextDirectory); err == nil { + options.ContextDirectory = contextDir + } params.Set("pullpolicy", options.PullPolicy.String()) @@ -312,7 +320,7 @@ func Build(ctx context.Context, containerFiles []string, options entities.BuildO contextDir, err := filepath.Abs(options.ContextDirectory) if err != nil { - logrus.Errorf("cannot find absolute path of %v: %v", options.ContextDirectory, err) + logrus.Errorf("Cannot find absolute path of %v: %v", options.ContextDirectory, err) return nil, err } @@ -339,7 +347,7 @@ func Build(ctx context.Context, containerFiles []string, options entities.BuildO } containerfile, err := filepath.Abs(c) if err != nil { - logrus.Errorf("cannot find absolute path of %v: %v", c, err) + logrus.Errorf("Cannot find absolute path of %v: %v", c, err) return nil, err } @@ -371,7 +379,7 @@ func Build(ctx context.Context, containerFiles []string, options entities.BuildO } tarfile, err := nTar(append(excludes, dontexcludes...), tarContent...) if err != nil { - logrus.Errorf("cannot tar container entries %v error: %v", tarContent, err) + logrus.Errorf("Cannot tar container entries %v error: %v", tarContent, err) return nil, err } defer func() { @@ -477,7 +485,7 @@ func nTar(excludes []string, sources ...string) (io.ReadCloser, error) { for _, src := range sources { s, err := filepath.Abs(src) if err != nil { - logrus.Errorf("cannot stat one of source context: %v", err) + logrus.Errorf("Cannot stat one of source context: %v", err) merr = multierror.Append(merr, err) return } diff --git a/pkg/bindings/images/types.go b/pkg/bindings/images/types.go index 801f5ed96..dc6bd91c3 100644 --- a/pkg/bindings/images/types.go +++ b/pkg/bindings/images/types.go @@ -65,6 +65,8 @@ type ExportOptions struct { Compress *bool // Format of the output Format *string + // Accept uncompressed layers when copying OCI images. + OciAcceptUncompressedLayers *bool } //go:generate go run ../generator/generator.go PruneOptions @@ -72,6 +74,8 @@ type ExportOptions struct { type PruneOptions struct { // Prune all images All *bool + // Prune images even when they're used by external containers + External *bool // Filters to apply when pruning images Filters map[string][]string } diff --git a/pkg/bindings/images/types_export_options.go b/pkg/bindings/images/types_export_options.go index 6229e435c..649b6814e 100644 --- a/pkg/bindings/images/types_export_options.go +++ b/pkg/bindings/images/types_export_options.go @@ -46,3 +46,18 @@ func (o *ExportOptions) GetFormat() string { } return *o.Format } + +// WithOciAcceptUncompressedLayers set field OciAcceptUncompressedLayers to given value +func (o *ExportOptions) WithOciAcceptUncompressedLayers(value bool) *ExportOptions { + o.OciAcceptUncompressedLayers = &value + return o +} + +// GetOciAcceptUncompressedLayers returns value of field OciAcceptUncompressedLayers +func (o *ExportOptions) GetOciAcceptUncompressedLayers() bool { + if o.OciAcceptUncompressedLayers == nil { + var z bool + return z + } + return *o.OciAcceptUncompressedLayers +} diff --git a/pkg/bindings/images/types_prune_options.go b/pkg/bindings/images/types_prune_options.go index 77bef32e3..c9772045e 100644 --- a/pkg/bindings/images/types_prune_options.go +++ b/pkg/bindings/images/types_prune_options.go @@ -32,6 +32,21 @@ func (o *PruneOptions) GetAll() bool { return *o.All } +// WithExternal set field External to given value +func (o *PruneOptions) WithExternal(value bool) *PruneOptions { + o.External = &value + return o +} + +// GetExternal returns value of field External +func (o *PruneOptions) GetExternal() bool { + if o.External == nil { + var z bool + return z + } + return *o.External +} + // WithFilters set field Filters to given value func (o *PruneOptions) WithFilters(value map[string][]string) *PruneOptions { o.Filters = value diff --git a/pkg/bindings/test/fixture/Containerfile b/pkg/bindings/test/fixture/Containerfile new file mode 100644 index 000000000..3a1031f32 --- /dev/null +++ b/pkg/bindings/test/fixture/Containerfile @@ -0,0 +1 @@ +From quay.io/libpod/alpine_nginx diff --git a/pkg/bindings/test/images_test.go b/pkg/bindings/test/images_test.go index ff8f72c85..aa8ff0537 100644 --- a/pkg/bindings/test/images_test.go +++ b/pkg/bindings/test/images_test.go @@ -9,9 +9,11 @@ import ( "github.com/containers/podman/v3/pkg/bindings" "github.com/containers/podman/v3/pkg/bindings/containers" "github.com/containers/podman/v3/pkg/bindings/images" + "github.com/containers/podman/v3/pkg/domain/entities" . "github.com/onsi/ginkgo" . "github.com/onsi/gomega" - "github.com/onsi/gomega/gexec" + . "github.com/onsi/gomega/gexec" + . "github.com/onsi/gomega/gstruct" ) var _ = Describe("Podman images", func() { @@ -20,7 +22,7 @@ var _ = Describe("Podman images", func() { // err error // podmanTest *PodmanTestIntegration bt *bindingTest - s *gexec.Session + s *Session err error ) @@ -37,7 +39,7 @@ var _ = Describe("Podman images", func() { s = bt.startAPIService() time.Sleep(1 * time.Second) err := bt.NewConnection() - Expect(err).To(BeNil()) + Expect(err).ToNot(HaveOccurred()) }) AfterEach(func() { @@ -57,19 +59,19 @@ var _ = Describe("Podman images", func() { // Inspect by short name data, err := images.GetImage(bt.conn, alpine.shortName, nil) - Expect(err).To(BeNil()) + Expect(err).ToNot(HaveOccurred()) // Inspect with full ID _, err = images.GetImage(bt.conn, data.ID, nil) - Expect(err).To(BeNil()) + Expect(err).ToNot(HaveOccurred()) // Inspect with partial ID _, err = images.GetImage(bt.conn, data.ID[0:12], nil) - Expect(err).To(BeNil()) + Expect(err).ToNot(HaveOccurred()) // Inspect by long name _, err = images.GetImage(bt.conn, alpine.name, nil) - Expect(err).To(BeNil()) + Expect(err).ToNot(HaveOccurred()) // TODO it looks like the images API always returns size regardless // of bool or not. What should we do ? // Expect(data.Size).To(BeZero()) @@ -77,7 +79,7 @@ var _ = Describe("Podman images", func() { options := new(images.GetOptions).WithSize(true) // Enabling the size parameter should result in size being populated data, err = images.GetImage(bt.conn, alpine.name, options) - Expect(err).To(BeNil()) + Expect(err).ToNot(HaveOccurred()) Expect(data.Size).To(BeNumerically(">", 0)) }) @@ -90,7 +92,7 @@ var _ = Describe("Podman images", func() { // Remove an image by name, validate image is removed and error is nil inspectData, err := images.GetImage(bt.conn, busybox.shortName, nil) - Expect(err).To(BeNil()) + Expect(err).ToNot(HaveOccurred()) response, errs = images.Remove(bt.conn, []string{busybox.shortName}, nil) Expect(len(errs)).To(BeZero()) @@ -101,10 +103,10 @@ var _ = Describe("Podman images", func() { // Start a container with alpine image var top string = "top" _, err = bt.RunTopContainer(&top, nil) - Expect(err).To(BeNil()) + Expect(err).ToNot(HaveOccurred()) // we should now have a container called "top" running containerResponse, err := containers.Inspect(bt.conn, "top", nil) - Expect(err).To(BeNil()) + Expect(err).ToNot(HaveOccurred()) Expect(containerResponse.Name).To(Equal("top")) // try to remove the image "alpine". This should fail since we are not force @@ -115,7 +117,7 @@ var _ = Describe("Podman images", func() { // Removing the image "alpine" where force = true options := new(images.RemoveOptions).WithForce(true) response, errs = images.Remove(bt.conn, []string{alpine.shortName}, options) - Expect(errs).To(BeNil()) + Expect(errs).To(Or(HaveLen(0), BeNil())) // To be extra sure, check if the previously created container // is gone as well. _, err = containers.Inspect(bt.conn, "top", nil) @@ -141,11 +143,11 @@ var _ = Describe("Podman images", func() { // Validates if the image is tagged successfully. err = images.Tag(bt.conn, alpine.shortName, "demo", alpine.shortName, nil) - Expect(err).To(BeNil()) + Expect(err).ToNot(HaveOccurred()) // Validates if name updates when the image is retagged. _, err := images.GetImage(bt.conn, "alpine:demo", nil) - Expect(err).To(BeNil()) + Expect(err).ToNot(HaveOccurred()) }) @@ -154,7 +156,7 @@ var _ = Describe("Podman images", func() { // Array to hold the list of images returned imageSummary, err := images.List(bt.conn, nil) // There Should be no errors in the response. - Expect(err).To(BeNil()) + Expect(err).ToNot(HaveOccurred()) // Since in the begin context two images are created the // list context should have only 2 images Expect(len(imageSummary)).To(Equal(2)) @@ -163,23 +165,23 @@ var _ = Describe("Podman images", func() { // And the count should be three now. bt.Pull("testimage:20200929") imageSummary, err = images.List(bt.conn, nil) - Expect(err).To(BeNil()) - Expect(len(imageSummary)).To(Equal(3)) + Expect(err).ToNot(HaveOccurred()) + Expect(len(imageSummary)).To(BeNumerically(">=", 2)) // Validate the image names. var names []string for _, i := range imageSummary { names = append(names, i.RepoTags...) } - Expect(StringInSlice(alpine.name, names)).To(BeTrue()) - Expect(StringInSlice(busybox.name, names)).To(BeTrue()) + Expect(names).To(ContainElement(alpine.name)) + Expect(names).To(ContainElement(busybox.name)) // List images with a filter filters := make(map[string][]string) filters["reference"] = []string{alpine.name} options := new(images.ListOptions).WithFilters(filters).WithAll(false) filteredImages, err := images.List(bt.conn, options) - Expect(err).To(BeNil()) + Expect(err).ToNot(HaveOccurred()) Expect(len(filteredImages)).To(BeNumerically("==", 1)) // List images with a bad filter @@ -194,17 +196,17 @@ var _ = Describe("Podman images", func() { It("Image Exists", func() { // exists on bogus image should be false, with no error exists, err := images.Exists(bt.conn, "foobar", nil) - Expect(err).To(BeNil()) + Expect(err).ToNot(HaveOccurred()) Expect(exists).To(BeFalse()) // exists with shortname should be true exists, err = images.Exists(bt.conn, alpine.shortName, nil) - Expect(err).To(BeNil()) + Expect(err).ToNot(HaveOccurred()) Expect(exists).To(BeTrue()) // exists with fqname should be true exists, err = images.Exists(bt.conn, alpine.name, nil) - Expect(err).To(BeNil()) + Expect(err).ToNot(HaveOccurred()) Expect(exists).To(BeTrue()) }) @@ -213,37 +215,37 @@ var _ = Describe("Podman images", func() { _, errs := images.Remove(bt.conn, []string{alpine.name}, nil) Expect(len(errs)).To(BeZero()) exists, err := images.Exists(bt.conn, alpine.name, nil) - Expect(err).To(BeNil()) + Expect(err).ToNot(HaveOccurred()) Expect(exists).To(BeFalse()) f, err := os.Open(filepath.Join(ImageCacheDir, alpine.tarballName)) defer f.Close() - Expect(err).To(BeNil()) + Expect(err).ToNot(HaveOccurred()) names, err := images.Load(bt.conn, f) - Expect(err).To(BeNil()) + Expect(err).ToNot(HaveOccurred()) Expect(names.Names[0]).To(Equal(alpine.name)) exists, err = images.Exists(bt.conn, alpine.name, nil) - Expect(err).To(BeNil()) + Expect(err).ToNot(HaveOccurred()) Expect(exists).To(BeTrue()) // load with a repo name f, err = os.Open(filepath.Join(ImageCacheDir, alpine.tarballName)) - Expect(err).To(BeNil()) + Expect(err).ToNot(HaveOccurred()) _, errs = images.Remove(bt.conn, []string{alpine.name}, nil) Expect(len(errs)).To(BeZero()) exists, err = images.Exists(bt.conn, alpine.name, nil) - Expect(err).To(BeNil()) + Expect(err).ToNot(HaveOccurred()) Expect(exists).To(BeFalse()) names, err = images.Load(bt.conn, f) - Expect(err).To(BeNil()) + Expect(err).ToNot(HaveOccurred()) Expect(names.Names[0]).To(Equal(alpine.name)) // load with a bad repo name should trigger a 500 f, err = os.Open(filepath.Join(ImageCacheDir, alpine.tarballName)) - Expect(err).To(BeNil()) + Expect(err).ToNot(HaveOccurred()) _, errs = images.Remove(bt.conn, []string{alpine.name}, nil) Expect(len(errs)).To(BeZero()) exists, err = images.Exists(bt.conn, alpine.name, nil) - Expect(err).To(BeNil()) + Expect(err).ToNot(HaveOccurred()) Expect(exists).To(BeFalse()) }) @@ -252,11 +254,11 @@ var _ = Describe("Podman images", func() { exportPath := filepath.Join(bt.tempDirPath, alpine.tarballName) w, err := os.Create(filepath.Join(bt.tempDirPath, alpine.tarballName)) defer w.Close() - Expect(err).To(BeNil()) + Expect(err).ToNot(HaveOccurred()) err = images.Export(bt.conn, []string{alpine.name}, w, nil) - Expect(err).To(BeNil()) + Expect(err).ToNot(HaveOccurred()) _, err = os.Stat(exportPath) - Expect(err).To(BeNil()) + Expect(err).ToNot(HaveOccurred()) // TODO how do we verify that a format change worked? }) @@ -266,21 +268,21 @@ var _ = Describe("Podman images", func() { _, errs := images.Remove(bt.conn, []string{alpine.name}, nil) Expect(len(errs)).To(BeZero()) exists, err := images.Exists(bt.conn, alpine.name, nil) - Expect(err).To(BeNil()) + Expect(err).ToNot(HaveOccurred()) Expect(exists).To(BeFalse()) f, err := os.Open(filepath.Join(ImageCacheDir, alpine.tarballName)) defer f.Close() - Expect(err).To(BeNil()) + Expect(err).ToNot(HaveOccurred()) changes := []string{"CMD /bin/foobar"} testMessage := "test_import" options := new(images.ImportOptions).WithMessage(testMessage).WithChanges(changes).WithReference(alpine.name) _, err = images.Import(bt.conn, f, options) - Expect(err).To(BeNil()) + Expect(err).ToNot(HaveOccurred()) exists, err = images.Exists(bt.conn, alpine.name, nil) - Expect(err).To(BeNil()) + Expect(err).ToNot(HaveOccurred()) Expect(exists).To(BeTrue()) data, err := images.GetImage(bt.conn, alpine.name, nil) - Expect(err).To(BeNil()) + Expect(err).ToNot(HaveOccurred()) Expect(data.Comment).To(Equal(testMessage)) }) @@ -294,9 +296,9 @@ var _ = Describe("Podman images", func() { var foundID bool data, err := images.GetImage(bt.conn, alpine.name, nil) - Expect(err).To(BeNil()) + Expect(err).ToNot(HaveOccurred()) history, err := images.History(bt.conn, alpine.name, nil) - Expect(err).To(BeNil()) + Expect(err).ToNot(HaveOccurred()) for _, i := range history { if i.ID == data.ID { foundID = true @@ -308,7 +310,7 @@ var _ = Describe("Podman images", func() { It("Search for an image", func() { reports, err := images.Search(bt.conn, "alpine", nil) - Expect(err).To(BeNil()) + Expect(err).ToNot(HaveOccurred()) Expect(len(reports)).To(BeNumerically(">", 1)) var foundAlpine bool for _, i := range reports { @@ -322,7 +324,7 @@ var _ = Describe("Podman images", func() { // Search for alpine with a limit of 10 options := new(images.SearchOptions).WithLimit(10) reports, err = images.Search(bt.conn, "docker.io/alpine", options) - Expect(err).To(BeNil()) + Expect(err).ToNot(HaveOccurred()) Expect(len(reports)).To(BeNumerically("<=", 10)) filters := make(map[string][]string) @@ -330,7 +332,7 @@ var _ = Describe("Podman images", func() { // Search for alpine with stars greater than 100 options = new(images.SearchOptions).WithFilters(filters) reports, err = images.Search(bt.conn, "docker.io/alpine", options) - Expect(err).To(BeNil()) + Expect(err).ToNot(HaveOccurred()) for _, i := range reports { Expect(i.Stars).To(BeNumerically(">=", 100)) } @@ -367,4 +369,12 @@ var _ = Describe("Podman images", func() { _, err = images.Pull(bt.conn, "bogus-transport:bogus.com/image:reference", nil) Expect(err).To(HaveOccurred()) }) + + It("Build no options", func() { + results, err := images.Build(bt.conn, []string{"fixture/Containerfile"}, entities.BuildOptions{}) + Expect(err).ToNot(HaveOccurred()) + Expect(*results).To(MatchFields(IgnoreMissing, Fields{ + "ID": Not(BeEmpty()), + })) + }) }) diff --git a/pkg/checkpoint/checkpoint_restore.go b/pkg/checkpoint/checkpoint_restore.go index 9fdf04933..f53e31f9b 100644 --- a/pkg/checkpoint/checkpoint_restore.go +++ b/pkg/checkpoint/checkpoint_restore.go @@ -51,7 +51,7 @@ func CRImportCheckpoint(ctx context.Context, runtime *libpod.Runtime, restoreOpt } defer func() { if err := os.RemoveAll(dir); err != nil { - logrus.Errorf("could not recursively remove %s: %q", dir, err) + logrus.Errorf("Could not recursively remove %s: %q", dir, err) } }() err = archive.Untar(archiveFile, dir, options) diff --git a/pkg/domain/entities/images.go b/pkg/domain/entities/images.go index c575212b1..2822b1ad7 100644 --- a/pkg/domain/entities/images.go +++ b/pkg/domain/entities/images.go @@ -251,8 +251,9 @@ type ImageListOptions struct { } type ImagePruneOptions struct { - All bool `json:"all" schema:"all"` - Filter []string `json:"filter" schema:"filter"` + All bool `json:"all" schema:"all"` + External bool `json:"external" schema:"external"` + Filter []string `json:"filter" schema:"filter"` } type ImageTagOptions struct{} @@ -301,10 +302,10 @@ type ImageSaveOptions struct { // than one image. Additional tags will be interpreted as references // to images which are added to the archive. MultiImageArchive bool + // Accept uncompressed layers when copying OCI images. + OciAcceptUncompressedLayers bool // Output - write image to the specified path. Output string - // Do not save the signature from the source image - RemoveSignatures bool // Quiet - suppress output when copying images Quiet bool } diff --git a/pkg/domain/infra/abi/containers.go b/pkg/domain/infra/abi/containers.go index dc5f7a0df..8e7e2d411 100644 --- a/pkg/domain/infra/abi/containers.go +++ b/pkg/domain/infra/abi/containers.go @@ -169,6 +169,10 @@ func (ic *ContainerEngine) ContainerStop(ctx context.Context, namesOrIds []strin logrus.Debugf("Container %s is already stopped", c.ID()) case options.All && errors.Cause(err) == define.ErrCtrStateInvalid: logrus.Debugf("Container %s is not running, could not stop", c.ID()) + // container never created in OCI runtime + // docker parity: do nothing just return container id + case errors.Cause(err) == define.ErrCtrStateInvalid: + logrus.Debugf("Container %s is either not created on runtime or is in a invalid state", c.ID()) default: return err } @@ -825,26 +829,12 @@ func (ic *ContainerEngine) ContainerStart(ctx context.Context, namesOrIds []stri }) if ctr.AutoRemove() { if err := ic.removeContainer(ctx, ctr, entities.RmOptions{}); err != nil { - logrus.Errorf("Error removing container %s: %v", ctr.ID(), err) + logrus.Errorf("Removing container %s: %v", ctr.ID(), err) } } return reports, errors.Wrapf(err, "unable to start container %s", ctr.ID()) } - - if ecode, err := ctr.Wait(ctx); err != nil { - if errors.Cause(err) == define.ErrNoSuchCtr { - // Check events - event, err := ic.Libpod.GetLastContainerEvent(ctx, ctr.ID(), events.Exited) - if err != nil { - logrus.Errorf("Cannot get exit code: %v", err) - exitCode = define.ExecErrorCodeNotFound - } else { - exitCode = event.ContainerExitCode - } - } - } else { - exitCode = int(ecode) - } + exitCode = ic.GetContainerExitCode(ctx, ctr) reports = append(reports, &entities.ContainerStartReport{ Id: ctr.ID(), RawInput: rawInput, @@ -874,7 +864,7 @@ func (ic *ContainerEngine) ContainerStart(ctx context.Context, namesOrIds []stri reports = append(reports, report) if ctr.AutoRemove() { if err := ic.removeContainer(ctx, ctr, entities.RmOptions{}); err != nil { - logrus.Errorf("Error removing container %s: %v", ctr.ID(), err) + logrus.Errorf("Removing container %s: %v", ctr.ID(), err) } } continue @@ -985,34 +975,43 @@ func (ic *ContainerEngine) ContainerRun(ctx context.Context, opts entities.Conta report.ExitCode = define.ExitCode(err) return &report, err } - - if ecode, err := ctr.Wait(ctx); err != nil { - if errors.Cause(err) == define.ErrNoSuchCtr { - // Check events - event, err := ic.Libpod.GetLastContainerEvent(ctx, ctr.ID(), events.Exited) - if err != nil { - logrus.Errorf("Cannot get exit code: %v", err) - report.ExitCode = define.ExecErrorCodeNotFound - } else { - report.ExitCode = event.ContainerExitCode - } - } - } else { - report.ExitCode = int(ecode) - } + report.ExitCode = ic.GetContainerExitCode(ctx, ctr) if opts.Rm && !ctr.ShouldRestart(ctx) { if err := ic.Libpod.RemoveContainer(ctx, ctr, false, true); err != nil { if errors.Cause(err) == define.ErrNoSuchCtr || errors.Cause(err) == define.ErrCtrRemoved { logrus.Infof("Container %s was already removed, skipping --rm", ctr.ID()) } else { - logrus.Errorf("Error removing container %s: %v", ctr.ID(), err) + logrus.Errorf("Removing container %s: %v", ctr.ID(), err) } } } return &report, nil } +func (ic *ContainerEngine) GetContainerExitCode(ctx context.Context, ctr *libpod.Container) int { + exitCode, err := ctr.Wait(ctx) + if err == nil { + return int(exitCode) + } + if errors.Cause(err) != define.ErrNoSuchCtr { + logrus.Errorf("Could not retrieve exit code: %v", err) + return define.ExecErrorCodeNotFound + } + // Make 4 attempt with 0.25s backoff between each for 1 second total + var event *events.Event + for i := 0; i < 4; i++ { + event, err = ic.Libpod.GetLastContainerEvent(ctx, ctr.ID(), events.Exited) + if err != nil { + time.Sleep(250 * time.Millisecond) + continue + } + return int(event.ContainerExitCode) + } + logrus.Errorf("Could not retrieve exit code from event: %v", err) + return define.ExecErrorCodeNotFound +} + func (ic *ContainerEngine) ContainerLogs(ctx context.Context, containers []string, options entities.ContainerLogsOptions) error { if options.StdoutWriter == nil && options.StderrWriter == nil { return errors.New("no io.Writer set for container logs") diff --git a/pkg/domain/infra/abi/containers_runlabel.go b/pkg/domain/infra/abi/containers_runlabel.go index 435baa8c8..add82f0fb 100644 --- a/pkg/domain/infra/abi/containers_runlabel.go +++ b/pkg/domain/infra/abi/containers_runlabel.go @@ -87,7 +87,7 @@ func (ic *ContainerEngine) ContainerRunlabel(ctx context.Context, label string, ctr, err := ic.Libpod.LookupContainer(name) if err != nil { if errors.Cause(err) != define.ErrNoSuchCtr { - logrus.Debugf("Error occurred searching for container %s: %s", name, err.Error()) + logrus.Debugf("Error occurred searching for container %s: %v", name, err) return err } } else { @@ -167,7 +167,7 @@ func generateRunlabelCommand(runlabel string, img *libimage.Image, inputName str // I would prefer to use os.getenv but it appears PWD is not in the os env list. d, err := os.Getwd() if err != nil { - logrus.Error("unable to determine current working directory") + logrus.Error("Unable to determine current working directory") return "" } return d diff --git a/pkg/domain/infra/abi/generate.go b/pkg/domain/infra/abi/generate.go index 1e614ce58..081a2464b 100644 --- a/pkg/domain/infra/abi/generate.go +++ b/pkg/domain/infra/abi/generate.go @@ -107,7 +107,7 @@ func (ic *ContainerEngine) GenerateKube(ctx context.Context, nameOrIDs []string, // Generate kube pods and services from pods. if len(pods) >= 1 { - pos, svcs, err := getKubePods(pods, options.Service) + pos, svcs, err := getKubePods(ctx, pods, options.Service) if err != nil { return nil, err } @@ -120,7 +120,7 @@ func (ic *ContainerEngine) GenerateKube(ctx context.Context, nameOrIDs []string, // Generate the kube pods from containers. if len(ctrs) >= 1 { - po, err := libpod.GenerateForKube(ctrs) + po, err := libpod.GenerateForKube(ctx, ctrs) if err != nil { return nil, err } @@ -153,12 +153,12 @@ func (ic *ContainerEngine) GenerateKube(ctx context.Context, nameOrIDs []string, } // getKubePods returns kube pod and service YAML files from podman pods. -func getKubePods(pods []*libpod.Pod, getService bool) ([][]byte, [][]byte, error) { +func getKubePods(ctx context.Context, pods []*libpod.Pod, getService bool) ([][]byte, [][]byte, error) { pos := [][]byte{} svcs := [][]byte{} for _, p := range pods { - po, sp, err := p.GenerateForKube() + po, sp, err := p.GenerateForKube(ctx) if err != nil { return nil, nil, err } diff --git a/pkg/domain/infra/abi/images.go b/pkg/domain/infra/abi/images.go index a88d38a10..c06059205 100644 --- a/pkg/domain/infra/abi/images.go +++ b/pkg/domain/infra/abi/images.go @@ -41,13 +41,21 @@ func (ir *ImageEngine) Exists(_ context.Context, nameOrID string) (*entities.Boo func (ir *ImageEngine) Prune(ctx context.Context, opts entities.ImagePruneOptions) ([]*reports.PruneReport, error) { pruneOptions := &libimage.RemoveImagesOptions{ - Filters: append(opts.Filter, "containers=false", "readonly=false"), - WithSize: true, + RemoveContainerFunc: ir.Libpod.RemoveContainersForImageCallback(ctx), + IsExternalContainerFunc: ir.Libpod.IsExternalContainerCallback(ctx), + ExternalContainers: opts.External, + Filters: append(opts.Filter, "readonly=false"), + WithSize: true, } if !opts.All { pruneOptions.Filters = append(pruneOptions.Filters, "dangling=true") } + if opts.External { + pruneOptions.Filters = append(pruneOptions.Filters, "containers=external") + } else { + pruneOptions.Filters = append(pruneOptions.Filters, "containers=false") + } var pruneReports []*reports.PruneReport @@ -367,7 +375,11 @@ func (ir *ImageEngine) Load(ctx context.Context, options entities.ImageLoadOptio func (ir *ImageEngine) Save(ctx context.Context, nameOrID string, tags []string, options entities.ImageSaveOptions) error { saveOptions := &libimage.SaveOptions{} saveOptions.DirForceCompress = options.Compress - saveOptions.RemoveSignatures = options.RemoveSignatures + saveOptions.OciAcceptUncompressedLayers = options.OciAcceptUncompressedLayers + + // Force signature removal to preserve backwards compat. + // See https://github.com/containers/podman/pull/11669#issuecomment-925250264 + saveOptions.RemoveSignatures = true if !options.Quiet { saveOptions.Writer = os.Stderr @@ -572,7 +584,7 @@ func (ir *ImageEngine) Sign(ctx context.Context, names []string, options entitie } defer func() { if err = rawSource.Close(); err != nil { - logrus.Errorf("unable to close %s image source %q", srcRef.DockerReference().Name(), err) + logrus.Errorf("Unable to close %s image source %q", srcRef.DockerReference().Name(), err) } }() topManifestBlob, manifestType, err := rawSource.GetManifest(ctx, nil) diff --git a/pkg/domain/infra/abi/manifest.go b/pkg/domain/infra/abi/manifest.go index 1dd0686ac..d1bd5e2e4 100644 --- a/pkg/domain/infra/abi/manifest.go +++ b/pkg/domain/infra/abi/manifest.go @@ -146,7 +146,7 @@ func (ir *ImageEngine) remoteManifestInspect(ctx context.Context, name string) ( switch manType { case manifest.DockerV2Schema2MediaType: - logrus.Warnf("Warning! The manifest type %s is not a manifest list but a single image.", manType) + logrus.Warnf("The manifest type %s is not a manifest list but a single image.", manType) schema2Manifest, err := manifest.Schema2FromManifest(result) if err != nil { return nil, errors.Wrapf(err, "error parsing manifest blob %q as a %q", string(result), manType) diff --git a/pkg/domain/infra/abi/play.go b/pkg/domain/infra/abi/play.go index 87506f70c..cf72a6253 100644 --- a/pkg/domain/infra/abi/play.go +++ b/pkg/domain/infra/abi/play.go @@ -113,7 +113,7 @@ func (ic *ContainerEngine) PlayKube(ctx context.Context, path string, options en report.Volumes = append(report.Volumes, r.Volumes...) validKinds++ default: - logrus.Infof("kube kind %s not supported", kind) + logrus.Infof("Kube kind %s not supported", kind) continue } } @@ -319,8 +319,8 @@ func (ic *ContainerEngine) playKubePod(ctx context.Context, podName string, podY if err != nil { return nil, err } - specgenOpts := kube.CtrSpecGenOptions{ + Annotations: annotations, Container: initCtr, Image: pulledImage, Volumes: volumes, @@ -435,6 +435,7 @@ func (ic *ContainerEngine) getImageAndLabelInfo(ctx context.Context, cwd string, buildOpts.Isolation = buildahDefine.IsolationChroot buildOpts.CommonBuildOpts = commonOpts buildOpts.Output = container.Image + buildOpts.ContextDirectory = filepath.Dir(buildFile) if _, _, err := ic.Libpod.Build(ctx, *buildOpts, []string{buildFile}...); err != nil { return nil, nil, err } @@ -662,21 +663,21 @@ func getBuildFile(imageName string, cwd string) (string, error) { containerfilePath := filepath.Join(cwd, buildDirName, "Containerfile") dockerfilePath := filepath.Join(cwd, buildDirName, "Dockerfile") - _, err := os.Stat(filepath.Join(containerfilePath)) + _, err := os.Stat(containerfilePath) if err == nil { - logrus.Debugf("building %s with %s", imageName, containerfilePath) + logrus.Debugf("Building %s with %s", imageName, containerfilePath) return containerfilePath, nil } // If the error is not because the file does not exist, take // a mulligan and try Dockerfile. If that also fails, return that // error if err != nil && !os.IsNotExist(err) { - logrus.Errorf("%v: unable to check for %s", err, containerfilePath) + logrus.Error(err.Error()) } _, err = os.Stat(filepath.Join(dockerfilePath)) if err == nil { - logrus.Debugf("building %s with %s", imageName, dockerfilePath) + logrus.Debugf("Building %s with %s", imageName, dockerfilePath) return dockerfilePath, nil } // Strike two diff --git a/pkg/domain/infra/abi/secrets.go b/pkg/domain/infra/abi/secrets.go index 2bf8eaae3..34c230e75 100644 --- a/pkg/domain/infra/abi/secrets.go +++ b/pkg/domain/infra/abi/secrets.go @@ -21,7 +21,7 @@ func (ic *ContainerEngine) SecretCreate(ctx context.Context, name string, reader // set defaults from config for the case they are not set by an upper layer // (-> i.e. tests that talk directly to the api) - cfg, err := ic.Libpod.GetConfig() + cfg, err := ic.Libpod.GetConfigNoCopy() if err != nil { return nil, err } diff --git a/pkg/domain/infra/abi/terminal/sigproxy_linux.go b/pkg/domain/infra/abi/terminal/sigproxy_linux.go index a9bd2d5fb..3b129f5ea 100644 --- a/pkg/domain/infra/abi/terminal/sigproxy_linux.go +++ b/pkg/domain/infra/abi/terminal/sigproxy_linux.go @@ -42,7 +42,7 @@ func ProxySignals(ctr *libpod.Container) { if errors.Cause(err) == define.ErrCtrStateInvalid { logrus.Infof("Ceasing signal forwarding to container %s as it has stopped", ctr.ID()) } else { - logrus.Errorf("Error forwarding signal %d to container %s: %v", s, ctr.ID(), err) + logrus.Errorf("forwarding signal %d to container %s: %v", s, ctr.ID(), err) } // If the container dies, and we find out here, // we need to forward that one signal to @@ -51,7 +51,7 @@ func ProxySignals(ctr *libpod.Container) { // play out. signal.StopCatch(sigBuffer) if err := syscall.Kill(syscall.Getpid(), s.(syscall.Signal)); err != nil { - logrus.Errorf("failed to kill pid %d", syscall.Getpid()) + logrus.Errorf("Failed to kill pid %d", syscall.Getpid()) } return } diff --git a/pkg/domain/infra/abi/terminal/terminal_linux.go b/pkg/domain/infra/abi/terminal/terminal_linux.go index 09c0f802d..ba047bf33 100644 --- a/pkg/domain/infra/abi/terminal/terminal_linux.go +++ b/pkg/domain/infra/abi/terminal/terminal_linux.go @@ -29,7 +29,7 @@ func ExecAttachCtr(ctx context.Context, ctr *libpod.Container, execConfig *libpo defer cancel() defer func() { if err := restoreTerminal(oldTermState); err != nil { - logrus.Errorf("unable to restore terminal: %q", err) + logrus.Errorf("Unable to restore terminal: %q", err) } }() } @@ -53,7 +53,7 @@ func StartAttachCtr(ctx context.Context, ctr *libpod.Container, stdout, stderr, } defer func() { if err := restoreTerminal(oldTermState); err != nil { - logrus.Errorf("unable to restore terminal: %q", err) + logrus.Errorf("Unable to restore terminal: %q", err) } }() defer cancel() diff --git a/pkg/domain/infra/abi/trust.go b/pkg/domain/infra/abi/trust.go index d3aff62ba..af7814163 100644 --- a/pkg/domain/infra/abi/trust.go +++ b/pkg/domain/infra/abi/trust.go @@ -165,7 +165,7 @@ var typeDescription = map[string]string{"insecureAcceptAnything": "accept", "sig func trustTypeDescription(trustType string) string { trustDescription, exist := typeDescription[trustType] if !exist { - logrus.Warnf("invalid trust type %s", trustType) + logrus.Warnf("Invalid trust type %s", trustType) } return trustDescription } diff --git a/pkg/domain/infra/runtime_libpod.go b/pkg/domain/infra/runtime_libpod.go index 5cbee2e76..7ec6135ee 100644 --- a/pkg/domain/infra/runtime_libpod.go +++ b/pkg/domain/infra/runtime_libpod.go @@ -369,7 +369,7 @@ func StartWatcher(rt *libpod.Runtime) { logrus.Debugf("waiting for SIGHUP to reload configuration") <-ch if err := rt.Reload(); err != nil { - logrus.Errorf("unable to reload configuration: %v", err) + logrus.Errorf("Unable to reload configuration: %v", err) continue } } diff --git a/pkg/domain/infra/tunnel/containers.go b/pkg/domain/infra/tunnel/containers.go index 81ddce42f..9fe2d163c 100644 --- a/pkg/domain/infra/tunnel/containers.go +++ b/pkg/domain/infra/tunnel/containers.go @@ -561,7 +561,7 @@ func (ic *ContainerEngine) ContainerStart(ctx context.Context, namesOrIds []stri errorhandling.Contains(err, define.ErrCtrRemoved) { logrus.Debugf("Container %s does not exist: %v", id, err) } else { - logrus.Errorf("Error removing container %s: %v", id, err) + logrus.Errorf("Removing container %s: %v", id, err) } } } @@ -646,7 +646,7 @@ func (ic *ContainerEngine) ContainerStart(ctx context.Context, namesOrIds []stri errorhandling.Contains(err, types.ErrLayerUnknown) { logrus.Debugf("Container %s does not exist: %v", ctr.ID, err) } else { - logrus.Errorf("Error removing container %s: %v", ctr.ID, err) + logrus.Errorf("Removing container %s: %v", ctr.ID, err) } } } @@ -731,7 +731,7 @@ func (ic *ContainerEngine) ContainerRun(ctx context.Context, opts entities.Conta errorhandling.Contains(err, types.ErrLayerUnknown) { logrus.Debugf("Container %s does not exist: %v", con.ID, err) } else { - logrus.Errorf("Error removing container %s: %v", con.ID, err) + logrus.Errorf("Removing container %s: %v", con.ID, err) } } } diff --git a/pkg/domain/infra/tunnel/images.go b/pkg/domain/infra/tunnel/images.go index 9a746d68c..d41a20348 100644 --- a/pkg/domain/infra/tunnel/images.go +++ b/pkg/domain/infra/tunnel/images.go @@ -95,7 +95,7 @@ func (ir *ImageEngine) Prune(ctx context.Context, opts entities.ImagePruneOption f := strings.Split(filter, "=") filters[f[0]] = f[1:] } - options := new(images.PruneOptions).WithAll(opts.All).WithFilters(filters) + options := new(images.PruneOptions).WithAll(opts.All).WithFilters(filters).WithExternal(opts.External) reports, err := images.Prune(ir.ClientCtx, options) if err != nil { return nil, err @@ -256,6 +256,7 @@ func (ir *ImageEngine) Save(ctx context.Context, nameOrID string, tags []string, err error ) options := new(images.ExportOptions).WithFormat(opts.Format).WithCompress(opts.Compress) + options = options.WithOciAcceptUncompressedLayers(opts.OciAcceptUncompressedLayers) switch opts.Format { case "oci-dir", "docker-dir": diff --git a/pkg/errorhandling/errorhandling.go b/pkg/errorhandling/errorhandling.go index 6adbc9f34..44a0c3efd 100644 --- a/pkg/errorhandling/errorhandling.go +++ b/pkg/errorhandling/errorhandling.go @@ -63,7 +63,7 @@ func StringsToErrors(strErrs []string) []error { // a defer. func SyncQuiet(f *os.File) { if err := f.Sync(); err != nil { - logrus.Errorf("unable to sync file %s: %q", f.Name(), err) + logrus.Errorf("Unable to sync file %s: %q", f.Name(), err) } } @@ -71,7 +71,7 @@ func SyncQuiet(f *os.File) { // a defer. func CloseQuiet(f *os.File) { if err := f.Close(); err != nil { - logrus.Errorf("unable to close file %s: %q", f.Name(), err) + logrus.Errorf("Unable to close file %s: %q", f.Name(), err) } } diff --git a/pkg/hooks/exec/exec.go b/pkg/hooks/exec/exec.go index f6b6636ad..2b7bc5f31 100644 --- a/pkg/hooks/exec/exec.go +++ b/pkg/hooks/exec/exec.go @@ -56,7 +56,7 @@ func Run(ctx context.Context, hook *rspec.Hook, state []byte, stdout io.Writer, return err, err case <-ctx.Done(): if err := cmd.Process.Kill(); err != nil { - logrus.Errorf("failed to kill pid %v", cmd.Process) + logrus.Errorf("Failed to kill pid %v", cmd.Process) } timer := time.NewTimer(postKillTimeout) defer timer.Stop() diff --git a/pkg/hooks/exec/runtimeconfigfilter.go b/pkg/hooks/exec/runtimeconfigfilter.go index 10b8fedc2..3ab3073b2 100644 --- a/pkg/hooks/exec/runtimeconfigfilter.go +++ b/pkg/hooks/exec/runtimeconfigfilter.go @@ -61,7 +61,7 @@ func RuntimeConfigFilter(ctx context.Context, hooks []spec.Hook, config *spec.Sp if err == nil { logrus.Debugf("precreate hook %d made configuration changes:\n%s", i, diff) } else { - logrus.Warnf("precreate hook %d made configuration changes, but we could not compute a diff: %v", i, err) + logrus.Warnf("Precreate hook %d made configuration changes, but we could not compute a diff: %v", i, err) } } diff --git a/pkg/hooks/monitor.go b/pkg/hooks/monitor.go index 6fa94cd17..ece6e52d1 100644 --- a/pkg/hooks/monitor.go +++ b/pkg/hooks/monitor.go @@ -36,7 +36,7 @@ func (m *Manager) Monitor(ctx context.Context, sync chan<- error) { for _, dir := range m.directories { err = watcher.Add(dir) if err != nil { - logrus.Errorf("failed to watch %q for hooks", dir) + logrus.Errorf("Failed to watch %q for hooks", dir) sync <- err return } @@ -52,7 +52,7 @@ func (m *Manager) Monitor(ctx context.Context, sync chan<- error) { for _, dir := range m.directories { err = ReadDir(dir, m.extensionStages, m.hooks) if err != nil { - logrus.Errorf("failed loading hooks for %s: %v", event.Name, err) + logrus.Errorf("Failed loading hooks for %s: %v", event.Name, err) } } case <-ctx.Done(): diff --git a/pkg/machine/config.go b/pkg/machine/config.go index 8db2335aa..3ff5c7fe7 100644 --- a/pkg/machine/config.go +++ b/pkg/machine/config.go @@ -58,6 +58,9 @@ type ListResponse struct { LastUp time.Time Running bool VMType string + CPUs uint64 + Memory uint64 + DiskSize uint64 } type SSHOptions struct { diff --git a/pkg/machine/fcos.go b/pkg/machine/fcos.go index cfcadeb02..99197ac0e 100644 --- a/pkg/machine/fcos.go +++ b/pkg/machine/fcos.go @@ -139,6 +139,8 @@ func getFCOSDownload(imageStream string) (*fcosDownloadInfo, error) { ) switch imageStream { case "testing", "": + streamType = fedoracoreos.StreamTesting + case "next": streamType = fedoracoreos.StreamNext case "stable": streamType = fedoracoreos.StreamStable diff --git a/pkg/machine/ignition.go b/pkg/machine/ignition.go index 89b556b14..e211f5ea6 100644 --- a/pkg/machine/ignition.go +++ b/pkg/machine/ignition.go @@ -6,6 +6,7 @@ import ( "encoding/json" "fmt" "io/ioutil" + "net/url" ) /* @@ -80,6 +81,7 @@ func NewIgnitionFile(ign DynamicIgnition) error { // so a listening host knows it can being interacting with it ready := `[Unit] Requires=dev-virtio\\x2dports-%s.device +After=remove-moby.service OnFailure=emergency.target OnFailureJobMode=isolate [Service] @@ -89,6 +91,23 @@ ExecStart=/bin/sh -c '/usr/bin/echo Ready >/dev/%s' [Install] RequiredBy=multi-user.target ` + deMoby := `[Unit] +Description=Remove moby-engine +# Run once for the machine +After=systemd-machine-id-commit.service +Before=zincati.service +ConditionPathExists=!/var/lib/%N.stamp + +[Service] +Type=oneshot +RemainAfterExit=yes +ExecStart=/usr/bin/rpm-ostree override remove moby-engine +ExecStart=/usr/bin/rpm-ostree ex apply-live --allow-replacement +ExecStartPost=/bin/touch /var/lib/%N.stamp + +[Install] +WantedBy=multi-user.target + ` _ = ready ignSystemd := Systemd{ Units: []Unit{ @@ -101,6 +120,21 @@ RequiredBy=multi-user.target Name: "ready.service", Contents: strToPtr(fmt.Sprintf(ready, "vport1p1", "vport1p1")), }, + { + Enabled: boolToPtr(false), + Name: "docker.service", + Mask: boolToPtr(true), + }, + { + Enabled: boolToPtr(false), + Name: "docker.socket", + Mask: boolToPtr(true), + }, + { + Enabled: boolToPtr(true), + Name: "remove-moby.service", + Contents: &deMoby, + }, }} ignConfig := Config{ Ignition: ignVersion, @@ -161,6 +195,22 @@ func getFiles(usrName string) []File { var ( files []File ) + + lingerExample := `[Unit] +Description=A systemd user unit demo +After=network-online.target +Wants=network-online.target podman.socket +[Service] +ExecStart=/usr/bin/sleep infinity +` + containers := `[containers] +netns="bridge" +rootless_networking="cni" +` + rootContainers := `[engine] +machine_enabled=true +` + // Add a fake systemd service to get the user socket rolling files = append(files, File{ Node: Node{ @@ -171,7 +221,7 @@ func getFiles(usrName string) []File { FileEmbedded1: FileEmbedded1{ Append: nil, Contents: Resource{ - Source: strToPtr("data:,%5BUnit%5D%0ADescription%3DA%20systemd%20user%20unit%20demo%0AAfter%3Dnetwork-online.target%0AWants%3Dnetwork-online.target%20podman.socket%0A%5BService%5D%0AExecStart%3D%2Fusr%2Fbin%2Fsleep%20infinity%0A"), + Source: encodeDataURLPtr(lingerExample), }, Mode: intToPtr(0744), }, @@ -188,7 +238,7 @@ func getFiles(usrName string) []File { FileEmbedded1: FileEmbedded1{ Append: nil, Contents: Resource{ - Source: strToPtr("data:,%5Bcontainers%5D%0D%0Anetns%3D%22bridge%22%0D%0Arootless_networking%3D%22cni%22"), + Source: encodeDataURLPtr(containers), }, Mode: intToPtr(0744), }, @@ -213,7 +263,7 @@ func getFiles(usrName string) []File { FileEmbedded1: FileEmbedded1{ Append: nil, Contents: Resource{ - Source: strToPtr("data:,%5Bengine%5D%0Amachine_enabled%3Dtrue%0A"), + Source: encodeDataURLPtr(rootContainers), }, Mode: intToPtr(0644), }, @@ -233,7 +283,22 @@ func getFiles(usrName string) []File { FileEmbedded1: FileEmbedded1{ Append: nil, Contents: Resource{ - Source: strToPtr("data:,unqualified-search-registries%3D%5B%22docker.io%22%5D"), + Source: encodeDataURLPtr("unqualified-search-registries=[\"docker.io\"]\n"), + }, + Mode: intToPtr(0644), + }, + }) + + files = append(files, File{ + Node: Node{ + Path: "/etc/tmpfiles.d/podman-docker.conf", + }, + FileEmbedded1: FileEmbedded1{ + Append: nil, + // Create a symlink from the docker socket to the podman socket. + // Taken from https://github.com/containers/podman/blob/main/contrib/systemd/system/podman-docker.conf + Contents: Resource{ + Source: encodeDataURLPtr("L+ /run/docker.sock - - - - /run/podman/podman.sock\n"), }, Mode: intToPtr(0644), }, @@ -253,5 +318,20 @@ func getLinks(usrName string) []Link { Hard: boolToPtr(false), Target: "/home/" + usrName + "/.config/systemd/user/linger-example.service", }, + }, { + Node: Node{ + Group: getNodeGrp("root"), + Path: "/usr/local/bin/docker", + Overwrite: boolToPtr(true), + User: getNodeUsr("root"), + }, + LinkEmbedded1: LinkEmbedded1{ + Hard: boolToPtr(false), + Target: "/usr/bin/podman", + }, }} } + +func encodeDataURLPtr(contents string) *string { + return strToPtr(fmt.Sprintf("data:,%s", url.PathEscape(contents))) +} diff --git a/pkg/machine/qemu/config.go b/pkg/machine/qemu/config.go index 3d0fa4094..9f5f45b58 100644 --- a/pkg/machine/qemu/config.go +++ b/pkg/machine/qemu/config.go @@ -17,6 +17,8 @@ type MachineVM struct { ImagePath string // Memory in megabytes assigned to the vm Memory uint64 + // Disk size in gigabytes assigned to the vm + DiskSize uint64 // Name of the vm Name string // SSH port for user networking diff --git a/pkg/machine/qemu/machine.go b/pkg/machine/qemu/machine.go index d5f538594..727b3cda4 100644 --- a/pkg/machine/qemu/machine.go +++ b/pkg/machine/qemu/machine.go @@ -64,6 +64,7 @@ func NewMachine(opts machine.InitOptions) (machine.VM, error) { vm.CPUs = opts.CPUS vm.Memory = opts.Memory + vm.DiskSize = opts.DiskSize // Look up the executable execPath, err := exec.LookPath(QemuCommand) @@ -140,7 +141,7 @@ func (v *MachineVM) Init(opts machine.InitOptions) error { v.IdentityPath = filepath.Join(sshDir, v.Name) switch opts.ImagePath { - case "testing", "stable", "": + case "testing", "next", "stable", "": // Get image as usual dd, err := machine.NewFcosDownloader(vmtype, v.Name, opts.ImagePath) if err != nil { @@ -366,7 +367,7 @@ func (v *MachineVM) Stop(name string, _ machine.StopOptions) error { return err } if _, err := os.Stat(pidFile); os.IsNotExist(err) { - logrus.Infof("pid file %s does not exist", pidFile) + logrus.Info(err) return nil } pidString, err := ioutil.ReadFile(pidFile) @@ -391,7 +392,12 @@ func (v *MachineVM) Stop(name string, _ machine.StopOptions) error { logrus.Warn(err) } // Remove socket - return os.Remove(qemuSocketFile) + if err := os.Remove(qemuSocketFile); err != nil { + return err + } + + fmt.Printf("Successfully stopped machine: %s", name) + return nil } // NewQMPMonitor creates the monitor subsection of our vm @@ -459,6 +465,22 @@ func (v *MachineVM) Remove(name string, opts machine.RemoveOptions) (string, fun for _, msg := range files { confirmationMessage += msg + "\n" } + + // Get path to socket and pidFile before we do any cleanups + qemuSocketFile, pidFile, errSocketFile := v.getSocketandPid() + //silently try to delete socket and pid file + //remove socket and pid file if any: warn at low priority if things fail + if errSocketFile == nil { + // Remove the pidfile + if err := os.Remove(pidFile); err != nil && !errors.Is(err, os.ErrNotExist) { + logrus.Debugf("Error while removing pidfile: %v", err) + } + // Remove socket + if err := os.Remove(qemuSocketFile); err != nil && !errors.Is(err, os.ErrNotExist) { + logrus.Debugf("Error while removing podman-machine-socket: %v", err) + } + } + confirmationMessage += "\n" return confirmationMessage, func() error { for _, f := range files { @@ -574,6 +596,9 @@ func GetVMInfos() ([]*machine.ListResponse, error) { listEntry.Name = vm.Name listEntry.VMType = "qemu" + listEntry.CPUs = vm.CPUs + listEntry.Memory = vm.Memory + listEntry.DiskSize = vm.DiskSize fi, err := os.Stat(fullPath) if err != nil { return err diff --git a/pkg/netns/netns_linux.go b/pkg/netns/netns_linux.go index c13ae2f4d..3e6e668b5 100644 --- a/pkg/netns/netns_linux.go +++ b/pkg/netns/netns_linux.go @@ -133,19 +133,19 @@ func NewNSWithName(name string) (ns.NetNS, error) { var origNS ns.NetNS origNS, err = ns.GetNS(threadNsPath) if err != nil { - logrus.Warnf("cannot open current network namespace %s: %q", threadNsPath, err) + logrus.Warnf("Cannot open current network namespace %s: %q", threadNsPath, err) return } defer func() { if err := origNS.Close(); err != nil { - logrus.Errorf("unable to close namespace: %q", err) + logrus.Errorf("Unable to close namespace: %q", err) } }() // create a new netns on the current thread err = unix.Unshare(unix.CLONE_NEWNET) if err != nil { - logrus.Warnf("cannot create a new network namespace: %q", err) + logrus.Warnf("Cannot create a new network namespace: %q", err) return } @@ -157,7 +157,7 @@ func NewNSWithName(name string) (ns.NetNS, error) { // the network namespace owned by root on the host. return } - logrus.Warnf("unable to reset namespace: %q", err) + logrus.Warnf("Unable to reset namespace: %q", err) } }() diff --git a/pkg/ps/ps.go b/pkg/ps/ps.go index 54079baa1..0f154c524 100644 --- a/pkg/ps/ps.go +++ b/pkg/ps/ps.go @@ -139,11 +139,11 @@ func ListContainerBatch(rt *libpod.Runtime, ctr *libpod.Container, opts entities } startedTime, err = c.StartedTime() if err != nil { - logrus.Errorf("error getting started time for %q: %v", c.ID(), err) + logrus.Errorf("Getting started time for %q: %v", c.ID(), err) } exitedTime, err = c.FinishedTime() if err != nil { - logrus.Errorf("error getting exited time for %q: %v", c.ID(), err) + logrus.Errorf("Getting exited time for %q: %v", c.ID(), err) } pid, err = c.PID() @@ -170,12 +170,12 @@ func ListContainerBatch(rt *libpod.Runtime, ctr *libpod.Container, opts entities rootFsSize, err := c.RootFsSize() if err != nil { - logrus.Errorf("error getting root fs size for %q: %v", c.ID(), err) + logrus.Errorf("Getting root fs size for %q: %v", c.ID(), err) } rwSize, err := c.RWSize() if err != nil { - logrus.Errorf("error getting rw size for %q: %v", c.ID(), err) + logrus.Errorf("Getting rw size for %q: %v", c.ID(), err) } size.RootFsSize = rootFsSize @@ -241,6 +241,13 @@ func ListContainerBatch(rt *libpod.Runtime, ctr *libpod.Container, opts entities UTS: uts, } } + + if hc, err := ctr.HealthCheckStatus(); err == nil { + ps.Status = hc + } else { + logrus.Debug(err) + } + return ps, nil } diff --git a/pkg/rootless/rootless_linux.c b/pkg/rootless/rootless_linux.c index 4d8443fcb..6ce4b1e29 100644 --- a/pkg/rootless/rootless_linux.c +++ b/pkg/rootless/rootless_linux.c @@ -212,6 +212,7 @@ can_use_shortcut () continue; if (strcmp (argv[argc], "mount") == 0 + || strcmp (argv[argc], "machine") == 0 || strcmp (argv[argc], "search") == 0 || (strcmp (argv[argc], "system") == 0 && argv[argc+1] && strcmp (argv[argc+1], "service") != 0)) { diff --git a/pkg/rootless/rootless_linux.go b/pkg/rootless/rootless_linux.go index c046ecde7..7f9228666 100644 --- a/pkg/rootless/rootless_linux.go +++ b/pkg/rootless/rootless_linux.go @@ -61,20 +61,20 @@ func IsRootless() bool { if rootlessUIDInit != 0 { // This happens if we joined the user+mount namespace as part of if err := os.Setenv("_CONTAINERS_USERNS_CONFIGURED", "done"); err != nil { - logrus.Errorf("failed to set environment variable %s as %s", "_CONTAINERS_USERNS_CONFIGURED", "done") + logrus.Errorf("Failed to set environment variable %s as %s", "_CONTAINERS_USERNS_CONFIGURED", "done") } if err := os.Setenv("_CONTAINERS_ROOTLESS_UID", fmt.Sprintf("%d", rootlessUIDInit)); err != nil { - logrus.Errorf("failed to set environment variable %s as %d", "_CONTAINERS_ROOTLESS_UID", rootlessUIDInit) + logrus.Errorf("Failed to set environment variable %s as %d", "_CONTAINERS_ROOTLESS_UID", rootlessUIDInit) } if err := os.Setenv("_CONTAINERS_ROOTLESS_GID", fmt.Sprintf("%d", rootlessGIDInit)); err != nil { - logrus.Errorf("failed to set environment variable %s as %d", "_CONTAINERS_ROOTLESS_GID", rootlessGIDInit) + logrus.Errorf("Failed to set environment variable %s as %d", "_CONTAINERS_ROOTLESS_GID", rootlessGIDInit) } } isRootless = os.Geteuid() != 0 || os.Getenv("_CONTAINERS_USERNS_CONFIGURED") != "" if !isRootless { hasCapSysAdmin, err := unshare.HasCapSysAdmin() if err != nil { - logrus.Warnf("failed to read CAP_SYS_ADMIN presence for the current process") + logrus.Warnf("Failed to read CAP_SYS_ADMIN presence for the current process") } if err == nil && !hasCapSysAdmin { isRootless = true @@ -284,12 +284,12 @@ func becomeRootInUserNS(pausePid, fileToRead string, fileOutput *os.File) (_ boo toWrite = []byte("1") } if _, err := w.Write(toWrite); err != nil { - logrus.Errorf("failed to write byte 0: %q", err) + logrus.Errorf("Failed to write byte 0: %q", err) } if retErr != nil && pid > 0 { if err := unix.Kill(pid, unix.SIGKILL); err != nil { if err != unix.ESRCH { - logrus.Errorf("failed to cleanup process %d: %v", pid, err) + logrus.Errorf("Failed to cleanup process %d: %v", pid, err) } } C.reexec_in_user_namespace_wait(C.int(pid), 0) @@ -325,7 +325,7 @@ func becomeRootInUserNS(pausePid, fileToRead string, fileOutput *os.File) (_ boo uidsMapped = err == nil } if !uidsMapped { - logrus.Warnf("using rootless single mapping into the namespace. This might break some images. Check /etc/subuid and /etc/subgid for adding sub*ids") + logrus.Warnf("Using rootless single mapping into the namespace. This might break some images. Check /etc/subuid and /etc/subgid for adding sub*ids") setgroups := fmt.Sprintf("/proc/%d/setgroups", pid) err = ioutil.WriteFile(setgroups, []byte("deny\n"), 0666) if err != nil { @@ -416,7 +416,7 @@ func becomeRootInUserNS(pausePid, fileToRead string, fileOutput *os.File) (_ boo if err := unix.Kill(int(pidC), s.(unix.Signal)); err != nil { if err != unix.ESRCH { - logrus.Errorf("failed to propagate signal to child process %d: %v", int(pidC), err) + logrus.Errorf("Failed to propagate signal to child process %d: %v", int(pidC), err) } } } diff --git a/pkg/rootlessport/rootlessport_linux.go b/pkg/rootlessport/rootlessport_linux.go index 6c7b8e6d7..37fb7ce79 100644 --- a/pkg/rootlessport/rootlessport_linux.go +++ b/pkg/rootlessport/rootlessport_linux.go @@ -125,17 +125,17 @@ func parent() error { quit := make(chan struct{}) errCh := make(chan error) // start the parent driver. initComplete will be closed when the child connected to the parent. - logrus.Infof("starting parent driver") + logrus.Infof("Starting parent driver") go func() { driverErr := driver.RunParentDriver(initComplete, quit, nil) if driverErr != nil { - logrus.WithError(driverErr).Warn("parent driver exited") + logrus.WithError(driverErr).Warn("Parent driver exited") } errCh <- driverErr close(errCh) }() opaque := driver.OpaqueForChild() - logrus.Infof("opaque=%+v", opaque) + logrus.Infof("Opaque=%+v", opaque) opaqueJSON, err := json.Marshal(opaque) if err != nil { return err @@ -146,9 +146,9 @@ func parent() error { } defer func() { // stop the child - logrus.Info("stopping child driver") + logrus.Info("Stopping child driver") if err := childQuitW.Close(); err != nil { - logrus.WithError(err).Warn("unable to close childQuitW") + logrus.WithError(err).Warn("Unable to close childQuitW") } }() @@ -164,7 +164,7 @@ func parent() error { return err } if err := childNS.Do(func(_ ns.NetNS) error { - logrus.Infof("starting child driver in child netns (%q %v)", cmd.Path, cmd.Args) + logrus.Infof("Starting child driver in child netns (%q %v)", cmd.Path, cmd.Args) return cmd.Start() }); err != nil { return err @@ -179,11 +179,11 @@ func parent() error { defer func() { if err := unix.Kill(cmd.Process.Pid, unix.SIGTERM); err != nil { - logrus.WithError(err).Warn("kill child process") + logrus.WithError(err).Warn("Kill child process") } }() - logrus.Info("waiting for initComplete") + logrus.Info("Waiting for initComplete") // wait for the child to connect to the parent outer: for { @@ -203,15 +203,15 @@ outer: } defer func() { - logrus.Info("stopping parent driver") + logrus.Info("Stopping parent driver") quit <- struct{}{} if err := <-errCh; err != nil { - logrus.WithError(err).Warn("parent driver returned error on exit") + logrus.WithError(err).Warn("Parent driver returned error on exit") } }() // let parent expose ports - logrus.Infof("exposing ports %v", cfg.Mappings) + logrus.Infof("Exposing ports %v", cfg.Mappings) if err := exposePorts(driver, cfg.Mappings, cfg.ChildIP); err != nil { return err } @@ -235,13 +235,13 @@ outer: // remove the socket file on exit defer os.Remove(socketfile) if err != nil { - logrus.Warnf("failed to close the socketDir fd: %v", err) + logrus.Warnf("Failed to close the socketDir fd: %v", err) } defer socket.Close() go serve(socket, driver) } - logrus.Info("ready") + logrus.Info("Ready") // https://github.com/containers/podman/issues/11248 // Copy /dev/null to stdout and stderr to prevent SIGPIPE errors @@ -259,7 +259,7 @@ outer: } // wait for ExitFD to be closed - logrus.Info("waiting for exitfd to be closed") + logrus.Info("Waiting for exitfd to be closed") if _, err := ioutil.ReadAll(exitR); err != nil { return err } @@ -353,10 +353,10 @@ func child() error { errCh <- dErr }() defer func() { - logrus.Info("stopping child driver") + logrus.Info("Stopping child driver") quit <- struct{}{} if err := <-errCh; err != nil { - logrus.WithError(err).Warn("child driver returned error on exit") + logrus.WithError(err).Warn("Child driver returned error on exit") } }() diff --git a/pkg/servicereaper/service.go b/pkg/servicereaper/service.go index e9c4fe908..e105148f0 100644 --- a/pkg/servicereaper/service.go +++ b/pkg/servicereaper/service.go @@ -46,7 +46,7 @@ func reaper(sigc chan os.Signal) { if err != nil { // do not log error for ECHILD if err != syscall.ECHILD { - logrus.Warnf("wait for pid %d failed: %v ", pid, err) + logrus.Warnf("Wait for pid %d failed: %v ", pid, err) } delete(s.pidMap, pid) continue diff --git a/pkg/specgen/generate/container.go b/pkg/specgen/generate/container.go index f3ee42b2f..d4bd6073e 100644 --- a/pkg/specgen/generate/container.go +++ b/pkg/specgen/generate/container.go @@ -54,7 +54,7 @@ func CompleteSpec(ctx context.Context, r *libpod.Runtime, s *specgen.SpecGenerat } } - rtc, err := r.GetConfig() + rtc, err := r.GetConfigNoCopy() if err != nil { return nil, err } diff --git a/pkg/specgen/generate/container_create.go b/pkg/specgen/generate/container_create.go index 11027ebdb..a50685d0f 100644 --- a/pkg/specgen/generate/container_create.go +++ b/pkg/specgen/generate/container_create.go @@ -24,7 +24,7 @@ import ( // Returns the created, container and any warnings resulting from creating the // container, or an error. func MakeContainer(ctx context.Context, rt *libpod.Runtime, s *specgen.SpecGenerator) (*spec.Spec, *specgen.SpecGenerator, []libpod.CtrCreateOption, error) { - rtc, err := rt.GetConfig() + rtc, err := rt.GetConfigNoCopy() if err != nil { return nil, nil, nil, err } @@ -517,6 +517,7 @@ func CreateExitCommandArgs(storageConfig types.StoreOptions, config *config.Conf "--log-level", logrus.GetLevel().String(), "--cgroup-manager", config.Engine.CgroupManager, "--tmpdir", config.Engine.TmpDir, + "--cni-config-dir", config.Network.NetworkConfigDir, } if config.Engine.OCIRuntime != "" { command = append(command, []string{"--runtime", config.Engine.OCIRuntime}...) diff --git a/pkg/specgen/generate/kube/kube.go b/pkg/specgen/generate/kube/kube.go index c01d7a1f0..9389b1a20 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/parse" "github.com/containers/common/pkg/secrets" "github.com/containers/image/v5/manifest" + "github.com/containers/podman/v3/libpod/define" "github.com/containers/podman/v3/libpod/network/types" ann "github.com/containers/podman/v3/pkg/annotations" "github.com/containers/podman/v3/pkg/domain/entities" @@ -86,6 +87,8 @@ func ToPodOpt(ctx context.Context, podName string, p entities.PodCreateOptions, } type CtrSpecGenOptions struct { + // Annotations from the Pod + Annotations map[string]string // Container as read from the pod yaml Container v1.Container // Image available to use (pulled or found local) @@ -157,7 +160,7 @@ func ToSpecGen(ctx context.Context, opts *CtrSpecGenOptions) (*specgen.SpecGener return nil, errors.Wrap(err, "Failed to set CPU quota") } if milliCPU > 0 { - period, quota := util.CoresToPeriodAndQuota(float64(milliCPU) / 1000) + period, quota := util.CoresToPeriodAndQuota(float64(milliCPU)) s.ResourceLimits.CPU = &spec.LinuxCPU{ Quota: "a, Period: &period, @@ -289,6 +292,14 @@ func ToSpecGen(ctx context.Context, opts *CtrSpecGenOptions) (*specgen.SpecGener volume.MountPath = dest switch volumeSource.Type { case KubeVolumeTypeBindMount: + // If the container has bind mounts, we need to check if + // a selinux mount option exists for it + for k, v := range opts.Annotations { + // Make sure the z/Z option is not already there (from editing the YAML) + if strings.Replace(k, define.BindMountPrefix, "", 1) == volumeSource.Source && !util.StringInSlice("z", options) && !util.StringInSlice("Z", options) { + options = append(options, v) + } + } mount := spec.Mount{ Destination: volume.MountPath, Source: volumeSource.Source, diff --git a/pkg/specgen/generate/oci.go b/pkg/specgen/generate/oci.go index 55010f716..beccd9fc2 100644 --- a/pkg/specgen/generate/oci.go +++ b/pkg/specgen/generate/oci.go @@ -62,7 +62,7 @@ func addRlimits(s *specgen.SpecGenerator, g *generate.Generator) error { if isRootless { var rlimit unix.Rlimit if err := unix.Getrlimit(unix.RLIMIT_NOFILE, &rlimit); err != nil { - logrus.Warnf("failed to return RLIMIT_NOFILE ulimit %q", err) + logrus.Warnf("Failed to return RLIMIT_NOFILE ulimit %q", err) } if rlimit.Cur < current { current = rlimit.Cur @@ -79,7 +79,7 @@ func addRlimits(s *specgen.SpecGenerator, g *generate.Generator) error { if isRootless { var rlimit unix.Rlimit if err := unix.Getrlimit(unix.RLIMIT_NPROC, &rlimit); err != nil { - logrus.Warnf("failed to return RLIMIT_NPROC ulimit %q", err) + logrus.Warnf("Failed to return RLIMIT_NPROC ulimit %q", err) } if rlimit.Cur < current { current = rlimit.Cur diff --git a/pkg/specgen/generate/security.go b/pkg/specgen/generate/security.go index a12cc09e2..a11debdb5 100644 --- a/pkg/specgen/generate/security.go +++ b/pkg/specgen/generate/security.go @@ -139,7 +139,7 @@ func securityConfigureGenerator(s *specgen.SpecGenerator, g *generate.Generator, if len(privCapsRequired) == 0 { caplist = capsRequired } else { - logrus.Errorf("capabilities requested by user or image are not allowed by default: %q", strings.Join(privCapsRequired, ",")) + logrus.Errorf("Capabilities requested by user or image are not allowed by default: %q", strings.Join(privCapsRequired, ",")) } } } diff --git a/pkg/specgen/generate/validate.go b/pkg/specgen/generate/validate.go index 50efe7fa3..b0d84825e 100644 --- a/pkg/specgen/generate/validate.go +++ b/pkg/specgen/generate/validate.go @@ -72,10 +72,9 @@ func verifyContainerResourcesCgroupV1(s *specgen.SpecGenerator) ([]string, error // Pids checks if s.ResourceLimits.Pids != nil { - pids := s.ResourceLimits.Pids // TODO: Should this be 0, or checking that ResourceLimits.Pids // is set at all? - if pids.Limit > 0 && !sysInfo.PidsLimit { + if s.ResourceLimits.Pids.Limit >= 0 && !sysInfo.PidsLimit { warnings = append(warnings, "Your kernel does not support pids limit capabilities or the cgroup is not mounted. PIDs limit discarded.") s.ResourceLimits.Pids = nil } diff --git a/pkg/specgen/specgen.go b/pkg/specgen/specgen.go index 7aa27487a..70b2aa1ef 100644 --- a/pkg/specgen/specgen.go +++ b/pkg/specgen/specgen.go @@ -411,7 +411,7 @@ type ContainerNetworkConfig struct { // Expose is a number of ports that will be forwarded to the container // if PublishExposedPorts is set. // Expose is a map of uint16 (port number) to a string representing - // protocol. Allowed protocols are "tcp", "udp", and "sctp", or some + // protocol i.e map[uint16]string. Allowed protocols are "tcp", "udp", and "sctp", or some // combination of the three separated by commas. // If protocol is set to "" we will assume TCP. // Only available if NetNS is set to Bridge or Slirp, and diff --git a/pkg/specgenutil/volumes.go b/pkg/specgenutil/volumes.go index 0ed08198f..3ce96164f 100644 --- a/pkg/specgenutil/volumes.go +++ b/pkg/specgenutil/volumes.go @@ -243,7 +243,7 @@ func getBindMount(args []string) (spec.Mount, error) { Type: define.TypeBind, } - var setSource, setDest, setRORW, setSuid, setDev, setExec, setRelabel bool + var setSource, setDest, setRORW, setSuid, setDev, setExec, setRelabel, setOwnership bool for _, val := range args { kv := strings.SplitN(val, "=", 2) @@ -343,6 +343,18 @@ func getBindMount(args []string) (spec.Mount, error) { default: return newMount, errors.Wrapf(util.ErrBadMntOption, "%s mount option must be 'private' or 'shared'", kv[0]) } + case "U", "chown": + if setOwnership { + return newMount, errors.Wrapf(optionArgError, "cannot pass 'U' or 'chown' option more than once") + } + ok, err := validChownFlag(val) + if err != nil { + return newMount, err + } + if ok { + newMount.Options = append(newMount.Options, "U") + } + setOwnership = true case "consistency": // Often used on MACs and mistakenly on Linux platforms. // Since Docker ignores this option so shall we. @@ -375,7 +387,7 @@ func getTmpfsMount(args []string) (spec.Mount, error) { Source: define.TypeTmpfs, } - var setDest, setRORW, setSuid, setDev, setExec, setTmpcopyup bool + var setDest, setRORW, setSuid, setDev, setExec, setTmpcopyup, setOwnership bool for _, val := range args { kv := strings.SplitN(val, "=", 2) @@ -431,6 +443,18 @@ func getTmpfsMount(args []string) (spec.Mount, error) { } newMount.Destination = filepath.Clean(kv[1]) setDest = true + case "U", "chown": + if setOwnership { + return newMount, errors.Wrapf(optionArgError, "cannot pass 'U' or 'chown' option more than once") + } + ok, err := validChownFlag(val) + if err != nil { + return newMount, err + } + if ok { + newMount.Options = append(newMount.Options, "U") + } + setOwnership = true case "consistency": // Often used on MACs and mistakenly on Linux platforms. // Since Docker ignores this option so shall we. @@ -486,7 +510,7 @@ func getDevptsMount(args []string) (spec.Mount, error) { func getNamedVolume(args []string) (*specgen.NamedVolume, error) { newVolume := new(specgen.NamedVolume) - var setSource, setDest, setRORW, setSuid, setDev, setExec bool + var setSource, setDest, setRORW, setSuid, setDev, setExec, setOwnership bool for _, val := range args { kv := strings.SplitN(val, "=", 2) @@ -532,6 +556,18 @@ func getNamedVolume(args []string) (*specgen.NamedVolume, error) { } newVolume.Dest = filepath.Clean(kv[1]) setDest = true + case "U", "chown": + if setOwnership { + return newVolume, errors.Wrapf(optionArgError, "cannot pass 'U' or 'chown' option more than once") + } + ok, err := validChownFlag(val) + if err != nil { + return newVolume, err + } + if ok { + newVolume.Options = append(newVolume.Options, "U") + } + setOwnership = true case "consistency": // Often used on MACs and mistakenly on Linux platforms. // Since Docker ignores this option so shall we. @@ -628,3 +664,24 @@ func getTmpfsMounts(tmpfsFlag []string) (map[string]spec.Mount, error) { } return m, nil } + +// validChownFlag ensures that the U or chown flag is correctly used +func validChownFlag(flag string) (bool, error) { + kv := strings.SplitN(flag, "=", 2) + switch len(kv) { + case 1: + case 2: + // U=[true|false] + switch strings.ToLower(kv[1]) { + case "true": + case "false": + return false, nil + default: + return false, errors.Wrapf(optionArgError, "'U' or 'chown' must be set to true or false, instead received %q", kv[1]) + } + default: + return false, errors.Wrapf(optionArgError, "badly formatted option %q", flag) + } + + return true, nil +} diff --git a/pkg/trust/trust.go b/pkg/trust/trust.go index 18a6a1717..584d1fa02 100644 --- a/pkg/trust/trust.go +++ b/pkg/trust/trust.go @@ -188,7 +188,7 @@ func GetGPGIdFromKeyPath(path string) []string { cmd := exec.Command("gpg2", "--with-colons", path) results, err := cmd.Output() if err != nil { - logrus.Errorf("error getting key identity: %s", err) + logrus.Errorf("Getting key identity: %s", err) return nil } return parseUids(results) @@ -203,7 +203,7 @@ func GetGPGIdFromKeyData(key string) []string { } tmpfileName, err := CreateTmpFile("", "", decodeKey) if err != nil { - logrus.Errorf("error creating key date temp file %s", err) + logrus.Errorf("Creating key date temp file %s", err) } defer os.Remove(tmpfileName) return GetGPGIdFromKeyPath(tmpfileName) |