github.com/phroggyy/helm@v3.0.0-beta.3+incompatible/cmd/helm/search_repo.go (about) 1 /* 2 Copyright The Helm Authors. 3 4 Licensed under the Apache License, Version 2.0 (the "License"); 5 you may not use this file except in compliance with the License. 6 You may obtain a copy of the License at 7 8 http://www.apache.org/licenses/LICENSE-2.0 9 10 Unless required by applicable law or agreed to in writing, software 11 distributed under the License is distributed on an "AS IS" BASIS, 12 WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 See the License for the specific language governing permissions and 14 limitations under the License. 15 */ 16 17 package main 18 19 import ( 20 "fmt" 21 "io" 22 "path/filepath" 23 "strings" 24 25 "github.com/Masterminds/semver" 26 "github.com/gosuri/uitable" 27 "github.com/pkg/errors" 28 "github.com/spf13/cobra" 29 30 "helm.sh/helm/cmd/helm/search" 31 "helm.sh/helm/pkg/helmpath" 32 "helm.sh/helm/pkg/repo" 33 ) 34 35 const searchRepoDesc = ` 36 Search reads through all of the repositories configured on the system, and 37 looks for matches. Search of these repositories uses the metadata stored on 38 the system. 39 40 Repositories are managed with 'helm repo' commands. 41 ` 42 43 // searchMaxScore suggests that any score higher than this is not considered a match. 44 const searchMaxScore = 25 45 46 type searchRepoOptions struct { 47 versions bool 48 regexp bool 49 version string 50 maxColWidth uint 51 repoFile string 52 repoCacheDir string 53 } 54 55 func newSearchRepoCmd(out io.Writer) *cobra.Command { 56 o := &searchRepoOptions{} 57 58 cmd := &cobra.Command{ 59 Use: "repo [keyword]", 60 Short: "search repositories for a keyword in charts", 61 Long: searchRepoDesc, 62 RunE: func(cmd *cobra.Command, args []string) error { 63 o.repoFile = settings.RepositoryConfig 64 o.repoCacheDir = settings.RepositoryCache 65 return o.run(out, args) 66 }, 67 } 68 69 f := cmd.Flags() 70 f.BoolVarP(&o.regexp, "regexp", "r", false, "use regular expressions for searching repositories you have added") 71 f.BoolVarP(&o.versions, "versions", "l", false, "show the long listing, with each version of each chart on its own line, for repositories you have added") 72 f.StringVar(&o.version, "version", "", "search using semantic versioning constraints on repositories you have added") 73 f.UintVar(&o.maxColWidth, "max-col-width", 50, "maximum column width for output table") 74 75 return cmd 76 } 77 78 func (o *searchRepoOptions) run(out io.Writer, args []string) error { 79 index, err := o.buildIndex(out) 80 if err != nil { 81 return err 82 } 83 84 var res []*search.Result 85 if len(args) == 0 { 86 res = index.All() 87 } else { 88 q := strings.Join(args, " ") 89 res, err = index.Search(q, searchMaxScore, o.regexp) 90 if err != nil { 91 return err 92 } 93 } 94 95 search.SortScore(res) 96 data, err := o.applyConstraint(res) 97 if err != nil { 98 return err 99 } 100 101 fmt.Fprintln(out, o.formatSearchResults(data)) 102 103 return nil 104 } 105 106 func (o *searchRepoOptions) applyConstraint(res []*search.Result) ([]*search.Result, error) { 107 if len(o.version) == 0 { 108 return res, nil 109 } 110 111 constraint, err := semver.NewConstraint(o.version) 112 if err != nil { 113 return res, errors.Wrap(err, "an invalid version/constraint format") 114 } 115 116 data := res[:0] 117 foundNames := map[string]bool{} 118 for _, r := range res { 119 if _, found := foundNames[r.Name]; found { 120 continue 121 } 122 v, err := semver.NewVersion(r.Chart.Version) 123 if err != nil || constraint.Check(v) { 124 data = append(data, r) 125 if !o.versions { 126 foundNames[r.Name] = true // If user hasn't requested all versions, only show the latest that matches 127 } 128 } 129 } 130 131 return data, nil 132 } 133 134 func (o *searchRepoOptions) formatSearchResults(res []*search.Result) string { 135 if len(res) == 0 { 136 return "No results found" 137 } 138 table := uitable.New() 139 table.MaxColWidth = o.maxColWidth 140 table.AddRow("NAME", "CHART VERSION", "APP VERSION", "DESCRIPTION") 141 for _, r := range res { 142 table.AddRow(r.Name, r.Chart.Version, r.Chart.AppVersion, r.Chart.Description) 143 } 144 return table.String() 145 } 146 147 func (o *searchRepoOptions) buildIndex(out io.Writer) (*search.Index, error) { 148 // Load the repositories.yaml 149 rf, err := repo.LoadFile(o.repoFile) 150 if isNotExist(err) || len(rf.Repositories) == 0 { 151 return nil, errors.New("no repositories configured") 152 } 153 154 i := search.NewIndex() 155 for _, re := range rf.Repositories { 156 n := re.Name 157 f := filepath.Join(o.repoCacheDir, helmpath.CacheIndexFile(n)) 158 ind, err := repo.LoadIndexFile(f) 159 if err != nil { 160 // TODO should print to stderr 161 fmt.Fprintf(out, "WARNING: Repo %q is corrupt or missing. Try 'helm repo update'.", n) 162 continue 163 } 164 165 i.AddRepo(n, ind, o.versions || len(o.version) > 0) 166 } 167 return i, nil 168 }