github.com/containers/libpod@v1.9.4-0.20220419124438-4284fd425507/pkg/api/handlers/libpod/images.go (about)

     1  package libpod
     2  
     3  import (
     4  	"context"
     5  	"fmt"
     6  	"io"
     7  	"io/ioutil"
     8  	"net/http"
     9  	"os"
    10  	"strconv"
    11  	"strings"
    12  
    13  	"github.com/containers/buildah"
    14  	"github.com/containers/image/v5/docker"
    15  	"github.com/containers/image/v5/docker/reference"
    16  	"github.com/containers/image/v5/manifest"
    17  	"github.com/containers/image/v5/types"
    18  	"github.com/containers/libpod/libpod"
    19  	"github.com/containers/libpod/libpod/define"
    20  	"github.com/containers/libpod/libpod/image"
    21  	image2 "github.com/containers/libpod/libpod/image"
    22  	"github.com/containers/libpod/pkg/api/handlers"
    23  	"github.com/containers/libpod/pkg/api/handlers/utils"
    24  	"github.com/containers/libpod/pkg/domain/entities"
    25  	"github.com/containers/libpod/pkg/util"
    26  	utils2 "github.com/containers/libpod/utils"
    27  	"github.com/gorilla/schema"
    28  	"github.com/pkg/errors"
    29  )
    30  
    31  // Commit
    32  // author string
    33  // "container"
    34  // repo string
    35  // tag string
    36  // message
    37  // pause bool
    38  // changes []string
    39  
    40  // create
    41  
    42  func ImageExists(w http.ResponseWriter, r *http.Request) {
    43  	runtime := r.Context().Value("runtime").(*libpod.Runtime)
    44  	name := utils.GetName(r)
    45  
    46  	_, err := runtime.ImageRuntime().NewFromLocal(name)
    47  	if err != nil {
    48  		utils.Error(w, "Something went wrong.", http.StatusNotFound, errors.Wrapf(err, "Failed to find image %s", name))
    49  		return
    50  	}
    51  	utils.WriteResponse(w, http.StatusNoContent, "")
    52  }
    53  
    54  func ImageTree(w http.ResponseWriter, r *http.Request) {
    55  	runtime := r.Context().Value("runtime").(*libpod.Runtime)
    56  	name := utils.GetName(r)
    57  
    58  	img, err := runtime.ImageRuntime().NewFromLocal(name)
    59  	if err != nil {
    60  		utils.Error(w, "Something went wrong.", http.StatusNotFound, errors.Wrapf(err, "Failed to find image %s", name))
    61  		return
    62  	}
    63  
    64  	decoder := r.Context().Value("decoder").(*schema.Decoder)
    65  	query := struct {
    66  		WhatRequires bool `schema:"whatrequires"`
    67  	}{
    68  		WhatRequires: false,
    69  	}
    70  	if err := decoder.Decode(&query, r.URL.Query()); err != nil {
    71  		utils.Error(w, http.StatusText(http.StatusBadRequest), http.StatusBadRequest,
    72  			errors.Wrapf(err, "failed to parse parameters for %s", r.URL.String()))
    73  		return
    74  	}
    75  
    76  	tree, err := img.GenerateTree(query.WhatRequires)
    77  	if err != nil {
    78  		utils.Error(w, "Server error", http.StatusInternalServerError, errors.Wrapf(err, "failed to generate image tree for %s", name))
    79  		return
    80  	}
    81  
    82  	utils.WriteResponse(w, http.StatusOK, tree)
    83  }
    84  
    85  func GetImage(w http.ResponseWriter, r *http.Request) {
    86  	name := utils.GetName(r)
    87  	newImage, err := utils.GetImage(r, name)
    88  	if err != nil {
    89  		utils.Error(w, "Something went wrong.", http.StatusNotFound, errors.Wrapf(err, "Failed to find image %s", name))
    90  		return
    91  	}
    92  	inspect, err := newImage.Inspect(r.Context())
    93  	if err != nil {
    94  		utils.Error(w, "Server error", http.StatusInternalServerError, errors.Wrapf(err, "failed in inspect image %s", inspect.ID))
    95  		return
    96  	}
    97  	utils.WriteResponse(w, http.StatusOK, inspect)
    98  }
    99  
   100  func GetImages(w http.ResponseWriter, r *http.Request) {
   101  	images, err := utils.GetImages(w, r)
   102  	if err != nil {
   103  		utils.Error(w, "Something went wrong.", http.StatusInternalServerError, errors.Wrap(err, "Failed get images"))
   104  		return
   105  	}
   106  	var summaries = make([]*entities.ImageSummary, len(images))
   107  	for j, img := range images {
   108  		is, err := handlers.ImageToImageSummary(img)
   109  		if err != nil {
   110  			utils.Error(w, "Something went wrong.", http.StatusInternalServerError, errors.Wrap(err, "Failed transform image summaries"))
   111  			return
   112  		}
   113  		// libpod has additional fields that we need to populate.
   114  		is.Created = img.Created().Unix()
   115  		is.ReadOnly = img.IsReadOnly()
   116  		summaries[j] = is
   117  	}
   118  	utils.WriteResponse(w, http.StatusOK, summaries)
   119  }
   120  
   121  func PruneImages(w http.ResponseWriter, r *http.Request) {
   122  	var (
   123  		err error
   124  	)
   125  	runtime := r.Context().Value("runtime").(*libpod.Runtime)
   126  	decoder := r.Context().Value("decoder").(*schema.Decoder)
   127  	query := struct {
   128  		All     bool                `schema:"all"`
   129  		Filters map[string][]string `schema:"filters"`
   130  	}{
   131  		// override any golang type defaults
   132  	}
   133  
   134  	if err := decoder.Decode(&query, r.URL.Query()); err != nil {
   135  		utils.Error(w, http.StatusText(http.StatusBadRequest), http.StatusBadRequest,
   136  			errors.Wrapf(err, "Failed to parse parameters for %s", r.URL.String()))
   137  		return
   138  	}
   139  
   140  	var libpodFilters = []string{}
   141  	if _, found := r.URL.Query()["filters"]; found {
   142  		dangling := query.Filters["all"]
   143  		if len(dangling) > 0 {
   144  			query.All, err = strconv.ParseBool(query.Filters["all"][0])
   145  			if err != nil {
   146  				utils.InternalServerError(w, err)
   147  				return
   148  			}
   149  		}
   150  		// dangling is special and not implemented in the libpod side of things
   151  		delete(query.Filters, "dangling")
   152  		for k, v := range query.Filters {
   153  			libpodFilters = append(libpodFilters, fmt.Sprintf("%s=%s", k, v[0]))
   154  		}
   155  	}
   156  
   157  	cids, err := runtime.ImageRuntime().PruneImages(r.Context(), query.All, libpodFilters)
   158  	if err != nil {
   159  		utils.Error(w, "Something went wrong.", http.StatusInternalServerError, err)
   160  		return
   161  	}
   162  	utils.WriteResponse(w, http.StatusOK, cids)
   163  }
   164  
   165  func ExportImage(w http.ResponseWriter, r *http.Request) {
   166  	var (
   167  		output string
   168  	)
   169  	runtime := r.Context().Value("runtime").(*libpod.Runtime)
   170  	decoder := r.Context().Value("decoder").(*schema.Decoder)
   171  	query := struct {
   172  		Compress bool   `schema:"compress"`
   173  		Format   string `schema:"format"`
   174  	}{
   175  		Format: define.OCIArchive,
   176  	}
   177  
   178  	if err := decoder.Decode(&query, r.URL.Query()); err != nil {
   179  		utils.Error(w, http.StatusText(http.StatusBadRequest), http.StatusBadRequest,
   180  			errors.Wrapf(err, "Failed to parse parameters for %s", r.URL.String()))
   181  		return
   182  	}
   183  	name := utils.GetName(r)
   184  	newImage, err := runtime.ImageRuntime().NewFromLocal(name)
   185  	if err != nil {
   186  		utils.ImageNotFound(w, name, err)
   187  		return
   188  	}
   189  	switch query.Format {
   190  	case define.OCIArchive, define.V2s2Archive:
   191  		tmpfile, err := ioutil.TempFile("", "api.tar")
   192  		if err != nil {
   193  			utils.Error(w, "unable to create tmpfile", http.StatusInternalServerError, errors.Wrap(err, "unable to create tempfile"))
   194  			return
   195  		}
   196  		output = tmpfile.Name()
   197  		if err := tmpfile.Close(); err != nil {
   198  			utils.Error(w, "unable to close tmpfile", http.StatusInternalServerError, errors.Wrap(err, "unable to close tempfile"))
   199  			return
   200  		}
   201  	case define.OCIManifestDir, define.V2s2ManifestDir:
   202  		tmpdir, err := ioutil.TempDir("", "save")
   203  		if err != nil {
   204  			utils.Error(w, "unable to create tmpdir", http.StatusInternalServerError, errors.Wrap(err, "unable to create tempdir"))
   205  			return
   206  		}
   207  		output = tmpdir
   208  	default:
   209  		utils.Error(w, "unknown format", http.StatusInternalServerError, errors.Errorf("unknown format %q", query.Format))
   210  		return
   211  	}
   212  	if err := newImage.Save(r.Context(), name, query.Format, output, []string{}, false, query.Compress); err != nil {
   213  		utils.Error(w, http.StatusText(http.StatusBadRequest), http.StatusBadRequest, err)
   214  		return
   215  	}
   216  	defer os.RemoveAll(output)
   217  	// if dir format, we need to tar it
   218  	if query.Format == "oci-dir" || query.Format == "docker-dir" {
   219  		rdr, err := utils2.Tar(output)
   220  		if err != nil {
   221  			utils.InternalServerError(w, err)
   222  			return
   223  		}
   224  		defer rdr.Close()
   225  		utils.WriteResponse(w, http.StatusOK, rdr)
   226  		return
   227  	}
   228  	rdr, err := os.Open(output)
   229  	if err != nil {
   230  		utils.Error(w, "Something went wrong.", http.StatusInternalServerError, errors.Wrap(err, "failed to read the exported tarfile"))
   231  		return
   232  	}
   233  	defer rdr.Close()
   234  	utils.WriteResponse(w, http.StatusOK, rdr)
   235  }
   236  
   237  func ImagesLoad(w http.ResponseWriter, r *http.Request) {
   238  	runtime := r.Context().Value("runtime").(*libpod.Runtime)
   239  	decoder := r.Context().Value("decoder").(*schema.Decoder)
   240  	query := struct {
   241  		Reference string `schema:"reference"`
   242  	}{
   243  		// Add defaults here once needed.
   244  	}
   245  
   246  	if err := decoder.Decode(&query, r.URL.Query()); err != nil {
   247  		utils.Error(w, http.StatusText(http.StatusBadRequest), http.StatusBadRequest,
   248  			errors.Wrapf(err, "failed to parse parameters for %s", r.URL.String()))
   249  		return
   250  	}
   251  
   252  	tmpfile, err := ioutil.TempFile("", "libpod-images-load.tar")
   253  	if err != nil {
   254  		utils.Error(w, "Something went wrong.", http.StatusInternalServerError, errors.Wrap(err, "unable to create tempfile"))
   255  		return
   256  	}
   257  	defer os.Remove(tmpfile.Name())
   258  	defer tmpfile.Close()
   259  
   260  	if _, err := io.Copy(tmpfile, r.Body); err != nil && err != io.EOF {
   261  		utils.Error(w, "Something went wrong.", http.StatusInternalServerError, errors.Wrap(err, "unable to write archive to temporary file"))
   262  		return
   263  	}
   264  
   265  	tmpfile.Close()
   266  	loadedImage, err := runtime.LoadImage(context.Background(), query.Reference, tmpfile.Name(), os.Stderr, "")
   267  	if err != nil {
   268  		utils.Error(w, "Something went wrong.", http.StatusInternalServerError, errors.Wrap(err, "unable to load image"))
   269  		return
   270  	}
   271  	split := strings.Split(loadedImage, ",")
   272  	newImage, err := runtime.ImageRuntime().NewFromLocal(split[0])
   273  	if err != nil {
   274  		utils.InternalServerError(w, err)
   275  		return
   276  	}
   277  	// TODO this should go into libpod proper at some point.
   278  	if len(query.Reference) > 0 {
   279  		if err := newImage.TagImage(query.Reference); err != nil {
   280  			utils.InternalServerError(w, err)
   281  			return
   282  		}
   283  	}
   284  	utils.WriteResponse(w, http.StatusOK, entities.ImageLoadReport{Name: loadedImage})
   285  }
   286  
   287  func ImagesImport(w http.ResponseWriter, r *http.Request) {
   288  	runtime := r.Context().Value("runtime").(*libpod.Runtime)
   289  	decoder := r.Context().Value("decoder").(*schema.Decoder)
   290  	query := struct {
   291  		Changes   []string `schema:"changes"`
   292  		Message   string   `schema:"message"`
   293  		Reference string   `schema:"reference"`
   294  		URL       string   `schema:"URL"`
   295  	}{
   296  		// Add defaults here once needed.
   297  	}
   298  
   299  	if err := decoder.Decode(&query, r.URL.Query()); err != nil {
   300  		utils.Error(w, http.StatusText(http.StatusBadRequest), http.StatusBadRequest,
   301  			errors.Wrapf(err, "failed to parse parameters for %s", r.URL.String()))
   302  		return
   303  	}
   304  
   305  	// Check if we need to load the image from a URL or from the request's body.
   306  	source := query.URL
   307  	if len(query.URL) == 0 {
   308  		tmpfile, err := ioutil.TempFile("", "libpod-images-import.tar")
   309  		if err != nil {
   310  			utils.Error(w, "Something went wrong.", http.StatusInternalServerError, errors.Wrap(err, "unable to create tempfile"))
   311  			return
   312  		}
   313  		defer os.Remove(tmpfile.Name())
   314  		defer tmpfile.Close()
   315  
   316  		if _, err := io.Copy(tmpfile, r.Body); err != nil && err != io.EOF {
   317  			utils.Error(w, "Something went wrong.", http.StatusInternalServerError, errors.Wrap(err, "unable to write archive to temporary file"))
   318  			return
   319  		}
   320  
   321  		tmpfile.Close()
   322  		source = tmpfile.Name()
   323  	}
   324  	importedImage, err := runtime.Import(context.Background(), source, query.Reference, query.Changes, query.Message, true)
   325  	if err != nil {
   326  		utils.Error(w, "Something went wrong.", http.StatusInternalServerError, errors.Wrap(err, "unable to import image"))
   327  		return
   328  	}
   329  
   330  	utils.WriteResponse(w, http.StatusOK, entities.ImageImportReport{Id: importedImage})
   331  }
   332  
   333  // ImagesPull is the v2 libpod endpoint for pulling images.  Note that the
   334  // mandatory `reference` must be a reference to a registry (i.e., of docker
   335  // transport or be normalized to one).  Other transports are rejected as they
   336  // do not make sense in a remote context.
   337  func ImagesPull(w http.ResponseWriter, r *http.Request) {
   338  	runtime := r.Context().Value("runtime").(*libpod.Runtime)
   339  	decoder := r.Context().Value("decoder").(*schema.Decoder)
   340  	query := struct {
   341  		Reference    string `schema:"reference"`
   342  		Credentials  string `schema:"credentials"`
   343  		OverrideOS   string `schema:"overrideOS"`
   344  		OverrideArch string `schema:"overrideArch"`
   345  		TLSVerify    bool   `schema:"tlsVerify"`
   346  		AllTags      bool   `schema:"allTags"`
   347  	}{
   348  		TLSVerify: true,
   349  	}
   350  
   351  	if err := decoder.Decode(&query, r.URL.Query()); err != nil {
   352  		utils.Error(w, http.StatusText(http.StatusBadRequest), http.StatusBadRequest,
   353  			errors.Wrapf(err, "failed to parse parameters for %s", r.URL.String()))
   354  		return
   355  	}
   356  
   357  	if len(query.Reference) == 0 {
   358  		utils.InternalServerError(w, errors.New("reference parameter cannot be empty"))
   359  		return
   360  	}
   361  
   362  	imageRef, err := utils.ParseDockerReference(query.Reference)
   363  	if err != nil {
   364  		utils.Error(w, http.StatusText(http.StatusBadRequest), http.StatusBadRequest,
   365  			errors.Wrapf(err, "image destination %q is not a docker-transport reference", query.Reference))
   366  		return
   367  	}
   368  
   369  	// Trim the docker-transport prefix.
   370  	rawImage := strings.TrimPrefix(query.Reference, fmt.Sprintf("%s://", docker.Transport.Name()))
   371  
   372  	// all-tags doesn't work with a tagged reference, so let's check early
   373  	namedRef, err := reference.Parse(rawImage)
   374  	if err != nil {
   375  		utils.Error(w, http.StatusText(http.StatusBadRequest), http.StatusBadRequest,
   376  			errors.Wrapf(err, "error parsing reference %q", rawImage))
   377  		return
   378  	}
   379  	if _, isTagged := namedRef.(reference.Tagged); isTagged && query.AllTags {
   380  		utils.Error(w, http.StatusText(http.StatusBadRequest), http.StatusBadRequest,
   381  			errors.Errorf("reference %q must not have a tag for all-tags", rawImage))
   382  		return
   383  	}
   384  
   385  	var registryCreds *types.DockerAuthConfig
   386  	if len(query.Credentials) != 0 {
   387  		creds, err := util.ParseRegistryCreds(query.Credentials)
   388  		if err != nil {
   389  			utils.Error(w, http.StatusText(http.StatusBadRequest), http.StatusBadRequest,
   390  				errors.Wrapf(err, "error parsing credentials %q", query.Credentials))
   391  			return
   392  		}
   393  		registryCreds = creds
   394  	}
   395  
   396  	// Setup the registry options
   397  	dockerRegistryOptions := image.DockerRegistryOptions{
   398  		DockerRegistryCreds: registryCreds,
   399  		OSChoice:            query.OverrideOS,
   400  		ArchitectureChoice:  query.OverrideArch,
   401  	}
   402  	if _, found := r.URL.Query()["tlsVerify"]; found {
   403  		dockerRegistryOptions.DockerInsecureSkipTLSVerify = types.NewOptionalBool(!query.TLSVerify)
   404  	}
   405  
   406  	// Prepare the images we want to pull
   407  	imagesToPull := []string{}
   408  	res := []handlers.LibpodImagesPullReport{}
   409  	imageName := namedRef.String()
   410  
   411  	if !query.AllTags {
   412  		imagesToPull = append(imagesToPull, imageName)
   413  	} else {
   414  		systemContext := image.GetSystemContext("", "", false)
   415  		tags, err := docker.GetRepositoryTags(context.Background(), systemContext, imageRef)
   416  		if err != nil {
   417  			utils.InternalServerError(w, errors.Wrap(err, "error getting repository tags"))
   418  			return
   419  		}
   420  		for _, tag := range tags {
   421  			imagesToPull = append(imagesToPull, fmt.Sprintf("%s:%s", imageName, tag))
   422  		}
   423  	}
   424  
   425  	authfile := ""
   426  	if sys := runtime.SystemContext(); sys != nil {
   427  		dockerRegistryOptions.DockerCertPath = sys.DockerCertPath
   428  		authfile = sys.AuthFilePath
   429  	}
   430  
   431  	// Finally pull the images
   432  	for _, img := range imagesToPull {
   433  		newImage, err := runtime.ImageRuntime().New(
   434  			context.Background(),
   435  			img,
   436  			"",
   437  			authfile,
   438  			os.Stderr,
   439  			&dockerRegistryOptions,
   440  			image.SigningOptions{},
   441  			nil,
   442  			util.PullImageAlways)
   443  		if err != nil {
   444  			utils.InternalServerError(w, errors.Wrapf(err, "error pulling image %q", query.Reference))
   445  			return
   446  		}
   447  		res = append(res, handlers.LibpodImagesPullReport{ID: newImage.ID()})
   448  	}
   449  
   450  	utils.WriteResponse(w, http.StatusOK, res)
   451  }
   452  
   453  // PushImage is the handler for the compat http endpoint for pushing images.
   454  func PushImage(w http.ResponseWriter, r *http.Request) {
   455  	decoder := r.Context().Value("decoder").(*schema.Decoder)
   456  	runtime := r.Context().Value("runtime").(*libpod.Runtime)
   457  
   458  	query := struct {
   459  		Credentials string `schema:"credentials"`
   460  		Destination string `schema:"destination"`
   461  		TLSVerify   bool   `schema:"tlsVerify"`
   462  	}{
   463  		// This is where you can override the golang default value for one of fields
   464  	}
   465  
   466  	if err := decoder.Decode(&query, r.URL.Query()); err != nil {
   467  		utils.Error(w, "Something went wrong.", http.StatusBadRequest, errors.Wrapf(err, "Failed to parse parameters for %s", r.URL.String()))
   468  		return
   469  	}
   470  
   471  	source := strings.TrimSuffix(utils.GetName(r), "/push") // GetName returns the entire path
   472  	if _, err := utils.ParseStorageReference(source); err != nil {
   473  		utils.Error(w, http.StatusText(http.StatusBadRequest), http.StatusBadRequest,
   474  			errors.Wrapf(err, "image source %q is not a containers-storage-transport reference", source))
   475  		return
   476  	}
   477  
   478  	destination := query.Destination
   479  	if destination == "" {
   480  		destination = source
   481  	}
   482  
   483  	if _, err := utils.ParseDockerReference(destination); err != nil {
   484  		utils.Error(w, http.StatusText(http.StatusBadRequest), http.StatusBadRequest,
   485  			errors.Wrapf(err, "image destination %q is not a docker-transport reference", destination))
   486  		return
   487  	}
   488  
   489  	newImage, err := runtime.ImageRuntime().NewFromLocal(source)
   490  	if err != nil {
   491  		utils.ImageNotFound(w, source, errors.Wrapf(err, "Failed to find image %s", source))
   492  		return
   493  	}
   494  
   495  	var registryCreds *types.DockerAuthConfig
   496  	if len(query.Credentials) != 0 {
   497  		creds, err := util.ParseRegistryCreds(query.Credentials)
   498  		if err != nil {
   499  			utils.Error(w, http.StatusText(http.StatusBadRequest), http.StatusBadRequest,
   500  				errors.Wrapf(err, "error parsing credentials %q", query.Credentials))
   501  			return
   502  		}
   503  		registryCreds = creds
   504  	}
   505  
   506  	// TODO: the X-Registry-Auth header is not checked yet here nor in any other
   507  	// endpoint. Pushing does NOT work with authentication at the moment.
   508  	dockerRegistryOptions := &image.DockerRegistryOptions{
   509  		DockerRegistryCreds: registryCreds,
   510  	}
   511  	authfile := ""
   512  	if sys := runtime.SystemContext(); sys != nil {
   513  		dockerRegistryOptions.DockerCertPath = sys.DockerCertPath
   514  		authfile = sys.AuthFilePath
   515  	}
   516  	if _, found := r.URL.Query()["tlsVerify"]; found {
   517  		dockerRegistryOptions.DockerInsecureSkipTLSVerify = types.NewOptionalBool(!query.TLSVerify)
   518  	}
   519  
   520  	err = newImage.PushImageToHeuristicDestination(
   521  		context.Background(),
   522  		destination,
   523  		"", // manifest type
   524  		authfile,
   525  		"", // digest file
   526  		"", // signature policy
   527  		os.Stderr,
   528  		false, // force compression
   529  		image.SigningOptions{},
   530  		dockerRegistryOptions,
   531  		nil, // additional tags
   532  	)
   533  	if err != nil {
   534  		utils.Error(w, "Something went wrong.", http.StatusBadRequest, errors.Wrapf(err, "Error pushing image %q", destination))
   535  		return
   536  	}
   537  
   538  	utils.WriteResponse(w, http.StatusOK, "")
   539  }
   540  
   541  func CommitContainer(w http.ResponseWriter, r *http.Request) {
   542  	var (
   543  		destImage string
   544  		mimeType  string
   545  	)
   546  	decoder := r.Context().Value("decoder").(*schema.Decoder)
   547  	runtime := r.Context().Value("runtime").(*libpod.Runtime)
   548  
   549  	query := struct {
   550  		Author    string   `schema:"author"`
   551  		Changes   []string `schema:"changes"`
   552  		Comment   string   `schema:"comment"`
   553  		Container string   `schema:"container"`
   554  		Format    string   `schema:"format"`
   555  		Pause     bool     `schema:"pause"`
   556  		Repo      string   `schema:"repo"`
   557  		Tag       string   `schema:"tag"`
   558  	}{
   559  		Format: "oci",
   560  	}
   561  
   562  	if err := decoder.Decode(&query, r.URL.Query()); err != nil {
   563  		utils.Error(w, "Something went wrong.", http.StatusBadRequest, errors.Wrapf(err, "Failed to parse parameters for %s", r.URL.String()))
   564  		return
   565  	}
   566  	rtc, err := runtime.GetConfig()
   567  	if err != nil {
   568  		utils.Error(w, "failed to get runtime config", http.StatusInternalServerError, errors.Wrap(err, "failed to get runtime config"))
   569  		return
   570  	}
   571  	sc := image2.GetSystemContext(rtc.Engine.SignaturePolicyPath, "", false)
   572  	tag := "latest"
   573  	options := libpod.ContainerCommitOptions{
   574  		Pause: true,
   575  	}
   576  	switch query.Format {
   577  	case "oci":
   578  		mimeType = buildah.OCIv1ImageManifest
   579  		if len(query.Comment) > 0 {
   580  			utils.InternalServerError(w, errors.New("messages are only compatible with the docker image format (-f docker)"))
   581  			return
   582  		}
   583  	case "docker":
   584  		mimeType = manifest.DockerV2Schema2MediaType
   585  	default:
   586  		utils.InternalServerError(w, errors.Errorf("unrecognized image format %q", query.Format))
   587  		return
   588  	}
   589  	options.CommitOptions = buildah.CommitOptions{
   590  		SignaturePolicyPath:   rtc.Engine.SignaturePolicyPath,
   591  		ReportWriter:          os.Stderr,
   592  		SystemContext:         sc,
   593  		PreferredManifestType: mimeType,
   594  	}
   595  
   596  	if len(query.Tag) > 0 {
   597  		tag = query.Tag
   598  	}
   599  	options.Message = query.Comment
   600  	options.Author = query.Author
   601  	options.Pause = query.Pause
   602  	options.Changes = query.Changes
   603  	ctr, err := runtime.LookupContainer(query.Container)
   604  	if err != nil {
   605  		utils.Error(w, "failed to lookup container", http.StatusNotFound, err)
   606  		return
   607  	}
   608  
   609  	// I know mitr hates this ... but doing for now
   610  	if len(query.Repo) > 1 {
   611  		destImage = fmt.Sprintf("%s:%s", query.Repo, tag)
   612  	}
   613  
   614  	commitImage, err := ctr.Commit(r.Context(), destImage, options)
   615  	if err != nil && !strings.Contains(err.Error(), "is not running") {
   616  		utils.Error(w, "Something went wrong.", http.StatusInternalServerError, errors.Wrapf(err, "CommitFailure"))
   617  		return
   618  	}
   619  	utils.WriteResponse(w, http.StatusOK, handlers.IDResponse{ID: commitImage.ID()}) // nolint
   620  }
   621  
   622  func UntagImage(w http.ResponseWriter, r *http.Request) {
   623  	runtime := r.Context().Value("runtime").(*libpod.Runtime)
   624  
   625  	name := utils.GetName(r)
   626  	newImage, err := runtime.ImageRuntime().NewFromLocal(name)
   627  	if err != nil {
   628  		utils.ImageNotFound(w, name, errors.Wrapf(err, "Failed to find image %s", name))
   629  		return
   630  	}
   631  	tag := "latest"
   632  	if len(r.Form.Get("tag")) > 0 {
   633  		tag = r.Form.Get("tag")
   634  	}
   635  	if len(r.Form.Get("repo")) < 1 {
   636  		utils.Error(w, "repo tag is required", http.StatusBadRequest, errors.New("repo parameter is required to tag an image"))
   637  		return
   638  	}
   639  	repo := r.Form.Get("repo")
   640  	tagName := fmt.Sprintf("%s:%s", repo, tag)
   641  	if err := newImage.UntagImage(tagName); err != nil {
   642  		utils.Error(w, "failed to untag", http.StatusInternalServerError, err)
   643  		return
   644  	}
   645  	utils.WriteResponse(w, http.StatusCreated, "")
   646  }
   647  
   648  func SearchImages(w http.ResponseWriter, r *http.Request) {
   649  	decoder := r.Context().Value("decoder").(*schema.Decoder)
   650  	query := struct {
   651  		Term      string   `json:"term"`
   652  		Limit     int      `json:"limit"`
   653  		Filters   []string `json:"filters"`
   654  		TLSVerify bool     `json:"tlsVerify"`
   655  	}{
   656  		// This is where you can override the golang default value for one of fields
   657  	}
   658  
   659  	if err := decoder.Decode(&query, r.URL.Query()); err != nil {
   660  		utils.Error(w, "Something went wrong.", http.StatusBadRequest, errors.Wrapf(err, "Failed to parse parameters for %s", r.URL.String()))
   661  		return
   662  	}
   663  
   664  	options := image.SearchOptions{
   665  		Limit: query.Limit,
   666  	}
   667  	if _, found := r.URL.Query()["tlsVerify"]; found {
   668  		options.InsecureSkipTLSVerify = types.NewOptionalBool(!query.TLSVerify)
   669  	}
   670  
   671  	if _, found := r.URL.Query()["filters"]; found {
   672  		filter, err := image.ParseSearchFilter(query.Filters)
   673  		if err != nil {
   674  			utils.Error(w, "Something went wrong.", http.StatusBadRequest, errors.Wrapf(err, "Failed to parse filters parameter for %s", r.URL.String()))
   675  			return
   676  		}
   677  		options.Filter = *filter
   678  	}
   679  
   680  	searchResults, err := image.SearchImages(query.Term, options)
   681  	if err != nil {
   682  		utils.BadRequest(w, "term", query.Term, err)
   683  		return
   684  	}
   685  	// Convert from image.SearchResults to entities.ImageSearchReport. We don't
   686  	// want to leak any low-level packages into the remote client, which
   687  	// requires converting.
   688  	reports := make([]entities.ImageSearchReport, len(searchResults))
   689  	for i := range searchResults {
   690  		reports[i].Index = searchResults[i].Index
   691  		reports[i].Name = searchResults[i].Name
   692  		reports[i].Description = searchResults[i].Index
   693  		reports[i].Stars = searchResults[i].Stars
   694  		reports[i].Official = searchResults[i].Official
   695  		reports[i].Automated = searchResults[i].Automated
   696  	}
   697  
   698  	utils.WriteResponse(w, http.StatusOK, reports)
   699  }