mirror of
				https://github.com/containers/podman.git
				synced 2025-10-31 18:08:51 +08:00 
			
		
		
		
	 7938f32c53
			
		
	
	7938f32c53
	
	
	
		
			
			This commit removes error message string from utils.Error in pkg/api. Param was not used inside a function for quite a long time [NO NEW TESTS NEEDED] Signed-off-by: Jakub Guzik <jguzik@redhat.com>
		
			
				
	
	
		
			214 lines
		
	
	
		
			6.1 KiB
		
	
	
	
		
			Go
		
	
	
	
	
	
			
		
		
	
	
			214 lines
		
	
	
		
			6.1 KiB
		
	
	
	
		
			Go
		
	
	
	
	
	
| package compat
 | |
| 
 | |
| import (
 | |
| 	"encoding/json"
 | |
| 	"fmt"
 | |
| 	"io/ioutil"
 | |
| 	"net/http"
 | |
| 	"strings"
 | |
| 
 | |
| 	"github.com/containers/image/v5/types"
 | |
| 	"github.com/containers/podman/v4/libpod"
 | |
| 	"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/domain/entities"
 | |
| 	"github.com/containers/podman/v4/pkg/domain/infra/abi"
 | |
| 	"github.com/containers/storage"
 | |
| 	"github.com/docker/docker/pkg/jsonmessage"
 | |
| 	"github.com/gorilla/schema"
 | |
| 	"github.com/pkg/errors"
 | |
| 	"github.com/sirupsen/logrus"
 | |
| )
 | |
| 
 | |
| // PushImage is the handler for the compat http endpoint for pushing images.
 | |
