diff options
Diffstat (limited to 'pkg')
-rw-r--r-- | pkg/api/handlers/libpod/manifests.go | 70 | ||||
-rw-r--r-- | pkg/api/server/register_containers.go | 5 | ||||
-rw-r--r-- | pkg/api/server/register_manifest.go | 5 | ||||
-rw-r--r-- | pkg/bindings/manifests/manifests.go | 43 | ||||
-rw-r--r-- | pkg/domain/entities/manifest.go | 12 | ||||
-rw-r--r-- | pkg/domain/infra/abi/manifest.go | 3 | ||||
-rw-r--r-- | pkg/domain/infra/abi/terminal/terminal_unsupported.go | 25 | ||||
-rw-r--r-- | pkg/domain/infra/tunnel/manifest.go | 2 | ||||
-rw-r--r-- | pkg/machine/ignition_freebsd.go | 8 | ||||
-rw-r--r-- | pkg/machine/qemu/options_freebsd.go | 13 | ||||
-rw-r--r-- | pkg/machine/qemu/options_freebsd_amd64.go | 18 | ||||
-rw-r--r-- | pkg/rootless/rootless_linux.go | 26 | ||||
-rw-r--r-- | pkg/specgen/generate/config_unsupported.go | 29 | ||||
-rw-r--r-- | pkg/specgen/generate/oci.go | 28 | ||||
-rw-r--r-- | pkg/specgen/generate/rlimit_int64.go | 6 | ||||
-rw-r--r-- | pkg/specgen/generate/rlimit_uint64.go | 6 | ||||
-rw-r--r-- | pkg/specgen/specgen.go | 3 | ||||
-rw-r--r-- | pkg/util/utils_freebsd.go | 12 | ||||
-rw-r--r-- | pkg/util/utils_unsupported.go | 4 |
19 files changed, 279 insertions, 39 deletions
diff --git a/pkg/api/handlers/libpod/manifests.go b/pkg/api/handlers/libpod/manifests.go index 2d6223e4e..b0c93f3b9 100644 --- a/pkg/api/handlers/libpod/manifests.go +++ b/pkg/api/handlers/libpod/manifests.go @@ -19,12 +19,14 @@ import ( "github.com/containers/podman/v4/pkg/api/handlers/utils" api "github.com/containers/podman/v4/pkg/api/types" "github.com/containers/podman/v4/pkg/auth" + "github.com/containers/podman/v4/pkg/channel" "github.com/containers/podman/v4/pkg/domain/entities" "github.com/containers/podman/v4/pkg/domain/infra/abi" "github.com/containers/podman/v4/pkg/errorhandling" "github.com/gorilla/mux" "github.com/gorilla/schema" "github.com/opencontainers/go-digest" + "github.com/sirupsen/logrus" ) func ManifestCreate(w http.ResponseWriter, r *http.Request) { @@ -311,9 +313,13 @@ func ManifestPush(w http.ResponseWriter, r *http.Request) { Format string `schema:"format"` RemoveSignatures bool `schema:"removeSignatures"` TLSVerify bool `schema:"tlsVerify"` + Quiet bool `schema:"quiet"` }{ // Add defaults here once needed. TLSVerify: true, + // #15210: older versions did not sent *any* data, so we need + // to be quiet by default to remain backwards compatible + Quiet: true, } if err := decoder.Decode(&query, r.URL.Query()); err != nil { utils.Error(w, http.StatusBadRequest, @@ -344,6 +350,7 @@ func ManifestPush(w http.ResponseWriter, r *http.Request) { CompressionFormat: query.CompressionFormat, Format: query.Format, Password: password, + Quiet: true, RemoveSignatures: query.RemoveSignatures, Username: username, } @@ -356,12 +363,67 @@ func ManifestPush(w http.ResponseWriter, r *http.Request) { imageEngine := abi.ImageEngine{Libpod: runtime} source := utils.GetName(r) - digest, err := imageEngine.ManifestPush(context.Background(), source, destination, options) - if err != nil { - utils.Error(w, http.StatusBadRequest, fmt.Errorf("error pushing image %q: %w", destination, err)) + + // Let's keep thing simple when running in quiet mode and push directly. + if query.Quiet { + digest, err := imageEngine.ManifestPush(context.Background(), source, destination, options) + if err != nil { + utils.Error(w, http.StatusBadRequest, fmt.Errorf("error pushing image %q: %w", destination, err)) + return + } + utils.WriteResponse(w, http.StatusOK, entities.ManifestPushReport{ID: digest}) return } - utils.WriteResponse(w, http.StatusOK, entities.IDResponse{ID: digest}) + + writer := channel.NewWriter(make(chan []byte)) + defer writer.Close() + options.Writer = writer + + pushCtx, pushCancel := context.WithCancel(r.Context()) + var digest string + var pushError error + go func() { + defer pushCancel() + digest, pushError = imageEngine.ManifestPush(pushCtx, source, destination, options) + }() + + flush := func() { + if flusher, ok := w.(http.Flusher); ok { + flusher.Flush() + } + } + + w.WriteHeader(http.StatusOK) + w.Header().Set("Content-Type", "application/json") + flush() + + enc := json.NewEncoder(w) + enc.SetEscapeHTML(true) + for { + var report entities.ManifestPushReport + select { + case s := <-writer.Chan(): + report.Stream = string(s) + if err := enc.Encode(report); err != nil { + logrus.Warnf("Failed to encode json: %v", err) + } + flush() + case <-pushCtx.Done(): + if pushError != nil { + report.Error = pushError.Error() + } else { + report.ID = digest + } + if err := enc.Encode(report); err != nil { + logrus.Warnf("Failed to encode json: %v", err) + } + flush() + return + case <-r.Context().Done(): + // Client has closed connection + return + } + } } // ManifestModify efficiently updates the named manifest list diff --git a/pkg/api/server/register_containers.go b/pkg/api/server/register_containers.go index b319fc14a..8aba4ea05 100644 --- a/pkg/api/server/register_containers.go +++ b/pkg/api/server/register_containers.go @@ -11,9 +11,9 @@ import ( func (s *APIServer) registerContainersHandlers(r *mux.Router) error { // swagger:operation POST /containers/create compat ContainerCreate // --- - // summary: Create a container // tags: // - containers (compat) + // summary: Create a container // produces: // - application/json // parameters: @@ -678,9 +678,9 @@ func (s *APIServer) registerContainersHandlers(r *mux.Router) error { // swagger:operation POST /libpod/containers/create libpod ContainerCreateLibpod // --- - // summary: Create a container // tags: // - containers + // summary: Create a container // produces: // - application/json // parameters: @@ -689,6 +689,7 @@ func (s *APIServer) registerContainersHandlers(r *mux.Router) error { // description: attributes for creating a container // schema: // $ref: "#/definitions/SpecGenerator" + // required: true // responses: // 201: // $ref: "#/responses/containerCreateResponse" diff --git a/pkg/api/server/register_manifest.go b/pkg/api/server/register_manifest.go index 19b507047..c22479cf9 100644 --- a/pkg/api/server/register_manifest.go +++ b/pkg/api/server/register_manifest.go @@ -75,6 +75,11 @@ func (s *APIServer) registerManifestHandlers(r *mux.Router) error { // type: boolean // default: true // description: Require HTTPS and verify signatures when contacting registries. + // - in: query + // name: quiet + // description: "silences extra stream data on push" + // type: boolean + // default: true // responses: // 200: // schema: diff --git a/pkg/bindings/manifests/manifests.go b/pkg/bindings/manifests/manifests.go index 80153c4b4..49e4089f5 100644 --- a/pkg/bindings/manifests/manifests.go +++ b/pkg/bindings/manifests/manifests.go @@ -2,10 +2,13 @@ package manifests import ( "context" + "encoding/json" "errors" "fmt" + "io" "io/ioutil" "net/http" + "os" "strconv" "strings" @@ -142,7 +145,6 @@ func Delete(ctx context.Context, name string) (*entities.ManifestRemoveReport, e // the name will be used instead. If the optional all boolean is specified, all images specified // in the list will be pushed as well. func Push(ctx context.Context, name, destination string, options *images.PushOptions) (string, error) { - var idr entities.IDResponse if options == nil { options = new(images.PushOptions) } @@ -176,7 +178,44 @@ func Push(ctx context.Context, name, destination string, options *images.PushOpt } defer response.Body.Close() - return idr.ID, response.Process(&idr) + if !response.IsSuccess() { + return "", response.Process(err) + } + + // Historically push writes status to stderr + writer := io.Writer(os.Stderr) + if options.GetQuiet() { + writer = io.Discard + } else if progressWriter := options.GetProgressWriter(); progressWriter != nil { + writer = progressWriter + } + + dec := json.NewDecoder(response.Body) + for { + var report entities.ManifestPushReport + if err := dec.Decode(&report); err != nil { + return "", err + } + + select { + case <-response.Request.Context().Done(): + break + default: + // non-blocking select + } + + switch { + case report.ID != "": + return report.ID, nil + case report.Stream != "": + fmt.Fprint(writer, report.Stream) + case report.Error != "": + // There can only be one error. + return "", errors.New(report.Error) + default: + return "", fmt.Errorf("failed to parse push results stream, unexpected input: %v", report) + } + } } // Modify modifies the given manifest list using options and the optional list of images diff --git a/pkg/domain/entities/manifest.go b/pkg/domain/entities/manifest.go index e88c5f854..126b76c62 100644 --- a/pkg/domain/entities/manifest.go +++ b/pkg/domain/entities/manifest.go @@ -61,6 +61,18 @@ type ManifestModifyOptions struct { ManifestRemoveOptions } +// ManifestPushReport provides the model for the pushed manifest +// +// swagger:model +type ManifestPushReport struct { + // ID of the pushed manifest + ID string `json:"Id"` + // Stream used to provide push progress + Stream string `json:"stream,omitempty"` + // Error contains text of errors from pushing + Error string `json:"error,omitempty"` +} + // ManifestRemoveOptions provides the model for removing digests from a manifest // // swagger:model diff --git a/pkg/domain/infra/abi/manifest.go b/pkg/domain/infra/abi/manifest.go index 4b10d9b18..e0c11267e 100644 --- a/pkg/domain/infra/abi/manifest.go +++ b/pkg/domain/infra/abi/manifest.go @@ -321,6 +321,7 @@ func (ir *ImageEngine) ManifestPush(ctx context.Context, name, destination strin pushOptions.SignBySigstorePrivateKeyFile = opts.SignBySigstorePrivateKeyFile pushOptions.SignSigstorePrivateKeyPassphrase = opts.SignSigstorePrivateKeyPassphrase pushOptions.InsecureSkipTLSVerify = opts.SkipTLSVerify + pushOptions.Writer = opts.Writer compressionFormat := opts.CompressionFormat if compressionFormat == "" { @@ -341,7 +342,7 @@ func (ir *ImageEngine) ManifestPush(ctx context.Context, name, destination strin if opts.All { pushOptions.ImageListSelection = cp.CopyAllImages } - if !opts.Quiet { + if !opts.Quiet && pushOptions.Writer == nil { pushOptions.Writer = os.Stderr } diff --git a/pkg/domain/infra/abi/terminal/terminal_unsupported.go b/pkg/domain/infra/abi/terminal/terminal_unsupported.go new file mode 100644 index 000000000..8fe325736 --- /dev/null +++ b/pkg/domain/infra/abi/terminal/terminal_unsupported.go @@ -0,0 +1,25 @@ +//go:build !linux +// +build !linux + +package terminal + +import ( + "context" + "errors" + "os" + + "github.com/containers/podman/v4/libpod" + "github.com/containers/podman/v4/libpod/define" +) + +// ExecAttachCtr execs and attaches to a container +func ExecAttachCtr(ctx context.Context, ctr *libpod.Container, execConfig *libpod.ExecConfig, streams *define.AttachStreams) (int, error) { + return -1, errors.New("not implemented ExecAttachCtr") +} + +// StartAttachCtr starts and (if required) attaches to a container +// if you change the signature of this function from os.File to io.Writer, it will trigger a downstream +// error. we may need to just lint disable this one. +func StartAttachCtr(ctx context.Context, ctr *libpod.Container, stdout, stderr, stdin *os.File, detachKeys string, sigProxy bool, startContainer bool) error { //nolint: interfacer + return errors.New("not implemented StartAttachCtr") +} diff --git a/pkg/domain/infra/tunnel/manifest.go b/pkg/domain/infra/tunnel/manifest.go index 00ecb3b59..2a514861d 100644 --- a/pkg/domain/infra/tunnel/manifest.go +++ b/pkg/domain/infra/tunnel/manifest.go @@ -99,7 +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).WithAll(opts.All).WithFormat(opts.Format).WithCompressionFormat(opts.CompressionFormat) + options.WithUsername(opts.Username).WithPassword(opts.Password).WithAuthfile(opts.Authfile).WithRemoveSignatures(opts.RemoveSignatures).WithAll(opts.All).WithFormat(opts.Format).WithCompressionFormat(opts.CompressionFormat).WithQuiet(opts.Quiet).WithProgressWriter(opts.Writer) if s := opts.SkipTLSVerify; s != types.OptionalBoolUndefined { if s == types.OptionalBoolTrue { diff --git a/pkg/machine/ignition_freebsd.go b/pkg/machine/ignition_freebsd.go new file mode 100644 index 000000000..ddea40782 --- /dev/null +++ b/pkg/machine/ignition_freebsd.go @@ -0,0 +1,8 @@ +//go:build freebsd +// +build freebsd + +package machine + +func getLocalTimeZone() (string, error) { + return "", nil +} diff --git a/pkg/machine/qemu/options_freebsd.go b/pkg/machine/qemu/options_freebsd.go new file mode 100644 index 000000000..124358db8 --- /dev/null +++ b/pkg/machine/qemu/options_freebsd.go @@ -0,0 +1,13 @@ +package qemu + +import ( + "os" +) + +func getRuntimeDir() (string, error) { + tmpDir, ok := os.LookupEnv("TMPDIR") + if !ok { + tmpDir = "/tmp" + } + return tmpDir, nil +} diff --git a/pkg/machine/qemu/options_freebsd_amd64.go b/pkg/machine/qemu/options_freebsd_amd64.go new file mode 100644 index 000000000..ff8d10db1 --- /dev/null +++ b/pkg/machine/qemu/options_freebsd_amd64.go @@ -0,0 +1,18 @@ +package qemu + +var ( + QemuCommand = "qemu-system-x86_64" +) + +func (v *MachineVM) addArchOptions() []string { + opts := []string{"-machine", "q35,accel=hvf:tcg", "-cpu", "host"} + return opts +} + +func (v *MachineVM) prepare() error { + return nil +} + +func (v *MachineVM) archRemovalFiles() []string { + return []string{} +} diff --git a/pkg/rootless/rootless_linux.go b/pkg/rootless/rootless_linux.go index b0012b32b..8c4316dbb 100644 --- a/pkg/rootless/rootless_linux.go +++ b/pkg/rootless/rootless_linux.go @@ -251,20 +251,22 @@ func becomeRootInUserNS(pausePid, fileToRead string, fileOutput *os.File) (_ boo return false, 0, nil } - if mounts, err := pmount.GetMounts(); err == nil { - for _, m := range mounts { - if m.Mountpoint == "/" { - isShared := false - for _, o := range strings.Split(m.Optional, ",") { - if strings.HasPrefix(o, "shared:") { - isShared = true - break + if _, inContainer := os.LookupEnv("container"); !inContainer { + if mounts, err := pmount.GetMounts(); err == nil { + for _, m := range mounts { + if m.Mountpoint == "/" { + isShared := false + for _, o := range strings.Split(m.Optional, ",") { + if strings.HasPrefix(o, "shared:") { + isShared = true + break + } } + if !isShared { + logrus.Warningf("%q is not a shared mount, this could cause issues or missing mounts with rootless containers", m.Mountpoint) + } + break } - if !isShared { - logrus.Warningf("%q is not a shared mount, this could cause issues or missing mounts with rootless containers", m.Mountpoint) - } - break } } } diff --git a/pkg/specgen/generate/config_unsupported.go b/pkg/specgen/generate/config_unsupported.go new file mode 100644 index 000000000..a97ae0709 --- /dev/null +++ b/pkg/specgen/generate/config_unsupported.go @@ -0,0 +1,29 @@ +//go:build !linux +// +build !linux + +package generate + +import ( + "errors" + + "github.com/containers/common/libimage" + "github.com/containers/podman/v4/pkg/specgen" + spec "github.com/opencontainers/runtime-spec/specs-go" + "github.com/opencontainers/runtime-tools/generate" +) + +// DevicesFromPath computes a list of devices +func DevicesFromPath(g *generate.Generator, devicePath string) error { + return errors.New("unsupported DevicesFromPath") +} + +func BlockAccessToKernelFilesystems(privileged, pidModeIsHost bool, mask, unmask []string, g *generate.Generator) { +} + +func supportAmbientCapabilities() bool { + return false +} + +func getSeccompConfig(s *specgen.SpecGenerator, configSpec *spec.Spec, img *libimage.Image) (*spec.LinuxSeccomp, error) { + return nil, errors.New("not implemented getSeccompConfig") +} diff --git a/pkg/specgen/generate/oci.go b/pkg/specgen/generate/oci.go index f59fe1011..a531494c9 100644 --- a/pkg/specgen/generate/oci.go +++ b/pkg/specgen/generate/oci.go @@ -58,38 +58,38 @@ func addRlimits(s *specgen.SpecGenerator, g *generate.Generator) { // files and number of processes to the maximum they can be set to // (without overriding a sysctl) if !nofileSet { - max := define.RLimitDefaultValue - current := define.RLimitDefaultValue + max := rlimT(define.RLimitDefaultValue) + current := rlimT(define.RLimitDefaultValue) 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) } - if rlimit.Cur < current { - current = rlimit.Cur + if rlimT(rlimit.Cur) < current { + current = rlimT(rlimit.Cur) } - if rlimit.Max < max { - max = rlimit.Max + if rlimT(rlimit.Max) < max { + max = rlimT(rlimit.Max) } } - g.AddProcessRlimits("RLIMIT_NOFILE", max, current) + g.AddProcessRlimits("RLIMIT_NOFILE", uint64(max), uint64(current)) } if !nprocSet { - max := define.RLimitDefaultValue - current := define.RLimitDefaultValue + max := rlimT(define.RLimitDefaultValue) + current := rlimT(define.RLimitDefaultValue) 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) } - if rlimit.Cur < current { - current = rlimit.Cur + if rlimT(rlimit.Cur) < current { + current = rlimT(rlimit.Cur) } - if rlimit.Max < max { - max = rlimit.Max + if rlimT(rlimit.Max) < max { + max = rlimT(rlimit.Max) } } - g.AddProcessRlimits("RLIMIT_NPROC", max, current) + g.AddProcessRlimits("RLIMIT_NPROC", uint64(max), uint64(current)) } } diff --git a/pkg/specgen/generate/rlimit_int64.go b/pkg/specgen/generate/rlimit_int64.go new file mode 100644 index 000000000..b4cce3453 --- /dev/null +++ b/pkg/specgen/generate/rlimit_int64.go @@ -0,0 +1,6 @@ +//go:build freebsd +// +build freebsd + +package generate + +type rlimT int64 diff --git a/pkg/specgen/generate/rlimit_uint64.go b/pkg/specgen/generate/rlimit_uint64.go new file mode 100644 index 000000000..d85f8dd2c --- /dev/null +++ b/pkg/specgen/generate/rlimit_uint64.go @@ -0,0 +1,6 @@ +//go:build linux || darwin +// +build linux darwin + +package generate + +type rlimT uint64 diff --git a/pkg/specgen/specgen.go b/pkg/specgen/specgen.go index c31c3f035..b90f07ef8 100644 --- a/pkg/specgen/specgen.go +++ b/pkg/specgen/specgen.go @@ -13,7 +13,8 @@ import ( spec "github.com/opencontainers/runtime-spec/specs-go" ) -// LogConfig describes the logging characteristics for a container +// LogConfig describes the logging characteristics for a container +// swagger:model LogConfigLibpod type LogConfig struct { // LogDriver is the container's log driver. // Optional. diff --git a/pkg/util/utils_freebsd.go b/pkg/util/utils_freebsd.go new file mode 100644 index 000000000..17436ae81 --- /dev/null +++ b/pkg/util/utils_freebsd.go @@ -0,0 +1,12 @@ +//go:build freebsd +// +build freebsd + +package util + +import ( + "errors" +) + +func GetContainerPidInformationDescriptors() ([]string, error) { + return []string{}, errors.New("this function is not supported on freebsd") +} diff --git a/pkg/util/utils_unsupported.go b/pkg/util/utils_unsupported.go index 3a0f8646b..26fb7adf9 100644 --- a/pkg/util/utils_unsupported.go +++ b/pkg/util/utils_unsupported.go @@ -1,5 +1,5 @@ -//go:build darwin || windows -// +build darwin windows +//go:build darwin || windows || freebsd +// +build darwin windows freebsd package util |