github.com/latiif/helm@v2.15.0+incompatible/cmd/helm/search.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 "strings" 23 24 "github.com/Masterminds/semver" 25 "github.com/gosuri/uitable" 26 "github.com/spf13/cobra" 27 28 "k8s.io/helm/cmd/helm/search" 29 "k8s.io/helm/pkg/helm/helmpath" 30 "k8s.io/helm/pkg/repo" 31 ) 32 33 const searchDesc = ` 34 Search reads through all of the repositories configured on the system, and 35 looks for matches. 36 37 Repositories are managed with 'helm repo' commands. 38 39 To look for charts with a particular name (such as stable/mysql), try 40 searching using vertical tabs (\v). Vertical tabs are used as the delimiter 41 between search fields. For example: 42 43 helm search --regexp '\vstable/mysql\v' 44 45 To search for charts using common keywords (such as "database" or 46 "key-value store"), use 47 48 helm search database 49 50 or 51 52 helm search key-value store 53 ` 54 55 // searchMaxScore suggests that any score higher than this is not considered a match. 56 const searchMaxScore = 25 57 58 type searchCmd struct { 59 out io.Writer 60 helmhome helmpath.Home 61 62 versions bool 63 regexp bool 64 version string 65 colWidth uint 66 output string 67 } 68 69 type chartElement struct { 70 Name string 71 Version string 72 AppVersion string 73 Description string 74 } 75 76 func newSearchCmd(out io.Writer) *cobra.Command { 77 sc := &searchCmd{out: out} 78 79 cmd := &cobra.Command{ 80 Use: "search [keyword]", 81 Short: "Search for a keyword in charts", 82 Long: searchDesc, 83 RunE: func(cmd *cobra.Command, args []string) error { 84 sc.helmhome = settings.Home 85 return sc.run(args) 86 }, 87 } 88 89 f := cmd.Flags() 90 f.BoolVarP(&sc.regexp, "regexp", "r", false, "Use regular expressions for searching") 91 f.BoolVarP(&sc.versions, "versions", "l", false, "Show the long listing, with each version of each chart on its own line") 92 f.StringVarP(&sc.version, "version", "v", "", "Search using semantic versioning constraints") 93 f.UintVar(&sc.colWidth, "col-width", 60, "Specifies the max column width of output") 94 bindOutputFlag(cmd, &sc.output) 95 96 return cmd 97 } 98 99 func (s *searchCmd) run(args []string) error { 100 index, err := s.buildIndex() 101 if err != nil { 102 return err 103 } 104 105 var res []*search.Result 106 if len(args) == 0 { 107 res = index.All() 108 } else { 109 q := strings.Join(args, " ") 110 res, err = index.Search(q, searchMaxScore, s.regexp) 111 if err != nil { 112 return err 113 } 114 } 115 116 search.SortScore(res) 117 data, err := s.applyConstraint(res) 118 if err != nil { 119 return err 120 } 121 122 return write(s.out, &searchWriter{data, s.colWidth}, outputFormat(s.output)) 123 } 124 125 func (s *searchCmd) applyConstraint(res []*search.Result) ([]*search.Result, error) { 126 if len(s.version) == 0 { 127 return res, nil 128 } 129 130 constraint, err := semver.NewConstraint(s.version) 131 if err != nil { 132 return res, fmt.Errorf("an invalid version/constraint format: %s", err) 133 } 134 135 data := res[:0] 136 foundNames := map[string]bool{} 137 for _, r := range res { 138 if _, found := foundNames[r.Name]; found { 139 continue 140 } 141 v, err := semver.NewVersion(r.Chart.Version) 142 if err != nil || constraint.Check(v) { 143 data = append(data, r) 144 if !s.versions { 145 foundNames[r.Name] = true // If user hasn't requested all versions, only show the latest that matches 146 } 147 } 148 } 149 150 return data, nil 151 } 152 153 func (s *searchCmd) buildIndex() (*search.Index, error) { 154 // Load the repositories.yaml 155 rf, err := repo.LoadRepositoriesFile(s.helmhome.RepositoryFile()) 156 if err != nil { 157 return nil, err 158 } 159 160 i := search.NewIndex() 161 for _, re := range rf.Repositories { 162 n := re.Name 163 f := s.helmhome.CacheIndex(n) 164 ind, err := repo.LoadIndexFile(f) 165 if err != nil { 166 fmt.Fprintf(s.out, "WARNING: Repo %q is corrupt or missing. Try 'helm repo update'.\n", n) 167 continue 168 } 169 170 i.AddRepo(n, ind, s.versions || len(s.version) > 0) 171 } 172 return i, nil 173 } 174 175 //////////// Printer implementation below here 176 type searchWriter struct { 177 results []*search.Result 178 columnWidth uint 179 } 180 181 func (r *searchWriter) WriteTable(out io.Writer) error { 182 if len(r.results) == 0 { 183 _, err := out.Write([]byte("No results found\n")) 184 if err != nil { 185 return fmt.Errorf("unable to write results: %s", err) 186 } 187 return nil 188 } 189 table := uitable.New() 190 table.MaxColWidth = r.columnWidth 191 table.AddRow("NAME", "CHART VERSION", "APP VERSION", "DESCRIPTION") 192 for _, r := range r.results { 193 table.AddRow(r.Name, r.Chart.Version, r.Chart.AppVersion, r.Chart.Description) 194 } 195 return encodeTable(out, table) 196 } 197 198 func (r *searchWriter) WriteJSON(out io.Writer) error { 199 return r.encodeByFormat(out, outputJSON) 200 } 201 202 func (r *searchWriter) WriteYAML(out io.Writer) error { 203 return r.encodeByFormat(out, outputYAML) 204 } 205 206 func (r *searchWriter) encodeByFormat(out io.Writer, format outputFormat) error { 207 var chartList []chartElement 208 209 for _, r := range r.results { 210 chartList = append(chartList, chartElement{r.Name, r.Chart.Version, r.Chart.AppVersion, r.Chart.Description}) 211 } 212 213 switch format { 214 case outputJSON: 215 return encodeJSON(out, chartList) 216 case outputYAML: 217 return encodeYAML(out, chartList) 218 } 219 220 // Because this is a non-exported function and only called internally by 221 // WriteJSON and WriteYAML, we shouldn't get invalid types 222 return nil 223 }