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

     1  package images
     2  
     3  import (
     4  	"reflect"
     5  	"strings"
     6  
     7  	buildahcli "github.com/containers/buildah/pkg/cli"
     8  	"github.com/containers/buildah/pkg/formats"
     9  	"github.com/containers/image/v5/types"
    10  	"github.com/containers/libpod/cmd/podmanV2/registry"
    11  	"github.com/containers/libpod/pkg/domain/entities"
    12  	"github.com/containers/libpod/pkg/util/camelcase"
    13  	"github.com/pkg/errors"
    14  	"github.com/spf13/cobra"
    15  	"github.com/spf13/pflag"
    16  )
    17  
    18  // searchOptionsWrapper wraps entities.ImagePullOptions and prevents leaking
    19  // CLI-only fields into the API types.
    20  type searchOptionsWrapper struct {
    21  	entities.ImageSearchOptions
    22  	// CLI only flags
    23  	TLSVerifyCLI bool   // Used to convert to an optional bool later
    24  	Format       string // For go templating
    25  }
    26  
    27  var (
    28  	searchOptions     = searchOptionsWrapper{}
    29  	searchDescription = `Search registries for a given image. Can search all the default registries or a specific registry.
    30  
    31  	Users can limit the number of results, and filter the output based on certain conditions.`
    32  
    33  	// Command: podman search
    34  	searchCmd = &cobra.Command{
    35  		Use:     "search [flags] TERM",
    36  		Short:   "Search registry for image",
    37  		Long:    searchDescription,
    38  		PreRunE: preRunE,
    39  		RunE:    imageSearch,
    40  		Args:    cobra.ExactArgs(1),
    41  		Example: `podman search --filter=is-official --limit 3 alpine
    42    podman search registry.fedoraproject.org/  # only works with v2 registries
    43    podman search --format "table {{.Index}} {{.Name}}" registry.fedoraproject.org/fedora`,
    44  	}
    45  
    46  	// Command: podman image search
    47  	imageSearchCmd = &cobra.Command{
    48  		Use:     searchCmd.Use,
    49  		Short:   searchCmd.Short,
    50  		Long:    searchCmd.Long,
    51  		PreRunE: searchCmd.PreRunE,
    52  		RunE:    searchCmd.RunE,
    53  		Args:    searchCmd.Args,
    54  		Example: `podman image search --filter=is-official --limit 3 alpine
    55  		podman image search registry.fedoraproject.org/  # only works with v2 registries
    56  		podman image search --format "table {{.Index}} {{.Name}}" registry.fedoraproject.org/fedora`,
    57  	}
    58  )
    59  
    60  func init() {
    61  	// search
    62  	registry.Commands = append(registry.Commands, registry.CliCommand{
    63  		Mode:    []entities.EngineMode{entities.ABIMode, entities.TunnelMode},
    64  		Command: searchCmd,
    65  	})
    66  
    67  	searchCmd.SetHelpTemplate(registry.HelpTemplate())
    68  	searchCmd.SetUsageTemplate(registry.UsageTemplate())
    69  	flags := searchCmd.Flags()
    70  	searchFlags(flags)
    71  
    72  	// images search
    73  	registry.Commands = append(registry.Commands, registry.CliCommand{
    74  		Mode:    []entities.EngineMode{entities.ABIMode, entities.TunnelMode},
    75  		Command: imageSearchCmd,
    76  		Parent:  imageCmd,
    77  	})
    78  
    79  	imageSearchFlags := imageSearchCmd.Flags()
    80  	searchFlags(imageSearchFlags)
    81  }
    82  
    83  // searchFlags set the flags for the pull command.
    84  func searchFlags(flags *pflag.FlagSet) {
    85  	flags.StringSliceVarP(&searchOptions.Filters, "filter", "f", []string{}, "Filter output based on conditions provided (default [])")
    86  	flags.StringVar(&searchOptions.Format, "format", "", "Change the output format to a Go template")
    87  	flags.IntVar(&searchOptions.Limit, "limit", 0, "Limit the number of results")
    88  	flags.BoolVar(&searchOptions.NoTrunc, "no-trunc", false, "Do not truncate the output")
    89  	flags.StringVar(&searchOptions.Authfile, "authfile", buildahcli.GetDefaultAuthFile(), "Path of the authentication file. Use REGISTRY_AUTH_FILE environment variable to override")
    90  	flags.BoolVar(&searchOptions.TLSVerifyCLI, "tls-verify", true, "Require HTTPS and verify certificates when contacting registries")
    91  	if registry.IsRemote() {
    92  		_ = flags.MarkHidden("authfile")
    93  		_ = flags.MarkHidden("tls-verify")
    94  	}
    95  }
    96  
    97  // imageSearch implements the command for searching images.
    98  func imageSearch(cmd *cobra.Command, args []string) error {
    99  	searchTerm := ""
   100  	switch len(args) {
   101  	case 1:
   102  		searchTerm = args[0]
   103  	default:
   104  		return errors.Errorf("search requires exactly one argument")
   105  	}
   106  
   107  	sarchOptsAPI := searchOptions.ImageSearchOptions
   108  	// TLS verification in c/image is controlled via a `types.OptionalBool`
   109  	// which allows for distinguishing among set-true, set-false, unspecified
   110  	// which is important to implement a sane way of dealing with defaults of
   111  	// boolean CLI flags.
   112  	if cmd.Flags().Changed("tls-verify") {
   113  		sarchOptsAPI.TLSVerify = types.NewOptionalBool(pullOptions.TLSVerifyCLI)
   114  	}
   115  
   116  	searchReport, err := registry.ImageEngine().Search(registry.GetContext(), searchTerm, sarchOptsAPI)
   117  	if err != nil {
   118  		return err
   119  	}
   120  
   121  	format := genSearchFormat(searchOptions.Format)
   122  	if len(searchReport) == 0 {
   123  		return nil
   124  	}
   125  	out := formats.StdoutTemplateArray{Output: searchToGeneric(searchReport), Template: format, Fields: searchHeaderMap()}
   126  	return out.Out()
   127  }
   128  
   129  // searchHeaderMap returns the headers of a SearchResult.
   130  func searchHeaderMap() map[string]string {
   131  	s := new(entities.ImageSearchReport)
   132  	v := reflect.Indirect(reflect.ValueOf(s))
   133  	values := make(map[string]string, v.NumField())
   134  
   135  	for i := 0; i < v.NumField(); i++ {
   136  		key := v.Type().Field(i).Name
   137  		value := key
   138  		values[key] = strings.ToUpper(strings.Join(camelcase.Split(value), " "))
   139  	}
   140  	return values
   141  }
   142  
   143  func genSearchFormat(format string) string {
   144  	if format != "" {
   145  		// "\t" from the command line is not being recognized as a tab
   146  		// replacing the string "\t" to a tab character if the user passes in "\t"
   147  		return strings.Replace(format, `\t`, "\t", -1)
   148  	}
   149  	return "table {{.Index}}\t{{.Name}}\t{{.Description}}\t{{.Stars}}\t{{.Official}}\t{{.Automated}}\t"
   150  }
   151  
   152  func searchToGeneric(params []entities.ImageSearchReport) (genericParams []interface{}) {
   153  	for _, v := range params {
   154  		genericParams = append(genericParams, interface{}(v))
   155  	}
   156  	return genericParams
   157  }