mirror of
https://github.com/containers/podman.git
synced 2025-05-21 09:05:56 +08:00

The new golangci-lint version 1.60.1 has problems with typecheck when linting remote files. We have certain pakcages that should never be inlcuded in remote but the typecheck tries to compile all of them but this never works and it seems to ignore the exclude files we gave it. To fix this the proper way is to mark all packages we only use locally with !remote tags. This is a bit ugly but more correct. I also moved the DecodeChanges() code around as it is called from the client so the handles package which should only be remote doesn't really fit anyway. Signed-off-by: Paul Holzinger <pholzing@redhat.com>
190 lines
5.3 KiB
Go
190 lines
5.3 KiB
Go
//go:build !remote
|
|
|
|
package libpod
|
|
|
|
import (
|
|
"context"
|
|
"encoding/json"
|
|
"errors"
|
|
"fmt"
|
|
"net/http"
|
|
"time"
|
|
|
|
"github.com/containers/common/libimage"
|
|
"github.com/containers/common/pkg/config"
|
|
"github.com/containers/image/v5/types"
|
|
"github.com/containers/podman/v5/libpod"
|
|
"github.com/containers/podman/v5/pkg/api/handlers/utils"
|
|
api "github.com/containers/podman/v5/pkg/api/types"
|
|
"github.com/containers/podman/v5/pkg/auth"
|
|
"github.com/containers/podman/v5/pkg/channel"
|
|
"github.com/containers/podman/v5/pkg/domain/entities"
|
|
"github.com/gorilla/schema"
|
|
"github.com/sirupsen/logrus"
|
|
)
|
|
|
|
// ImagesPull is the v2 libpod endpoint for pulling images. Note that the
|
|
// mandatory `reference` must be a reference to a registry (i.e., of docker
|
|
// transport or be normalized to one). Other transports are rejected as they
|
|
// do not make sense in a remote context.
|
|
func ImagesPull(w http.ResponseWriter, r *http.Request) {
|
|
runtime := r.Context().Value(api.RuntimeKey).(*libpod.Runtime)
|
|
decoder := r.Context().Value(api.DecoderKey).(*schema.Decoder)
|
|
query := struct {
|
|
AllTags bool `schema:"allTags"`
|
|
CompatMode bool `schema:"compatMode"`
|
|
PullPolicy string `schema:"policy"`
|
|
Quiet bool `schema:"quiet"`
|
|
Reference string `schema:"reference"`
|
|
Retry uint `schema:"retry"`
|
|
RetryDelay string `schema:"retrydelay"`
|
|
TLSVerify bool `schema:"tlsVerify"`
|
|
// Platform fields below:
|
|
Arch string `schema:"Arch"`
|
|
OS string `schema:"OS"`
|
|
Variant string `schema:"Variant"`
|
|
}{
|
|
TLSVerify: true,
|
|
PullPolicy: "always",
|
|
}
|
|
|
|
if err := decoder.Decode(&query, r.URL.Query()); err != nil {
|
|
utils.Error(w, http.StatusBadRequest, fmt.Errorf("failed to parse parameters for %s: %w", r.URL.String(), err))
|
|
return
|
|
}
|
|
|
|
if query.Quiet && query.CompatMode {
|
|
utils.InternalServerError(w, errors.New("'quiet' and 'compatMode' cannot be used simultaneously"))
|
|
return
|
|
}
|
|
|
|
if len(query.Reference) == 0 {
|
|
utils.InternalServerError(w, errors.New("reference parameter cannot be empty"))
|
|
return
|
|
}
|
|
|
|
// Make sure that the reference has no transport or the docker one.
|
|
if err := utils.IsRegistryReference(query.Reference); err != nil {
|
|
utils.Error(w, http.StatusBadRequest, err)
|
|
return
|
|
}
|
|
|
|
pullOptions := &libimage.PullOptions{}
|
|
pullOptions.AllTags = query.AllTags
|
|
pullOptions.Architecture = query.Arch
|
|
pullOptions.OS = query.OS
|
|
pullOptions.Variant = query.Variant
|
|
|
|
if _, found := r.URL.Query()["tlsVerify"]; found {
|
|
pullOptions.InsecureSkipTLSVerify = types.NewOptionalBool(!query.TLSVerify)
|
|
}
|
|
|
|
// Do the auth dance.
|
|
authConf, authfile, err := auth.GetCredentials(r)
|
|
if err != nil {
|
|
utils.Error(w, http.StatusBadRequest, err)
|
|
return
|
|
}
|
|
defer auth.RemoveAuthfile(authfile)
|
|
|
|
pullOptions.AuthFilePath = authfile
|
|
if authConf != nil {
|
|
pullOptions.Username = authConf.Username
|
|
pullOptions.Password = authConf.Password
|
|
pullOptions.IdentityToken = authConf.IdentityToken
|
|
}
|
|
|
|
pullPolicy, err := config.ParsePullPolicy(query.PullPolicy)
|
|
if err != nil {
|
|
utils.Error(w, http.StatusBadRequest, err)
|
|
return
|
|
}
|
|
|
|
if _, found := r.URL.Query()["retry"]; found {
|
|
pullOptions.MaxRetries = &query.Retry
|
|
}
|
|
|
|
if _, found := r.URL.Query()["retrydelay"]; found {
|
|
duration, err := time.ParseDuration(query.RetryDelay)
|
|
if err != nil {
|
|
utils.Error(w, http.StatusBadRequest, err)
|
|
return
|
|
}
|
|
pullOptions.RetryDelay = &duration
|
|
}
|
|
|
|
// 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
|
|
}
|
|
|
|
if query.CompatMode {
|
|
utils.CompatPull(r.Context(), w, runtime, query.Reference, pullPolicy, pullOptions)
|
|
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())
|
|
go func() {
|
|
defer cancel()
|
|
pulledImages, pullError = runtime.LibimageRuntime().Pull(runCtx, query.Reference, pullPolicy, pullOptions)
|
|
}()
|
|
|
|
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.ImagePullReport
|
|
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 <-runCtx.Done():
|
|
for _, image := range pulledImages {
|
|
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()
|
|
}
|
|
if pullError != nil {
|
|
report.Error = pullError.Error()
|
|
}
|
|
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
|
|
}
|
|
}
|
|
}
|