github.com/stackdocker/rkt@v0.10.1-0.20151109095037-1aa827478248/rkt/image_list.go (about) 1 // Copyright 2015 The rkt Authors 2 // 3 // Licensed under the Apache License, Version 2.0 (the "License"); 4 // you may not use this file except in compliance with the License. 5 // You may obtain a copy of the License at 6 // 7 // http://www.apache.org/licenses/LICENSE-2.0 8 // 9 // Unless required by applicable law or agreed to in writing, software 10 // distributed under the License is distributed on an "AS IS" BASIS, 11 // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 // See the License for the specific language governing permissions and 13 // limitations under the License. 14 15 package main 16 17 import ( 18 "bytes" 19 "encoding/json" 20 "fmt" 21 "strings" 22 23 "github.com/coreos/rkt/Godeps/_workspace/src/github.com/appc/spec/schema" 24 "github.com/coreos/rkt/Godeps/_workspace/src/github.com/appc/spec/schema/lastditch" 25 "github.com/coreos/rkt/Godeps/_workspace/src/github.com/dustin/go-humanize" 26 "github.com/coreos/rkt/Godeps/_workspace/src/github.com/spf13/cobra" 27 "github.com/coreos/rkt/store" 28 ) 29 30 const ( 31 defaultTimeLayout = "2006-01-02 15:04:05.999 -0700 MST" 32 33 idField = "id" 34 nameField = "name" 35 importTimeField = "importtime" 36 latestField = "latest" 37 ) 38 39 var ( 40 // map of valid fields and related flag value 41 imagesAllFields = map[string]struct{}{ 42 idField: struct{}{}, 43 nameField: struct{}{}, 44 importTimeField: struct{}{}, 45 latestField: struct{}{}, 46 } 47 48 // map of valid fields and related header name 49 ImagesFieldHeaderMap = map[string]string{ 50 idField: "ID", 51 nameField: "NAME", 52 importTimeField: "IMPORT TIME", 53 latestField: "LATEST", 54 } 55 56 // map of valid sort fields containing the mapping between the provided field name 57 // and the related aciinfo's field name. 58 ImagesFieldAciInfoMap = map[string]string{ 59 idField: "blobkey", 60 nameField: "name", 61 importTimeField: "importtime", 62 latestField: "latest", 63 } 64 65 ImagesSortableFields = map[string]struct{}{ 66 nameField: struct{}{}, 67 importTimeField: struct{}{}, 68 } 69 ) 70 71 type ImagesFields []string 72 73 func (ifs *ImagesFields) Set(s string) error { 74 *ifs = []string{} 75 fields := strings.Split(s, ",") 76 seen := map[string]struct{}{} 77 for _, f := range fields { 78 // accept any case 79 f = strings.ToLower(f) 80 _, ok := imagesAllFields[f] 81 if !ok { 82 return fmt.Errorf("unknown field %q", f) 83 } 84 if _, ok := seen[f]; ok { 85 return fmt.Errorf("duplicated field %q", f) 86 } 87 *ifs = append(*ifs, f) 88 seen[f] = struct{}{} 89 } 90 91 return nil 92 } 93 94 func (ifs *ImagesFields) String() string { 95 return strings.Join(*ifs, ",") 96 } 97 98 func (ifs *ImagesFields) Type() string { 99 return "imagesFields" 100 } 101 102 type ImagesSortFields []string 103 104 func (isf *ImagesSortFields) Set(s string) error { 105 *isf = []string{} 106 fields := strings.Split(s, ",") 107 seen := map[string]struct{}{} 108 for _, f := range fields { 109 // accept any case 110 f = strings.ToLower(f) 111 _, ok := ImagesSortableFields[f] 112 if !ok { 113 return fmt.Errorf("unknown field %q", f) 114 } 115 if _, ok := seen[f]; ok { 116 return fmt.Errorf("duplicated field %q", f) 117 } 118 *isf = append(*isf, f) 119 seen[f] = struct{}{} 120 } 121 122 return nil 123 } 124 125 func (isf *ImagesSortFields) String() string { 126 return strings.Join(*isf, ",") 127 } 128 129 func (isf *ImagesSortFields) Type() string { 130 return "imagesSortFields" 131 } 132 133 type ImagesSortAsc bool 134 135 func (isa *ImagesSortAsc) Set(s string) error { 136 switch strings.ToLower(s) { 137 case "asc": 138 *isa = true 139 case "desc": 140 *isa = false 141 default: 142 return fmt.Errorf("wrong sort order") 143 } 144 return nil 145 } 146 147 func (isa *ImagesSortAsc) String() string { 148 if *isa { 149 return "asc" 150 } 151 return "desc" 152 } 153 154 func (isa *ImagesSortAsc) Type() string { 155 return "imagesSortAsc" 156 } 157 158 var ( 159 cmdImageList = &cobra.Command{ 160 Use: "list", 161 Short: "List images in the local store", 162 Run: runWrapper(runImages), 163 } 164 flagImagesFields ImagesFields 165 flagImagesSortFields ImagesSortFields 166 flagImagesSortAsc ImagesSortAsc 167 ) 168 169 func init() { 170 // Set defaults 171 flagImagesFields = []string{idField, nameField, importTimeField, latestField} 172 flagImagesSortFields = []string{importTimeField} 173 flagImagesSortAsc = true 174 175 cmdImage.AddCommand(cmdImageList) 176 cmdImageList.Flags().Var(&flagImagesFields, "fields", `comma separated list of fields to display. Accepted values: "id", "name", "importtime", "latest"`) 177 cmdImageList.Flags().Var(&flagImagesSortFields, "sort", `sort the output according to the provided comma separated list of fields. Accepted values: "name", "importtime"`) 178 cmdImageList.Flags().Var(&flagImagesSortAsc, "order", `choose the sorting order if at least one sort field is provided (--sort). Accepted values: "asc", "desc"`) 179 cmdImageList.Flags().BoolVar(&flagNoLegend, "no-legend", false, "suppress a legend with the list") 180 cmdImageList.Flags().BoolVar(&flagFullOutput, "full", false, "use long output format") 181 } 182 183 func runImages(cmd *cobra.Command, args []string) int { 184 var errors []error 185 tabBuffer := new(bytes.Buffer) 186 tabOut := getTabOutWithWriter(tabBuffer) 187 188 if !flagNoLegend { 189 var headerFields []string 190 for _, f := range flagImagesFields { 191 headerFields = append(headerFields, ImagesFieldHeaderMap[f]) 192 } 193 fmt.Fprintf(tabOut, "%s\n", strings.Join(headerFields, "\t")) 194 } 195 196 s, err := store.NewStore(globalFlags.Dir) 197 if err != nil { 198 stderr("images: cannot open store: %v\n", err) 199 return 1 200 } 201 202 var sortAciinfoFields []string 203 for _, f := range flagImagesSortFields { 204 sortAciinfoFields = append(sortAciinfoFields, ImagesFieldAciInfoMap[f]) 205 } 206 aciInfos, err := s.GetAllACIInfos(sortAciinfoFields, bool(flagImagesSortAsc)) 207 if err != nil { 208 stderr("images: unable to get aci infos: %v", err) 209 return 1 210 } 211 212 for _, aciInfo := range aciInfos { 213 imj, err := s.GetImageManifestJSON(aciInfo.BlobKey) 214 if err != nil { 215 // ignore aciInfo with missing image manifest as it can be deleted in the meantime 216 continue 217 } 218 var im *schema.ImageManifest 219 if err = json.Unmarshal(imj, &im); err != nil { 220 errors = append(errors, newImgListLoadError(err, imj, aciInfo.BlobKey)) 221 continue 222 } 223 version, ok := im.Labels.Get("version") 224 var fieldValues []string 225 for _, f := range flagImagesFields { 226 fieldValue := "" 227 switch f { 228 case idField: 229 hashKey := aciInfo.BlobKey 230 if !flagFullOutput { 231 // The short hash form is [HASH_ALGO]-[FIRST 12 CHAR] 232 // For example, sha512-123456789012 233 pos := strings.Index(hashKey, "-") 234 trimLength := pos + 13 235 if pos > 0 && trimLength < len(hashKey) { 236 hashKey = hashKey[:trimLength] 237 } 238 } 239 fieldValue = hashKey 240 case nameField: 241 fieldValue = aciInfo.Name 242 if ok { 243 fieldValue = fmt.Sprintf("%s:%s", fieldValue, version) 244 } 245 case importTimeField: 246 if flagFullOutput { 247 fieldValue = aciInfo.ImportTime.Format(defaultTimeLayout) 248 } else { 249 fieldValue = humanize.Time(aciInfo.ImportTime) 250 } 251 case latestField: 252 fieldValue = fmt.Sprintf("%t", aciInfo.Latest) 253 } 254 fieldValues = append(fieldValues, fieldValue) 255 256 } 257 fmt.Fprintf(tabOut, "%s\n", strings.Join(fieldValues, "\t")) 258 } 259 260 if len(errors) > 0 { 261 sep := "----------------------------------------" 262 stderr("%d error(s) encountered when listing images:", len(errors)) 263 stderr("%s", sep) 264 for _, err := range errors { 265 stderr("%s", err.Error()) 266 stderr("%s", sep) 267 } 268 stderr("Misc:") 269 stderr(" rkt's appc version: %s", schema.AppContainerVersion) 270 // make a visible break between errors and the listing 271 stderr("") 272 } 273 tabOut.Flush() 274 stdout("%s", tabBuffer.String()) 275 return 0 276 } 277 278 func newImgListLoadError(err error, imj []byte, blobKey string) error { 279 var lines []string 280 im := lastditch.ImageManifest{} 281 imErr := im.UnmarshalJSON(imj) 282 if imErr == nil { 283 lines = append(lines, fmt.Sprintf("Unable to load manifest of image %s (spec version %s) because it is invalid:", im.Name, im.ACVersion)) 284 lines = append(lines, fmt.Sprintf(" %v", err)) 285 } else { 286 lines = append(lines, "Unable to load manifest of an image because it is invalid:") 287 lines = append(lines, fmt.Sprintf(" %v", err)) 288 lines = append(lines, " Also, failed to get any information about invalid image manifest:") 289 lines = append(lines, fmt.Sprintf(" %v", imErr)) 290 } 291 lines = append(lines, "ID of the invalid image:") 292 lines = append(lines, fmt.Sprintf(" %s", blobKey)) 293 return fmt.Errorf("%s", strings.Join(lines, "\n")) 294 }