diff options
Diffstat (limited to 'pkg')
23 files changed, 224 insertions, 114 deletions
diff --git a/pkg/api/handlers/compat/images_build.go b/pkg/api/handlers/compat/images_build.go index a9185c3d3..15cfc824e 100644 --- a/pkg/api/handlers/compat/images_build.go +++ b/pkg/api/handlers/compat/images_build.go @@ -140,6 +140,7 @@ func BuildImage(w http.ResponseWriter, r *http.Request) { Registry: "docker.io", Rm: true, ShmSize: 64 * 1024 * 1024, + TLSVerify: true, } decoder := r.Context().Value(api.DecoderKey).(*schema.Decoder) diff --git a/pkg/api/handlers/compat/images_search.go b/pkg/api/handlers/compat/images_search.go index a6fd3a3a1..2fc95e84e 100644 --- a/pkg/api/handlers/compat/images_search.go +++ b/pkg/api/handlers/compat/images_search.go @@ -26,6 +26,7 @@ func SearchImages(w http.ResponseWriter, r *http.Request) { ListTags bool `json:"listTags"` }{ // This is where you can override the golang default value for one of fields + TLSVerify: true, } if err := decoder.Decode(&query, r.URL.Query()); err != nil { diff --git a/pkg/api/handlers/libpod/images_push.go b/pkg/api/handlers/libpod/images_push.go index f427dc01b..e931fd2f9 100644 --- a/pkg/api/handlers/libpod/images_push.go +++ b/pkg/api/handlers/libpod/images_push.go @@ -25,13 +25,15 @@ func PushImage(w http.ResponseWriter, r *http.Request) { runtime := r.Context().Value(api.RuntimeKey).(*libpod.Runtime) query := struct { - All bool `schema:"all"` - Destination string `schema:"destination"` - Format string `schema:"format"` - RemoveSignatures bool `schema:"removeSignatures"` - TLSVerify bool `schema:"tlsVerify"` - Quiet bool `schema:"quiet"` + All bool `schema:"all"` + CompressionFormat string `schema:"compressionFormat"` + Destination string `schema:"destination"` + Format string `schema:"format"` + RemoveSignatures bool `schema:"removeSignatures"` + TLSVerify bool `schema:"tlsVerify"` + Quiet bool `schema:"quiet"` }{ + TLSVerify: true, // #14971: older versions did not sent *any* data, so we need // to be quiet by default to remain backwards compatible Quiet: true, @@ -70,13 +72,14 @@ func PushImage(w http.ResponseWriter, r *http.Request) { password = authconf.Password } options := entities.ImagePushOptions{ - All: query.All, - Authfile: authfile, - Format: query.Format, - Password: password, - Quiet: true, - RemoveSignatures: query.RemoveSignatures, - Username: username, + All: query.All, + Authfile: authfile, + CompressionFormat: query.CompressionFormat, + Format: query.Format, + Password: password, + Quiet: true, + RemoveSignatures: query.RemoveSignatures, + Username: username, } if _, found := r.URL.Query()["tlsVerify"]; found { diff --git a/pkg/api/handlers/libpod/manifests.go b/pkg/api/handlers/libpod/manifests.go index 3235a2972..2d6223e4e 100644 --- a/pkg/api/handlers/libpod/manifests.go +++ b/pkg/api/handlers/libpod/manifests.go @@ -306,10 +306,14 @@ func ManifestPush(w http.ResponseWriter, r *http.Request) { decoder := r.Context().Value(api.DecoderKey).(*schema.Decoder) query := struct { - All bool `schema:"all"` - TLSVerify bool `schema:"tlsVerify"` + All bool `schema:"all"` + CompressionFormat string `schema:"compressionFormat"` + Format string `schema:"format"` + RemoveSignatures bool `schema:"removeSignatures"` + TLSVerify bool `schema:"tlsVerify"` }{ // Add defaults here once needed. + TLSVerify: true, } if err := decoder.Decode(&query, r.URL.Query()); err != nil { utils.Error(w, http.StatusBadRequest, @@ -335,10 +339,13 @@ func ManifestPush(w http.ResponseWriter, r *http.Request) { password = authconf.Password } options := entities.ImagePushOptions{ - Authfile: authfile, - Username: username, - Password: password, - All: query.All, + All: query.All, + Authfile: authfile, + CompressionFormat: query.CompressionFormat, + Format: query.Format, + Password: password, + RemoveSignatures: query.RemoveSignatures, + Username: username, } if sys := runtime.SystemContext(); sys != nil { options.CertDir = sys.DockerCertPath diff --git a/pkg/api/server/handler_api.go b/pkg/api/server/handler_api.go index a3aa681d0..57cafa5f6 100644 --- a/pkg/api/server/handler_api.go +++ b/pkg/api/server/handler_api.go @@ -1,7 +1,10 @@ package server import ( + "bufio" + "errors" "fmt" + "net" "net/http" "runtime" @@ -9,34 +12,54 @@ import ( "github.com/sirupsen/logrus" ) +type BufferedResponseWriter struct { + b *bufio.Writer + w http.ResponseWriter +} + // APIHandler is a wrapper to enhance HandlerFunc's and remove redundant code func (s *APIServer) APIHandler(h http.HandlerFunc) http.HandlerFunc { return func(w http.ResponseWriter, r *http.Request) { // Wrapper to hide some boilerplate - fn := func(w http.ResponseWriter, r *http.Request) { - if err := r.ParseForm(); err != nil { - logrus.WithFields(logrus.Fields{ - "X-Reference-Id": r.Header.Get("X-Reference-Id"), - }).Info("Failed Request: unable to parse form: " + err.Error()) - } + s.apiWrapper(h, w, r, false) + } +} + +// An API Handler to help historical clients with broken parsing that expect +// streaming JSON payloads to be reliably messaged framed (full JSON record +// always fits in each read()) +func (s *APIServer) StreamBufferedAPIHandler(h http.HandlerFunc) http.HandlerFunc { + return func(w http.ResponseWriter, r *http.Request) { + // Wrapper to hide some boilerplate + s.apiWrapper(h, w, r, true) + } +} - cv := version.APIVersion[version.Compat][version.CurrentAPI] - w.Header().Set("API-Version", fmt.Sprintf("%d.%d", cv.Major, cv.Minor)) +func (s *APIServer) apiWrapper(h http.HandlerFunc, w http.ResponseWriter, r *http.Request, buffer bool) { + if err := r.ParseForm(); err != nil { + logrus.WithFields(logrus.Fields{ + "X-Reference-Id": r.Header.Get("X-Reference-Id"), + }).Info("Failed Request: unable to parse form: " + err.Error()) + } - lv := version.APIVersion[version.Libpod][version.CurrentAPI].String() - w.Header().Set("Libpod-API-Version", lv) - w.Header().Set("Server", "Libpod/"+lv+" ("+runtime.GOOS+")") + cv := version.APIVersion[version.Compat][version.CurrentAPI] + w.Header().Set("API-Version", fmt.Sprintf("%d.%d", cv.Major, cv.Minor)) - if s.CorsHeaders != "" { - w.Header().Set("Access-Control-Allow-Origin", s.CorsHeaders) - w.Header().Set("Access-Control-Allow-Headers", "Origin, X-Requested-With, Content-Type, Accept, X-Registry-Auth, Connection, Upgrade, X-Registry-Config") - w.Header().Set("Access-Control-Allow-Methods", "HEAD, GET, POST, DELETE, PUT, OPTIONS") - } + lv := version.APIVersion[version.Libpod][version.CurrentAPI].String() + w.Header().Set("Libpod-API-Version", lv) + w.Header().Set("Server", "Libpod/"+lv+" ("+runtime.GOOS+")") - h(w, r) - } - fn(w, r) + if s.CorsHeaders != "" { + w.Header().Set("Access-Control-Allow-Origin", s.CorsHeaders) + w.Header().Set("Access-Control-Allow-Headers", "Origin, X-Requested-With, Content-Type, Accept, X-Registry-Auth, Connection, Upgrade, X-Registry-Config") + w.Header().Set("Access-Control-Allow-Methods", "HEAD, GET, POST, DELETE, PUT, OPTIONS") } + + if buffer { + w = newBufferedResponseWriter(w) + } + + h(w, r) } // VersionedPath prepends the version parsing code @@ -44,3 +67,37 @@ func (s *APIServer) APIHandler(h http.HandlerFunc) http.HandlerFunc { func VersionedPath(p string) string { return "/v{version:[0-9][0-9A-Za-z.-]*}" + p } + +func (w *BufferedResponseWriter) Header() http.Header { + return w.w.Header() +} + +func (w *BufferedResponseWriter) Hijack() (net.Conn, *bufio.ReadWriter, error) { + _ = w.b.Flush() + if wrapped, ok := w.w.(http.Hijacker); ok { + return wrapped.Hijack() + } + + return nil, nil, errors.New("ResponseWriter does not support hijacking") +} + +func (w *BufferedResponseWriter) Write(b []byte) (int, error) { + return w.b.Write(b) +} + +func (w *BufferedResponseWriter) WriteHeader(statusCode int) { + w.w.WriteHeader(statusCode) +} + +func (w *BufferedResponseWriter) Flush() { + _ = w.b.Flush() + if wrapped, ok := w.w.(http.Flusher); ok { + wrapped.Flush() + } +} +func newBufferedResponseWriter(rw http.ResponseWriter) *BufferedResponseWriter { + return &BufferedResponseWriter{ + bufio.NewWriterSize(rw, 8192), + rw, + } +} diff --git a/pkg/api/server/register_containers.go b/pkg/api/server/register_containers.go index e2ecdb6af..b319fc14a 100644 --- a/pkg/api/server/register_containers.go +++ b/pkg/api/server/register_containers.go @@ -397,9 +397,9 @@ func (s *APIServer) registerContainersHandlers(r *mux.Router) error { // $ref: "#/responses/containerNotFound" // 500: // $ref: "#/responses/internalError" - r.HandleFunc(VersionedPath("/containers/{name}/stats"), s.APIHandler(compat.StatsContainer)).Methods(http.MethodGet) + r.HandleFunc(VersionedPath("/containers/{name}/stats"), s.StreamBufferedAPIHandler(compat.StatsContainer)).Methods(http.MethodGet) // Added non version path to URI to support docker non versioned paths - r.HandleFunc("/containers/{name}/stats", s.APIHandler(compat.StatsContainer)).Methods(http.MethodGet) + r.HandleFunc("/containers/{name}/stats", s.StreamBufferedAPIHandler(compat.StatsContainer)).Methods(http.MethodGet) // swagger:operation POST /containers/{name}/stop compat ContainerStop // --- // tags: @@ -455,9 +455,9 @@ func (s *APIServer) registerContainersHandlers(r *mux.Router) error { // $ref: "#/responses/containerNotFound" // 500: // $ref: "#/responses/internalError" - r.HandleFunc(VersionedPath("/containers/{name}/top"), s.APIHandler(compat.TopContainer)).Methods(http.MethodGet) + r.HandleFunc(VersionedPath("/containers/{name}/top"), s.StreamBufferedAPIHandler(compat.TopContainer)).Methods(http.MethodGet) // Added non version path to URI to support docker non versioned paths - r.HandleFunc("/containers/{name}/top", s.APIHandler(compat.TopContainer)).Methods(http.MethodGet) + r.HandleFunc("/containers/{name}/top", s.StreamBufferedAPIHandler(compat.TopContainer)).Methods(http.MethodGet) // swagger:operation POST /containers/{name}/unpause compat ContainerUnpause // --- // tags: diff --git a/pkg/api/server/register_events.go b/pkg/api/server/register_events.go index 76f9ec619..3442b3eab 100644 --- a/pkg/api/server/register_events.go +++ b/pkg/api/server/register_events.go @@ -34,9 +34,9 @@ func (s *APIServer) registerEventsHandlers(r *mux.Router) error { // description: returns a string of json data describing an event // 500: // "$ref": "#/responses/internalError" - r.Handle(VersionedPath("/events"), s.APIHandler(compat.GetEvents)).Methods(http.MethodGet) + r.Handle(VersionedPath("/events"), s.StreamBufferedAPIHandler(compat.GetEvents)).Methods(http.MethodGet) // Added non version path to URI to support docker non versioned paths - r.Handle("/events", s.APIHandler(compat.GetEvents)).Methods(http.MethodGet) + r.Handle("/events", s.StreamBufferedAPIHandler(compat.GetEvents)).Methods(http.MethodGet) // swagger:operation GET /libpod/events system SystemEventsLibpod // --- // tags: diff --git a/pkg/api/server/register_images.go b/pkg/api/server/register_images.go index 11ab8cae0..d71e0d470 100644 --- a/pkg/api/server/register_images.go +++ b/pkg/api/server/register_images.go @@ -192,8 +192,8 @@ func (s *APIServer) registerImagesHandlers(r *mux.Router) error { // - in: query // name: tlsVerify // type: boolean - // default: false - // description: skip TLS verification for registries + // default: true + // description: Require HTTPS and verify signatures when contacting registries. // - in: query // name: listTags // type: boolean @@ -702,9 +702,9 @@ func (s *APIServer) registerImagesHandlers(r *mux.Router) error { // $ref: "#/responses/badParamError" // 500: // $ref: "#/responses/internalError" - r.Handle(VersionedPath("/build"), s.APIHandler(compat.BuildImage)).Methods(http.MethodPost) + r.Handle(VersionedPath("/build"), s.StreamBufferedAPIHandler(compat.BuildImage)).Methods(http.MethodPost) // Added non version path to URI to support docker non versioned paths - r.Handle("/build", s.APIHandler(compat.BuildImage)).Methods(http.MethodPost) + r.Handle("/build", s.StreamBufferedAPIHandler(compat.BuildImage)).Methods(http.MethodPost) /* libpod endpoints */ @@ -1120,8 +1120,8 @@ func (s *APIServer) registerImagesHandlers(r *mux.Router) error { // - in: query // name: tlsVerify // type: boolean - // default: false - // description: skip TLS verification for registries + // default: true + // description: Require HTTPS and verify signatures when contacting registries. // - in: query // name: listTags // type: boolean diff --git a/pkg/api/server/register_manifest.go b/pkg/api/server/register_manifest.go index 4fadb92fd..19b507047 100644 --- a/pkg/api/server/register_manifest.go +++ b/pkg/api/server/register_manifest.go @@ -69,12 +69,12 @@ func (s *APIServer) registerManifestHandlers(r *mux.Router) error { // name: all // description: push all images // type: boolean - // default: false + // default: true // - in: query // name: tlsVerify // type: boolean - // default: false - // description: skip TLS verification for registries + // default: true + // description: Require HTTPS and verify signatures when contacting registries. // responses: // 200: // schema: @@ -195,8 +195,8 @@ func (s *APIServer) registerManifestHandlers(r *mux.Router) error { // - in: query // name: tlsVerify // type: boolean - // default: false - // description: skip TLS verification for registries + // default: true + // description: Require HTTPS and verify signatures when contacting registries. // - in: body // name: options // description: options for mutating a manifest diff --git a/pkg/bindings/images/types.go b/pkg/bindings/images/types.go index 0e672cdea..0664afc1b 100644 --- a/pkg/bindings/images/types.go +++ b/pkg/bindings/images/types.go @@ -123,6 +123,8 @@ type PushOptions struct { Authfile *string // Compress tarball image layers when pushing to a directory using the 'dir' transport. Compress *bool + // CompressionFormat is the format to use for the compression of the blobs + CompressionFormat *string // Manifest type of the pushed image Format *string // Password for authenticating against the registry. diff --git a/pkg/bindings/images/types_push_options.go b/pkg/bindings/images/types_push_options.go index 63a19fb81..1ae031824 100644 --- a/pkg/bindings/images/types_push_options.go +++ b/pkg/bindings/images/types_push_options.go @@ -62,6 +62,21 @@ func (o *PushOptions) GetCompress() bool { return *o.Compress } +// WithCompressionFormat set field CompressionFormat to given value +func (o *PushOptions) WithCompressionFormat(value string) *PushOptions { + o.CompressionFormat = &value + return o +} + +// GetCompressionFormat returns value of field CompressionFormat +func (o *PushOptions) GetCompressionFormat() string { + if o.CompressionFormat == nil { + var z string + return z + } + return *o.CompressionFormat +} + // WithFormat set field Format to given value func (o *PushOptions) WithFormat(value string) *PushOptions { o.Format = &value diff --git a/pkg/bindings/test/images_test.go b/pkg/bindings/test/images_test.go index a005be6ac..8f76ce456 100644 --- a/pkg/bindings/test/images_test.go +++ b/pkg/bindings/test/images_test.go @@ -120,8 +120,6 @@ var _ = Describe("Podman images", func() { // deleting hence image cannot be deleted until the container is deleted. _, errs = images.Remove(bt.conn, []string{alpine.shortName}, nil) code, _ = bindings.CheckResponseCode(errs[0]) - // FIXME FIXME FIXME: #12441: another invalid error - // FIXME FIXME FIXME: this time msg="Image used by SHA: ..." Expect(code).To(BeNumerically("==", -1)) // Removing the image "alpine" where force = true diff --git a/pkg/domain/filters/containers.go b/pkg/domain/filters/containers.go index f88a165e7..de62b6582 100644 --- a/pkg/domain/filters/containers.go +++ b/pkg/domain/filters/containers.go @@ -7,6 +7,7 @@ import ( "strings" "time" + "github.com/containers/common/pkg/filters" cutil "github.com/containers/common/pkg/util" "github.com/containers/podman/v4/libpod" "github.com/containers/podman/v4/libpod/define" @@ -24,7 +25,7 @@ func GenerateContainerFilterFuncs(filter string, filterValues []string, r *libpo case "label": // we have to match that all given labels exits on that container return func(c *libpod.Container) bool { - return util.MatchLabelFilters(filterValues, c.Labels()) + return filters.MatchLabelFilters(filterValues, c.Labels()) }, nil case "name": // we only have to match one name @@ -299,7 +300,11 @@ func GeneratePruneContainerFilterFuncs(filter string, filterValues []string, r * switch filter { case "label": return func(c *libpod.Container) bool { - return util.MatchLabelFilters(filterValues, c.Labels()) + return filters.MatchLabelFilters(filterValues, c.Labels()) + }, nil + case "label!": + return func(c *libpod.Container) bool { + return !filters.MatchLabelFilters(filterValues, c.Labels()) }, nil case "until": return prepareUntilFilterFunc(filterValues) diff --git a/pkg/domain/filters/pods.go b/pkg/domain/filters/pods.go index 78b97db64..7b0944292 100644 --- a/pkg/domain/filters/pods.go +++ b/pkg/domain/filters/pods.go @@ -6,6 +6,7 @@ import ( "strconv" "strings" + "github.com/containers/common/pkg/filters" cutil "github.com/containers/common/pkg/util" "github.com/containers/podman/v4/libpod" "github.com/containers/podman/v4/libpod/define" @@ -115,7 +116,7 @@ func GeneratePodFilterFunc(filter string, filterValues []string, r *libpod.Runti case "label": return func(p *libpod.Pod) bool { labels := p.Labels() - return util.MatchLabelFilters(filterValues, labels) + return filters.MatchLabelFilters(filterValues, labels) }, nil case "until": return func(p *libpod.Pod) bool { diff --git a/pkg/domain/filters/volumes.go b/pkg/domain/filters/volumes.go index 7c5047225..9cec39fbb 100644 --- a/pkg/domain/filters/volumes.go +++ b/pkg/domain/filters/volumes.go @@ -6,6 +6,7 @@ import ( "regexp" "strings" + pruneFilters "github.com/containers/common/pkg/filters" "github.com/containers/podman/v4/libpod" "github.com/containers/podman/v4/pkg/util" ) @@ -36,7 +37,7 @@ func GenerateVolumeFilters(filters url.Values) ([]libpod.VolumeFilter, error) { case "label": filter := val vf = append(vf, func(v *libpod.Volume) bool { - return util.MatchLabelFilters([]string{filter}, v.Labels()) + return pruneFilters.MatchLabelFilters([]string{filter}, v.Labels()) }) case "opt": filterArray := strings.SplitN(val, "=", 2) @@ -100,7 +101,7 @@ func GeneratePruneVolumeFilters(filters url.Values) ([]libpod.VolumeFilter, erro switch filter { case "label": vf = append(vf, func(v *libpod.Volume) bool { - return util.MatchLabelFilters([]string{filterVal}, v.Labels()) + return pruneFilters.MatchLabelFilters([]string{filterVal}, v.Labels()) }) case "until": f, err := createUntilFilterVolumeFunction(filterVal) diff --git a/pkg/domain/infra/abi/containers.go b/pkg/domain/infra/abi/containers.go index dd7053a23..783224e9c 100644 --- a/pkg/domain/infra/abi/containers.go +++ b/pkg/domain/infra/abi/containers.go @@ -260,6 +260,7 @@ func (ic *ContainerEngine) ContainerPrune(ctx context.Context, options entities. if err != nil { return nil, err } + filterFuncs = append(filterFuncs, generatedFunc) } return ic.Libpod.PruneContainers(filterFuncs) @@ -1060,6 +1061,15 @@ func (ic *ContainerEngine) Diff(ctx context.Context, namesOrIDs []string, opts e } func (ic *ContainerEngine) ContainerRun(ctx context.Context, opts entities.ContainerRunOptions) (*entities.ContainerRunReport, error) { + removeContainer := func(ctr *libpod.Container, force bool) error { + var timeout *uint + if err := ic.Libpod.RemoveContainer(ctx, ctr, force, true, timeout); err != nil { + logrus.Debugf("unable to remove container %s after failing to start and attach to it: %v", ctr.ID(), err) + return err + } + return nil + } + warn, err := generate.CompleteSpec(ctx, ic.Libpod, opts.Spec) if err != nil { return nil, err @@ -1080,6 +1090,8 @@ func (ic *ContainerEngine) ContainerRun(ctx context.Context, opts entities.Conta if opts.CIDFile != "" { if err := util.CreateCidFile(opts.CIDFile, ctr.ID()); err != nil { + // If you fail to create CIDFile then remove the container + _ = removeContainer(ctr, true) return nil, err } } @@ -1097,6 +1109,11 @@ func (ic *ContainerEngine) ContainerRun(ctx context.Context, opts entities.Conta if err := ctr.Start(ctx, true); err != nil { // This means the command did not exist report.ExitCode = define.ExitCode(err) + if opts.Rm { + if rmErr := removeContainer(ctr, true); rmErr != nil && !errors.Is(rmErr, define.ErrNoSuchCtr) { + logrus.Errorf("Container %s failed to be removed", ctr.ID()) + } + } return &report, err } @@ -1113,10 +1130,7 @@ func (ic *ContainerEngine) ContainerRun(ctx context.Context, opts entities.Conta return &report, nil } if opts.Rm { - var timeout *uint - if deleteError := ic.Libpod.RemoveContainer(ctx, ctr, true, false, timeout); deleteError != nil { - logrus.Debugf("unable to remove container %s after failing to start and attach to it", ctr.ID()) - } + _ = removeContainer(ctr, true) } if errors.Is(err, define.ErrWillDeadlock) { logrus.Debugf("Deadlock error on %q: %v", ctr.ID(), err) @@ -1128,8 +1142,7 @@ func (ic *ContainerEngine) ContainerRun(ctx context.Context, opts entities.Conta } report.ExitCode = ic.GetContainerExitCode(ctx, ctr) if opts.Rm && !ctr.ShouldRestart(ctx) { - var timeout *uint - if err := ic.Libpod.RemoveContainer(ctx, ctr, false, true, timeout); err != nil { + if err := removeContainer(ctr, false); err != nil { if errors.Is(err, define.ErrNoSuchCtr) || errors.Is(err, define.ErrCtrRemoved) { logrus.Infof("Container %s was already removed, skipping --rm", ctr.ID()) diff --git a/pkg/domain/infra/abi/manifest.go b/pkg/domain/infra/abi/manifest.go index d20744d76..b135b05ba 100644 --- a/pkg/domain/infra/abi/manifest.go +++ b/pkg/domain/infra/abi/manifest.go @@ -13,6 +13,7 @@ import ( "github.com/containers/common/libimage" cp "github.com/containers/image/v5/copy" "github.com/containers/image/v5/manifest" + "github.com/containers/image/v5/pkg/compression" "github.com/containers/image/v5/pkg/shortnames" "github.com/containers/image/v5/transports" "github.com/containers/image/v5/transports/alltransports" @@ -318,6 +319,22 @@ func (ir *ImageEngine) ManifestPush(ctx context.Context, name, destination strin pushOptions.SignBy = opts.SignBy pushOptions.InsecureSkipTLSVerify = opts.SkipTLSVerify + compressionFormat := opts.CompressionFormat + if compressionFormat == "" { + config, err := ir.Libpod.GetConfigNoCopy() + if err != nil { + return "", err + } + compressionFormat = config.Engine.CompressionFormat + } + if compressionFormat != "" { + algo, err := compression.AlgorithmByName(compressionFormat) + if err != nil { + return "", err + } + pushOptions.CompressionFormat = &algo + } + if opts.All { pushOptions.ImageListSelection = cp.CopyAllImages } @@ -331,7 +348,8 @@ func (ir *ImageEngine) ManifestPush(ctx context.Context, name, destination strin } if opts.Rm { - if _, rmErrors := ir.Libpod.LibimageRuntime().RemoveImages(ctx, []string{manifestList.ID()}, nil); len(rmErrors) > 0 { + rmOpts := &libimage.RemoveImagesOptions{LookupManifest: true} + if _, rmErrors := ir.Libpod.LibimageRuntime().RemoveImages(ctx, []string{manifestList.ID()}, rmOpts); len(rmErrors) > 0 { return "", fmt.Errorf("error removing manifest after push: %w", rmErrors[0]) } } diff --git a/pkg/domain/infra/tunnel/containers.go b/pkg/domain/infra/tunnel/containers.go index 272c23268..98c73c51a 100644 --- a/pkg/domain/infra/tunnel/containers.go +++ b/pkg/domain/infra/tunnel/containers.go @@ -787,8 +787,17 @@ func (ic *ContainerEngine) ContainerRun(ctx context.Context, opts entities.Conta for _, w := range con.Warnings { fmt.Fprintf(os.Stderr, "%s\n", w) } + removeContainer := func(id string, force bool) error { + removeOptions := new(containers.RemoveOptions).WithVolumes(true).WithForce(force) + reports, err := containers.Remove(ic.ClientCtx, id, removeOptions) + logIfRmError(id, err, reports) + return err + } + if opts.CIDFile != "" { if err := util.CreateCidFile(opts.CIDFile, con.ID); err != nil { + // If you fail to create CIDFile then remove the container + _ = removeContainer(con.ID, true) return nil, err } } @@ -800,6 +809,11 @@ func (ic *ContainerEngine) ContainerRun(ctx context.Context, opts entities.Conta err := containers.Start(ic.ClientCtx, con.ID, new(containers.StartOptions).WithRecursive(true)) if err != nil { report.ExitCode = define.ExitCode(err) + if opts.Rm { + if rmErr := removeContainer(con.ID, true); rmErr != nil && !errors.Is(rmErr, define.ErrNoSuchCtr) { + logrus.Errorf("Container %s failed to be removed", con.ID) + } + } } return &report, err } @@ -812,10 +826,7 @@ func (ic *ContainerEngine) ContainerRun(ctx context.Context, opts entities.Conta report.ExitCode = define.ExitCode(err) if opts.Rm { - reports, rmErr := containers.Remove(ic.ClientCtx, con.ID, new(containers.RemoveOptions).WithForce(false).WithVolumes(true)) - if rmErr != nil || reports[0].Err != nil { - logrus.Debugf("unable to remove container %s after failing to start and attach to it", con.ID) - } + _ = removeContainer(con.ID, false) } return &report, err } @@ -831,8 +842,7 @@ func (ic *ContainerEngine) ContainerRun(ctx context.Context, opts entities.Conta } if !shouldRestart { - reports, err := containers.Remove(ic.ClientCtx, con.ID, new(containers.RemoveOptions).WithForce(false).WithVolumes(true)) - logIfRmError(con.ID, err, reports) + _ = removeContainer(con.ID, false) } }() } diff --git a/pkg/domain/infra/tunnel/images.go b/pkg/domain/infra/tunnel/images.go index 9ad408850..4f79325fd 100644 --- a/pkg/domain/infra/tunnel/images.go +++ b/pkg/domain/infra/tunnel/images.go @@ -240,7 +240,7 @@ func (ir *ImageEngine) Import(ctx context.Context, opts entities.ImageImportOpti func (ir *ImageEngine) Push(ctx context.Context, source string, destination string, opts entities.ImagePushOptions) error { options := new(images.PushOptions) - options.WithAll(opts.All).WithCompress(opts.Compress).WithUsername(opts.Username).WithPassword(opts.Password).WithAuthfile(opts.Authfile).WithFormat(opts.Format).WithRemoveSignatures(opts.RemoveSignatures).WithQuiet(opts.Quiet) + options.WithAll(opts.All).WithCompress(opts.Compress).WithUsername(opts.Username).WithPassword(opts.Password).WithAuthfile(opts.Authfile).WithFormat(opts.Format).WithRemoveSignatures(opts.RemoveSignatures).WithQuiet(opts.Quiet).WithCompressionFormat(opts.CompressionFormat) if s := opts.SkipTLSVerify; s != types.OptionalBoolUndefined { if s == types.OptionalBoolTrue { diff --git a/pkg/domain/infra/tunnel/manifest.go b/pkg/domain/infra/tunnel/manifest.go index d2554f198..00ecb3b59 100644 --- a/pkg/domain/infra/tunnel/manifest.go +++ b/pkg/domain/infra/tunnel/manifest.go @@ -99,8 +99,7 @@ func (ir *ImageEngine) ManifestRm(ctx context.Context, names []string) (*entitie // ManifestPush pushes a manifest list or image index to the destination func (ir *ImageEngine) ManifestPush(ctx context.Context, name, destination string, opts entities.ImagePushOptions) (string, error) { options := new(images.PushOptions) - options.WithUsername(opts.Username).WithPassword(opts.Password).WithAuthfile(opts.Authfile).WithRemoveSignatures(opts.RemoveSignatures) - options.WithAll(opts.All) + options.WithUsername(opts.Username).WithPassword(opts.Password).WithAuthfile(opts.Authfile).WithRemoveSignatures(opts.RemoveSignatures).WithAll(opts.All).WithFormat(opts.Format).WithCompressionFormat(opts.CompressionFormat) if s := opts.SkipTLSVerify; s != types.OptionalBoolUndefined { if s == types.OptionalBoolTrue { @@ -110,5 +109,15 @@ func (ir *ImageEngine) ManifestPush(ctx context.Context, name, destination strin } } digest, err := manifests.Push(ir.ClientCtx, name, destination, options) + if err != nil { + return "", fmt.Errorf("error adding to manifest list %s: %w", name, err) + } + + if opts.Rm { + if _, rmErrors := ir.Remove(ctx, []string{name}, entities.ImageRemoveOptions{LookupManifest: true}); len(rmErrors) > 0 { + return "", fmt.Errorf("error removing manifest after push: %w", rmErrors[0]) + } + } + return digest, err } diff --git a/pkg/machine/qemu/machine.go b/pkg/machine/qemu/machine.go index 3b57455c4..7974c261e 100644 --- a/pkg/machine/qemu/machine.go +++ b/pkg/machine/qemu/machine.go @@ -670,11 +670,11 @@ func (v *MachineVM) Start(name string, _ machine.StartOptions) error { // because / is immutable, we have to monkey around with permissions // if we dont mount in /home or /mnt args := []string{"-q", "--"} - if !strings.HasPrefix(mount.Target, "/home") || !strings.HasPrefix(mount.Target, "/mnt") { + if !strings.HasPrefix(mount.Target, "/home") && !strings.HasPrefix(mount.Target, "/mnt") { args = append(args, "sudo", "chattr", "-i", "/", ";") } args = append(args, "sudo", "mkdir", "-p", mount.Target) - if !strings.HasPrefix(mount.Target, "/home") || !strings.HasPrefix(mount.Target, "/mnt") { + if !strings.HasPrefix(mount.Target, "/home") && !strings.HasPrefix(mount.Target, "/mnt") { args = append(args, ";", "sudo", "chattr", "+i", "/", ";") } err = v.SSH(name, machine.SSHOptions{Args: args}) diff --git a/pkg/util/filters.go b/pkg/util/filters.go index 08148806f..104b9c3c2 100644 --- a/pkg/util/filters.go +++ b/pkg/util/filters.go @@ -5,7 +5,6 @@ import ( "errors" "fmt" "net/http" - "path/filepath" "strings" "time" @@ -94,35 +93,3 @@ func PrepareFilters(r *http.Request) (*map[string][]string, error) { } return &filterMap, nil } - -func matchPattern(pattern string, value string) bool { - if strings.Contains(pattern, "*") { - filter := fmt.Sprintf("*%s*", pattern) - filter = strings.ReplaceAll(filter, string(filepath.Separator), "|") - newName := strings.ReplaceAll(value, string(filepath.Separator), "|") - match, _ := filepath.Match(filter, newName) - return match - } - return false -} - -// MatchLabelFilters matches labels and returns true if they are valid -func MatchLabelFilters(filterValues []string, labels map[string]string) bool { -outer: - for _, filterValue := range filterValues { - filterArray := strings.SplitN(filterValue, "=", 2) - filterKey := filterArray[0] - if len(filterArray) > 1 { - filterValue = filterArray[1] - } else { - filterValue = "" - } - for labelKey, labelValue := range labels { - if ((labelKey == filterKey) || matchPattern(filterKey, labelKey)) && (filterValue == "" || labelValue == filterValue) { - continue outer - } - } - return false - } - return true -} diff --git a/pkg/util/filters_test.go b/pkg/util/filters_test.go index 47259013e..8e45ea61c 100644 --- a/pkg/util/filters_test.go +++ b/pkg/util/filters_test.go @@ -2,6 +2,8 @@ package util import ( "testing" + + "github.com/containers/common/pkg/filters" ) func TestMatchLabelFilters(t *testing.T) { @@ -71,7 +73,7 @@ func TestMatchLabelFilters(t *testing.T) { for _, tt := range tests { tt := tt t.Run(tt.name, func(t *testing.T) { - if got := MatchLabelFilters(tt.args.filterValues, tt.args.labels); got != tt.want { + if got := filters.MatchLabelFilters(tt.args.filterValues, tt.args.labels); got != tt.want { t.Errorf("MatchLabelFilters() = %v, want %v", got, tt.want) } }) |