diff options
Diffstat (limited to 'pkg/api/handlers/libpod')
-rw-r--r-- | pkg/api/handlers/libpod/containers.go | 41 | ||||
-rw-r--r-- | pkg/api/handlers/libpod/containers_create.go | 5 | ||||
-rw-r--r-- | pkg/api/handlers/libpod/generate.go | 62 | ||||
-rw-r--r-- | pkg/api/handlers/libpod/images.go | 6 | ||||
-rw-r--r-- | pkg/api/handlers/libpod/images_pull.go | 31 | ||||
-rw-r--r-- | pkg/api/handlers/libpod/images_push.go | 4 | ||||
-rw-r--r-- | pkg/api/handlers/libpod/kube.go | 8 | ||||
-rw-r--r-- | pkg/api/handlers/libpod/manifests.go | 77 | ||||
-rw-r--r-- | pkg/api/handlers/libpod/pods.go | 12 |
9 files changed, 177 insertions, 69 deletions
diff --git a/pkg/api/handlers/libpod/containers.go b/pkg/api/handlers/libpod/containers.go index 5d85d4009..a76e3d988 100644 --- a/pkg/api/handlers/libpod/containers.go +++ b/pkg/api/handlers/libpod/containers.go @@ -1,6 +1,7 @@ package libpod import ( + "encoding/json" "errors" "fmt" "io/ioutil" @@ -10,6 +11,7 @@ import ( "github.com/containers/podman/v4/libpod" "github.com/containers/podman/v4/libpod/define" + "github.com/containers/podman/v4/pkg/api/handlers" "github.com/containers/podman/v4/pkg/api/handlers/compat" "github.com/containers/podman/v4/pkg/api/handlers/utils" api "github.com/containers/podman/v4/pkg/api/types" @@ -17,6 +19,7 @@ import ( "github.com/containers/podman/v4/pkg/domain/infra/abi" "github.com/containers/podman/v4/pkg/util" "github.com/gorilla/schema" + "github.com/opencontainers/runtime-spec/specs-go" "github.com/sirupsen/logrus" ) @@ -263,16 +266,16 @@ func Checkpoint(w http.ResponseWriter, r *http.Request) { utils.InternalServerError(w, err) return } + if len(reports) != 1 { + utils.InternalServerError(w, fmt.Errorf("expected 1 restore report but got %d", len(reports))) + return + } + if reports[0].Err != nil { + utils.InternalServerError(w, reports[0].Err) + return + } if !query.Export { - if len(reports) != 1 { - utils.InternalServerError(w, fmt.Errorf("expected 1 restore report but got %d", len(reports))) - return - } - if reports[0].Err != nil { - utils.InternalServerError(w, reports[0].Err) - return - } utils.WriteResponse(w, http.StatusOK, reports[0]) return } @@ -391,6 +394,28 @@ func InitContainer(w http.ResponseWriter, r *http.Request) { utils.WriteResponse(w, http.StatusNoContent, "") } +func UpdateContainer(w http.ResponseWriter, r *http.Request) { + name := utils.GetName(r) + runtime := r.Context().Value(api.RuntimeKey).(*libpod.Runtime) + ctr, err := runtime.LookupContainer(name) + if err != nil { + utils.ContainerNotFound(w, name, err) + return + } + + options := &handlers.UpdateEntities{Resources: &specs.LinuxResources{}} + if err := json.NewDecoder(r.Body).Decode(&options.Resources); err != nil { + utils.Error(w, http.StatusInternalServerError, fmt.Errorf("decode(): %w", err)) + return + } + err = ctr.Update(options.Resources) + if err != nil { + utils.InternalServerError(w, err) + return + } + utils.WriteResponse(w, http.StatusCreated, ctr.ID()) +} + func ShouldRestart(w http.ResponseWriter, r *http.Request) { runtime := r.Context().Value(api.RuntimeKey).(*libpod.Runtime) // Now use the ABI implementation to prevent us from having duplicate diff --git a/pkg/api/handlers/libpod/containers_create.go b/pkg/api/handlers/libpod/containers_create.go index 1307c267a..429f45f91 100644 --- a/pkg/api/handlers/libpod/containers_create.go +++ b/pkg/api/handlers/libpod/containers_create.go @@ -1,7 +1,6 @@ package libpod import ( - "context" "encoding/json" "fmt" "net/http" @@ -63,12 +62,12 @@ func CreateContainer(w http.ResponseWriter, r *http.Request) { utils.InternalServerError(w, err) return } - rtSpec, spec, opts, err := generate.MakeContainer(context.Background(), runtime, &sg, false, nil) + rtSpec, spec, opts, err := generate.MakeContainer(r.Context(), runtime, &sg, false, nil) if err != nil { utils.InternalServerError(w, err) return } - ctr, err := generate.ExecuteCreate(context.Background(), runtime, rtSpec, spec, false, opts...) + ctr, err := generate.ExecuteCreate(r.Context(), runtime, rtSpec, spec, false, opts...) if err != nil { utils.InternalServerError(w, err) return diff --git a/pkg/api/handlers/libpod/generate.go b/pkg/api/handlers/libpod/generate.go index 48c4c59e1..9b38829ad 100644 --- a/pkg/api/handlers/libpod/generate.go +++ b/pkg/api/handlers/libpod/generate.go @@ -17,20 +17,21 @@ func GenerateSystemd(w http.ResponseWriter, r *http.Request) { runtime := r.Context().Value(api.RuntimeKey).(*libpod.Runtime) decoder := r.Context().Value(api.DecoderKey).(*schema.Decoder) query := struct { - Name bool `schema:"useName"` - New bool `schema:"new"` - NoHeader bool `schema:"noHeader"` - TemplateUnitFile bool `schema:"templateUnitFile"` - RestartPolicy *string `schema:"restartPolicy"` - RestartSec uint `schema:"restartSec"` - StopTimeout uint `schema:"stopTimeout"` - StartTimeout uint `schema:"startTimeout"` - ContainerPrefix *string `schema:"containerPrefix"` - PodPrefix *string `schema:"podPrefix"` - Separator *string `schema:"separator"` - Wants []string `schema:"wants"` - After []string `schema:"after"` - Requires []string `schema:"requires"` + Name bool `schema:"useName"` + New bool `schema:"new"` + NoHeader bool `schema:"noHeader"` + TemplateUnitFile bool `schema:"templateUnitFile"` + RestartPolicy *string `schema:"restartPolicy"` + RestartSec uint `schema:"restartSec"` + StopTimeout uint `schema:"stopTimeout"` + StartTimeout uint `schema:"startTimeout"` + ContainerPrefix *string `schema:"containerPrefix"` + PodPrefix *string `schema:"podPrefix"` + Separator *string `schema:"separator"` + Wants []string `schema:"wants"` + After []string `schema:"after"` + Requires []string `schema:"requires"` + AdditionalEnvVariables []string `schema:"additionalEnvVariables"` }{ StartTimeout: 0, StopTimeout: util.DefaultContainerConfig().Engine.StopTimeout, @@ -58,25 +59,26 @@ func GenerateSystemd(w http.ResponseWriter, r *http.Request) { containerEngine := abi.ContainerEngine{Libpod: runtime} options := entities.GenerateSystemdOptions{ - Name: query.Name, - New: query.New, - NoHeader: query.NoHeader, - TemplateUnitFile: query.TemplateUnitFile, - RestartPolicy: query.RestartPolicy, - StartTimeout: &query.StartTimeout, - StopTimeout: &query.StopTimeout, - ContainerPrefix: ContainerPrefix, - PodPrefix: PodPrefix, - Separator: Separator, - RestartSec: &query.RestartSec, - Wants: query.Wants, - After: query.After, - Requires: query.Requires, + Name: query.Name, + New: query.New, + NoHeader: query.NoHeader, + TemplateUnitFile: query.TemplateUnitFile, + RestartPolicy: query.RestartPolicy, + StartTimeout: &query.StartTimeout, + StopTimeout: &query.StopTimeout, + ContainerPrefix: ContainerPrefix, + PodPrefix: PodPrefix, + Separator: Separator, + RestartSec: &query.RestartSec, + Wants: query.Wants, + After: query.After, + Requires: query.Requires, + AdditionalEnvVariables: query.AdditionalEnvVariables, } report, err := containerEngine.GenerateSystemd(r.Context(), utils.GetName(r), options) if err != nil { - utils.Error(w, http.StatusInternalServerError, fmt.Errorf("error generating systemd units: %w", err)) + utils.Error(w, http.StatusInternalServerError, fmt.Errorf("generating systemd units: %w", err)) return } @@ -102,7 +104,7 @@ func GenerateKube(w http.ResponseWriter, r *http.Request) { options := entities.GenerateKubeOptions{Service: query.Service} report, err := containerEngine.GenerateKube(r.Context(), query.Names, options) if err != nil { - utils.Error(w, http.StatusInternalServerError, fmt.Errorf("error generating YAML: %w", err)) + utils.Error(w, http.StatusInternalServerError, fmt.Errorf("generating YAML: %w", err)) return } diff --git a/pkg/api/handlers/libpod/images.go b/pkg/api/handlers/libpod/images.go index 67943ecf1..82c1971cd 100644 --- a/pkg/api/handlers/libpod/images.go +++ b/pkg/api/handlers/libpod/images.go @@ -12,6 +12,7 @@ import ( "github.com/containers/buildah" "github.com/containers/common/libimage" + "github.com/containers/common/pkg/ssh" "github.com/containers/image/v5/manifest" "github.com/containers/podman/v4/libpod" "github.com/containers/podman/v4/libpod/define" @@ -547,6 +548,7 @@ func ImagesBatchRemove(w http.ResponseWriter, r *http.Request) { Ignore bool `schema:"ignore"` LookupManifest bool `schema:"lookupManifest"` Images []string `schema:"images"` + NoPrune bool `schema:"noprune"` }{} if err := decoder.Decode(&query, r.URL.Query()); err != nil { @@ -554,7 +556,7 @@ func ImagesBatchRemove(w http.ResponseWriter, r *http.Request) { return } - opts := entities.ImageRemoveOptions{All: query.All, Force: query.Force, Ignore: query.Ignore, LookupManifest: query.LookupManifest} + opts := entities.ImageRemoveOptions{All: query.All, Force: query.Force, Ignore: query.Ignore, LookupManifest: query.LookupManifest, NoPrune: query.NoPrune} imageEngine := abi.ImageEngine{Libpod: runtime} rmReport, rmErrors := imageEngine.Remove(r.Context(), query.Images, opts) strErrs := errorhandling.ErrorsToStrings(rmErrors) @@ -617,7 +619,7 @@ func ImageScp(w http.ResponseWriter, r *http.Request) { sourceArg := utils.GetName(r) - rep, source, dest, _, err := domainUtils.ExecuteTransfer(sourceArg, query.Destination, []string{}, query.Quiet) + rep, source, dest, _, err := domainUtils.ExecuteTransfer(sourceArg, query.Destination, []string{}, query.Quiet, ssh.GolangMode) if err != nil { utils.Error(w, http.StatusInternalServerError, err) return diff --git a/pkg/api/handlers/libpod/images_pull.go b/pkg/api/handlers/libpod/images_pull.go index 7e24ae5ac..57b2e3a78 100644 --- a/pkg/api/handlers/libpod/images_pull.go +++ b/pkg/api/handlers/libpod/images_pull.go @@ -82,17 +82,32 @@ func ImagesPull(w http.ResponseWriter, r *http.Request) { pullOptions.IdentityToken = authConf.IdentityToken } - writer := channel.NewWriter(make(chan []byte)) - defer writer.Close() - - pullOptions.Writer = writer - pullPolicy, err := config.ParsePullPolicy(query.PullPolicy) if err != nil { utils.Error(w, http.StatusBadRequest, err) return } + // Let's keep thing simple when running in quiet mode and pull directly. + if query.Quiet { + images, err := runtime.LibimageRuntime().Pull(r.Context(), query.Reference, pullPolicy, pullOptions) + var report entities.ImagePullReport + if err != nil { + report.Error = err.Error() + } + for _, image := range images { + report.Images = append(report.Images, image.ID()) + // Pull last ID from list and publish in 'id' stanza. This maintains previous API contract + report.ID = image.ID() + } + utils.WriteResponse(w, http.StatusOK, report) + return + } + + writer := channel.NewWriter(make(chan []byte)) + defer writer.Close() + pullOptions.Writer = writer + var pulledImages []*libimage.Image var pullError error runCtx, cancel := context.WithCancel(r.Context()) @@ -118,10 +133,8 @@ func ImagesPull(w http.ResponseWriter, r *http.Request) { select { case s := <-writer.Chan(): report.Stream = string(s) - if !query.Quiet { - if err := enc.Encode(report); err != nil { - logrus.Warnf("Failed to encode json: %v", err) - } + if err := enc.Encode(report); err != nil { + logrus.Warnf("Failed to encode json: %v", err) } flush() case <-runCtx.Done(): diff --git a/pkg/api/handlers/libpod/images_push.go b/pkg/api/handlers/libpod/images_push.go index e931fd2f9..4102f23de 100644 --- a/pkg/api/handlers/libpod/images_push.go +++ b/pkg/api/handlers/libpod/images_push.go @@ -90,8 +90,8 @@ func PushImage(w http.ResponseWriter, r *http.Request) { // Let's keep thing simple when running in quiet mode and push directly. if query.Quiet { - if err := imageEngine.Push(context.Background(), source, destination, options); err != nil { - utils.Error(w, http.StatusBadRequest, fmt.Errorf("error pushing image %q: %w", destination, err)) + if err := imageEngine.Push(r.Context(), source, destination, options); err != nil { + utils.Error(w, http.StatusBadRequest, fmt.Errorf("pushing image %q: %w", destination, err)) return } utils.WriteResponse(w, http.StatusOK, "") diff --git a/pkg/api/handlers/libpod/kube.go b/pkg/api/handlers/libpod/kube.go index 6cad58795..2a61d1723 100644 --- a/pkg/api/handlers/libpod/kube.go +++ b/pkg/api/handlers/libpod/kube.go @@ -103,7 +103,7 @@ func KubePlay(w http.ResponseWriter, r *http.Request) { report, err := containerEngine.PlayKube(r.Context(), r.Body, options) _ = r.Body.Close() if err != nil { - utils.Error(w, http.StatusInternalServerError, fmt.Errorf("error playing YAML file: %w", err)) + utils.Error(w, http.StatusInternalServerError, fmt.Errorf("playing YAML file: %w", err)) return } utils.WriteResponse(w, http.StatusOK, report) @@ -116,8 +116,12 @@ func KubePlayDown(w http.ResponseWriter, r *http.Request) { report, err := containerEngine.PlayKubeDown(r.Context(), r.Body, *options) _ = r.Body.Close() if err != nil { - utils.Error(w, http.StatusInternalServerError, fmt.Errorf("error tearing down YAML file: %w", err)) + utils.Error(w, http.StatusInternalServerError, fmt.Errorf("tearing down YAML file: %w", err)) return } utils.WriteResponse(w, http.StatusOK, report) } + +func KubeGenerate(w http.ResponseWriter, r *http.Request) { + GenerateKube(w, r) +} diff --git a/pkg/api/handlers/libpod/manifests.go b/pkg/api/handlers/libpod/manifests.go index 2d6223e4e..d5af72a61 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) { @@ -34,6 +36,7 @@ func ManifestCreate(w http.ResponseWriter, r *http.Request) { Name string `schema:"name"` Images []string `schema:"images"` All bool `schema:"all"` + Amend bool `schema:"amend"` }{ // Add defaults here once needed. } @@ -68,7 +71,7 @@ func ManifestCreate(w http.ResponseWriter, r *http.Request) { imageEngine := abi.ImageEngine{Libpod: runtime} - createOptions := entities.ManifestCreateOptions{All: query.All} + createOptions := entities.ManifestCreateOptions{All: query.All, Amend: query.Amend} manID, err := imageEngine.ManifestCreate(r.Context(), query.Name, query.Images, createOptions) if err != nil { utils.InternalServerError(w, err) @@ -290,9 +293,9 @@ func ManifestPushV3(w http.ResponseWriter, r *http.Request) { options.SkipTLSVerify = types.NewOptionalBool(!query.TLSVerify) } imageEngine := abi.ImageEngine{Libpod: runtime} - digest, err := imageEngine.ManifestPush(context.Background(), source, query.Destination, options) + digest, err := imageEngine.ManifestPush(r.Context(), source, query.Destination, options) if err != nil { - utils.Error(w, http.StatusBadRequest, fmt.Errorf("error pushing image %q: %w", query.Destination, err)) + utils.Error(w, http.StatusBadRequest, fmt.Errorf("pushing image %q: %w", query.Destination, err)) return } utils.WriteResponse(w, http.StatusOK, entities.IDResponse{ID: digest}) @@ -311,9 +314,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 +351,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 +364,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(r.Context(), source, destination, options) + if err != nil { + utils.Error(w, http.StatusBadRequest, fmt.Errorf("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/handlers/libpod/pods.go b/pkg/api/handlers/libpod/pods.go index 8b1d456ec..c39b9ee2f 100644 --- a/pkg/api/handlers/libpod/pods.go +++ b/pkg/api/handlers/libpod/pods.go @@ -51,7 +51,7 @@ func PodCreate(w http.ResponseWriter, r *http.Request) { } err = specgenutil.FillOutSpecGen(psg.InfraContainerSpec, &infraOptions, []string{}) // necessary for default values in many cases (userns, idmappings) if err != nil { - utils.Error(w, http.StatusInternalServerError, fmt.Errorf("error filling out specgen: %w", err)) + utils.Error(w, http.StatusInternalServerError, fmt.Errorf("filling out specgen: %w", err)) return } out, err := json.Marshal(psg) // marshal our spec so the matching options can be unmarshaled into infra @@ -178,7 +178,7 @@ func PodStop(w http.ResponseWriter, r *http.Request) { report := entities.PodStopReport{Id: pod.ID()} for id, err := range responses { - report.Errs = append(report.Errs, fmt.Errorf("error stopping container %s: %w", id, err)) + report.Errs = append(report.Errs, fmt.Errorf("stopping container %s: %w", id, err)) } code := http.StatusOK @@ -214,7 +214,7 @@ func PodStart(w http.ResponseWriter, r *http.Request) { report := entities.PodStartReport{Id: pod.ID()} for id, err := range responses { - report.Errs = append(report.Errs, fmt.Errorf("%v: %w", "error starting container "+id, err)) + report.Errs = append(report.Errs, fmt.Errorf("%v: %w", "starting container "+id, err)) } code := http.StatusOK @@ -270,7 +270,7 @@ func PodRestart(w http.ResponseWriter, r *http.Request) { report := entities.PodRestartReport{Id: pod.ID()} for id, err := range responses { - report.Errs = append(report.Errs, fmt.Errorf("error restarting container %s: %w", id, err)) + report.Errs = append(report.Errs, fmt.Errorf("restarting container %s: %w", id, err)) } code := http.StatusOK @@ -321,7 +321,7 @@ func PodPause(w http.ResponseWriter, r *http.Request) { report := entities.PodPauseReport{Id: pod.ID()} for id, v := range responses { - report.Errs = append(report.Errs, fmt.Errorf("error pausing container %s: %w", id, v)) + report.Errs = append(report.Errs, fmt.Errorf("pausing container %s: %w", id, v)) } code := http.StatusOK @@ -347,7 +347,7 @@ func PodUnpause(w http.ResponseWriter, r *http.Request) { report := entities.PodUnpauseReport{Id: pod.ID()} for id, v := range responses { - report.Errs = append(report.Errs, fmt.Errorf("error unpausing container %s: %w", id, v)) + report.Errs = append(report.Errs, fmt.Errorf("unpausing container %s: %w", id, v)) } code := http.StatusOK |