github.com/containers/libpod@v1.9.4-0.20220419124438-4284fd425507/cmd/podman/search.go (about)

     1  package main
     2  
     3  import (
     4  	"os"
     5  	"reflect"
     6  	"strings"
     7  
     8  	buildahcli "github.com/containers/buildah/pkg/cli"
     9  	"github.com/containers/buildah/pkg/formats"
    10  	"github.com/containers/image/v5/types"
    11  	"github.com/containers/libpod/cmd/podman/cliconfig"
    12  	"github.com/containers/libpod/libpod/image"
    13  	"github.com/pkg/errors"
    14  	"github.com/spf13/cobra"
    15  )
    16  
    17  var (
    18  	searchCommand     cliconfig.SearchValues
    19  	searchDescription = `Search registries for a given image. Can search all the default registries or a specific registry.
    20  
    21    Users can limit the number of results, and filter the output based on certain conditions.`
    22  	_searchCommand = &cobra.Command{
    23  		Use:   "search [flags] TERM",
    24  		Short: "Search registry for image",
    25  		Long:  searchDescription,
    26  		RunE: func(cmd *cobra.Command, args []string) error {
    27  			searchCommand.InputArgs = args
    28  			searchCommand.GlobalFlags = MainGlobalOpts
    29  			searchCommand.Remote = remoteclient
    30  			return searchCmd(&searchCommand)
    31  		},
    32  		Example: `podman search --filter=is-official --limit 3 alpine
    33    podman search registry.fedoraproject.org/  # only works with v2 registries
    34    podman search --format "table {{.Index}} {{.Name}}" registry.fedoraproject.org/fedora`,
    35  	}
    36  )
    37  
    38  func init() {
    39  	searchCommand.Command = _searchCommand
    40  	searchCommand.SetHelpTemplate(HelpTemplate())
    41  	searchCommand.SetUsageTemplate(UsageTemplate())
    42  	flags := searchCommand.Flags()
    43  	flags.StringSliceVarP(&searchCommand.Filter, "filter", "f", []string{}, "Filter output based on conditions provided (default [])")
    44  	flags.StringVar(&searchCommand.Format, "format", "", "Change the output format to a Go template")
    45  	flags.IntVar(&searchCommand.Limit, "limit", 0, "Limit the number of results")
    46  	flags.BoolVar(&searchCommand.NoTrunc, "no-trunc", false, "Do not truncate the output")
    47  	// Disabled flags for the remote client
    48  	if !remote {
    49  		flags.StringVar(&searchCommand.Authfile, "authfile", buildahcli.GetDefaultAuthFile(), "Path of the authentication file. Use REGISTRY_AUTH_FILE environment variable to override")
    50  		flags.BoolVar(&searchCommand.TlsVerify, "tls-verify", true, "Require HTTPS and verify certificates when contacting registries")
    51  	}
    52  }
    53  
    54  func searchCmd(c *cliconfig.SearchValues) error {
    55  	args := c.InputArgs
    56  	if len(args) > 1 {
    57  		return errors.Errorf("too many arguments. Requires exactly 1")
    58  	}
    59  	if len(args) == 0 {
    60  		return errors.Errorf("no argument given, requires exactly 1 argument")
    61  	}
    62  	term := args[0]
    63  
    64  	filter, err := image.ParseSearchFilter(c.Filter)
    65  	if err != nil {
    66  		return err
    67  	}
    68  
    69  	if c.Authfile != "" {
    70  		if _, err := os.Stat(c.Authfile); err != nil {
    71  			return errors.Wrapf(err, "error getting authfile %s", c.Authfile)
    72  		}
    73  	}
    74  
    75  	searchOptions := image.SearchOptions{
    76  		NoTrunc:  c.NoTrunc,
    77  		Limit:    c.Limit,
    78  		Filter:   *filter,
    79  		Authfile: c.Authfile,
    80  	}
    81  	if c.Flag("tls-verify").Changed {
    82  		searchOptions.InsecureSkipTLSVerify = types.NewOptionalBool(!c.TlsVerify)
    83  	}
    84  
    85  	results, err := image.SearchImages(term, searchOptions)
    86  	if err != nil {
    87  		return err
    88  	}
    89  	format := genSearchFormat(c.Format)
    90  	if len(results) == 0 {
    91  		return nil
    92  	}
    93  	out := formats.StdoutTemplateArray{Output: searchToGeneric(results), Template: format, Fields: searchHeaderMap()}
    94  	return out.Out()
    95  }
    96  
    97  // searchHeaderMap returns the headers of a SearchResult.
    98  func searchHeaderMap() map[string]string {
    99  	s := new(image.SearchResult)
   100  	v := reflect.Indirect(reflect.ValueOf(s))
   101  	values := make(map[string]string, v.NumField())
   102  
   103  	for i := 0; i < v.NumField(); i++ {
   104  		key := v.Type().Field(i).Name
   105  		value := key
   106  		values[key] = strings.ToUpper(splitCamelCase(value))
   107  	}
   108  	return values
   109  }
   110  
   111  func genSearchFormat(format string) string {
   112  	if format != "" {
   113  		// "\t" from the command line is not being recognized as a tab
   114  		// replacing the string "\t" to a tab character if the user passes in "\t"
   115  		return strings.Replace(format, `\t`, "\t", -1)
   116  	}
   117  	return "table {{.Index}}\t{{.Name}}\t{{.Description}}\t{{.Stars}}\t{{.Official}}\t{{.Automated}}\t"
   118  }
   119  
   120  func searchToGeneric(params []image.SearchResult) (genericParams []interface{}) {
   121  	for _, v := range params {
   122  		genericParams = append(genericParams, interface{}(v))
   123  	}
   124  	return genericParams
   125  }