| func PushImage(w http.ResponseWriter, r *http.Request) {
 | |
| 	decoder := r.Context().Value(api.DecoderKey).(*schema.Decoder)
 | |
| 	runtime := r.Context().Value(api.RuntimeKey).(*libpod.Runtime)
 | |
| 
 | |
| 	digestFile, err := ioutil.TempFile("", "digest.txt")
 | |
| 	if err != nil {
 | |
| 		utils.Error(w, http.StatusInternalServerError, errors.Wrap(err, "unable to create tempfile"))
 | |
| 		return
 | |
| 	}
 | |
| 	defer digestFile.Close()
 | |
| 
 | |
| 	// Now use the ABI implementation to prevent us from having duplicate
 | |
| 	// code.
 | |
| 	imageEngine := abi.ImageEngine{Libpod: runtime}
 | |
| 
 | |
| 	query := struct {
 | |
| 		All         bool   `schema:"all"`
 | |
| 		Compress    bool   `schema:"compress"`
 | |
| 		Destination string `schema:"destination"`
 | |
| 		Format      string `schema:"format"`
 | |
| 		TLSVerify   bool   `schema:"tlsVerify"`
 | |
| 		Tag         string `schema:"tag"`
 | |
| 	}{
 | |
| 		// 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 {
 | |
| 		utils.Error(w, http.StatusBadRequest, errors.Wrapf(err, "failed to parse parameters for %s", r.URL.String()))
 | |
| 		return
 | |
| 	}
 | |
| 
 | |
| 	// Note that Docker's docs state "Image name or ID" to be in the path
 | |
| 	// parameter but it really must be a name as Docker does not allow for
 | |
| 	// pushing an image by ID.
 | |
| 	imageName := strings.TrimSuffix(utils.GetName(r), "/push") // GetName returns the entire path
 | |
| 	if query.Tag != "" {
 | |
| 		imageName += ":" + query.Tag
 | |
| 	}
 | |
| 
 | |
| 	if _, err := utils.ParseStorageReference(imageName); err != nil {
 | |
| 		utils.Error(w, http.StatusBadRequest, errors.Wrapf(err, "image source %q is not a containers-storage-transport reference", imageName))
 | |
| 		return
 | |
| 	}
 | |
| 
 | |
| 	possiblyNormalizedName, err := utils.NormalizeToDockerHub(r, imageName)
 | |
| 	if err != nil {
 | |
| 		utils.Error(w, http.StatusInternalServerError, errors.Wrap(err, "error normalizing image"))
 | |
| 		return
 | |
| 	}
 | |
| 	imageName = possiblyNormalizedName
 | |
| 	localImage, _, err := runtime.LibimageRuntime().LookupImage(possiblyNormalizedName, nil)
 | |
| 	if err != nil {
 | |
| 		utils.ImageNotFound(w, imageName, errors.Wrapf(err, "failed to find image %s", imageName))
 | |
| 		return
 | |
| 	}
 | |
| 	rawManifest, _, err := localImage.Manifest(r.Context())
 | |
| 	if err != nil {
 | |
| 		utils.Error(w, http.StatusBadRequest, err)
 | |
| 		return
 | |
| 	}
 | |
| 
 | |
| 	authconf, authfile, err := auth.GetCredentials(r)
 | |
| 	if err != nil {
 | |
| 		utils.Error(w, http.StatusBadRequest, err)
 | |
| 		return
 | |
| 	}
 | |
| 	defer auth.RemoveAuthfile(authfile)
 | |
| 	var username, password string
 | |
| 	if authconf != nil {
 | |
| 		username = authconf.Username
 | |
| 		password = authconf.Password
 | |
| 	}
 | |
| 	options := entities.ImagePushOptions{
 | |
| 		All:        query.All,
 | |
| 		Authfile:   authfile,
 | |
| 		Compress:   query.Compress,
 | |
| 		Format:     query.Format,
 | |
| 		Password:   password,
 | |
| 		Username:   username,
 | |
| 		DigestFile: digestFile.Name(),
 | |
| 		Quiet:      true,
 | |
| 		Progress:   make(chan types.ProgressProperties),
 | |
| 	}
 | |
| 	if _, found := r.URL.Query()["tlsVerify"]; found {
 | |
| 		options.SkipTLSVerify = types.NewOptionalBool(!query.TLSVerify)
 | |
| 	}
 | |
| 
 | |
| 	var destination string
 | |
| 	if _, found := r.URL.Query()["destination"]; found {
 | |
| 		destination = query.Destination
 | |
| 	} else {
 | |
| 		destination = imageName
 | |
| 	}
 | |
| 
 | |
| 	flush := func() {}
 | |
| 	if flusher, ok := w.(http.Flusher); ok {
 | |
| 		flush = flusher.Flush
 | |
| 	}
 | |
| 
 | |
| 	w.WriteHeader(http.StatusOK)
 | |
| 	w.Header().Set("Content-Type", "application/json")
 | |
| 	flush()
 | |
| 
 | |
| 	var report jsonmessage.JSONMessage
 | |
| 	enc := json.NewEncoder(w)
 | |
| 	enc.SetEscapeHTML(true)
 | |
| 
 | |
| 	report.Status = fmt.Sprintf("The push refers to repository [%s]", imageName)
 | |
| 	if err := enc.Encode(report); err != nil {
 | |
| 		logrus.Warnf("Failed to json encode error %q", err.Error())
 | |
| 	}
 | |
| 	flush()
 | |
| 
 | |
| 	pushErrChan := make(chan error)
 | |
| 	go func() {
 | |
| 		pushErrChan <- imageEngine.Push(r.Context(), imageName, destination, options)
 | |
| 	}()
 | |
| 
 | |
| loop: // break out of for/select infinite loop
 | |
| 	for {
 | |
| 		report = jsonmessage.JSONMessage{}
 | |
| 
 | |
| 		select {
 | |
| 		case e := <-options.Progress:
 | |
| 			switch e.Event {
 | |
| 			case types.ProgressEventNewArtifact:
 | |
| 				report.Status = "Preparing"
 | |
| 			case types.ProgressEventRead:
 | |
| 				report.Status = "Pushing"
 | |
| 				report.Progress = &jsonmessage.JSONProgress{
 | |
| 					Current: int64(e.Offset),
 | |
| 					Total:   e.Artifact.Size,
 | |
| 				}
 | |
| 			case types.ProgressEventSkipped:
 | |
| 				report.Status = "Layer already exists"
 | |
| 			case types.ProgressEventDone:
 | |
| 				report.Status = "Pushed"
 | |
| 			}
 | |
| 			report.ID = e.Artifact.Digest.Encoded()[0:12]
 | |
| 			if err := enc.Encode(report); err != nil {
 | |
| 				logrus.Warnf("Failed to json encode error %q", err.Error())
 | |
| 			}
 | |
| 			flush()
 | |
| 		case err := <-pushErrChan:
 | |
| 			if err != nil {
 | |
| 				var msg string
 | |
| 				if errors.Is(err, storage.ErrImageUnknown) {
 | |
| 					msg = "An image does not exist locally with the tag: " + imageName
 | |
| 				} else {
 | |
| 					msg = err.Error()
 | |
| 				}
 | |
| 				report.Error = &jsonmessage.JSONError{
 | |
| 					Message: msg,
 | |
| 				}
 | |
| 				report.ErrorMessage = msg
 | |
| 				if err := enc.Encode(report); err != nil {
 | |
| 					logrus.Warnf("Failed to json encode error %q", err.Error())
 | |
| 				}
 | |
| 				flush()
 | |
| 				break loop
 | |
| 			}
 | |
| 
 | |
| 			digestBytes, err := ioutil.ReadAll(digestFile)
 | |
| 			if err != nil {
 | |
| 				report.Error = &jsonmessage.JSONError{
 | |
| 					Message: err.Error(),
 | |
| 				}
 | |
| 				report.ErrorMessage = err.Error()
 | |
| 				if err := enc.Encode(report); err != nil {
 | |
| 					logrus.Warnf("Failed to json encode error %q", err.Error())
 | |
| 				}
 | |
| 				flush()
 | |
| 				break loop
 | |
| 			}
 | |
| 			tag := query.Tag
 | |
| 			if tag == "" {
 | |
| 				tag = "latest"
 | |
| 			}
 | |
| 			report.Status = fmt.Sprintf("%s: digest: %s size: %d", tag, string(digestBytes), len(rawManifest))
 | |
| 			if err := enc.Encode(report); err != nil {
 | |
| 				logrus.Warnf("Failed to json encode error %q", err.Error())
 | |
| 			}
 | |
| 
 | |
| 			flush()
 | |
| 			break loop // break out of for/select infinite loop
 | |
| 		}
 | |
| 	}
 | |
| }
 |