| OLD | NEW |
| (Empty) |
| 1 // Copyright 2015 The LUCI 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 cli | |
| 16 | |
| 17 import ( | |
| 18 "bufio" | |
| 19 "fmt" | |
| 20 "os" | |
| 21 "sort" | |
| 22 "strings" | |
| 23 | |
| 24 "github.com/luci/luci-go/common/errors" | |
| 25 log "github.com/luci/luci-go/common/logging" | |
| 26 "github.com/luci/luci-go/common/proto/google" | |
| 27 "github.com/luci/luci-go/logdog/client/coordinator" | |
| 28 "github.com/luci/luci-go/logdog/common/types" | |
| 29 "github.com/luci/luci-go/luci_config/common/cfgtypes" | |
| 30 | |
| 31 "github.com/maruel/subcommands" | |
| 32 ) | |
| 33 | |
| 34 const ( | |
| 35 // defaultListResults is the default number of list results to return. | |
| 36 defaultListResults = 200 | |
| 37 ) | |
| 38 | |
| 39 type listCommandRun struct { | |
| 40 subcommands.CommandRunBase | |
| 41 | |
| 42 o coordinator.ListOptions | |
| 43 } | |
| 44 | |
| 45 func newListCommand() *subcommands.Command { | |
| 46 return &subcommands.Command{ | |
| 47 UsageLine: "ls", | |
| 48 ShortDesc: "List log stream hierarchy space.", | |
| 49 CommandRun: func() subcommands.CommandRun { | |
| 50 cmd := &listCommandRun{} | |
| 51 | |
| 52 fs := cmd.GetFlags() | |
| 53 fs.BoolVar(&cmd.o.State, "l", false, "Perform long listi
ng, showing stream details.") | |
| 54 fs.BoolVar(&cmd.o.StreamsOnly, "streams", false, "Only l
ist streams, not intermediate components.") | |
| 55 fs.BoolVar(&cmd.o.Purged, "purged", false, "Include purg
ed streams in listing (admin-only).") | |
| 56 return cmd | |
| 57 }, | |
| 58 } | |
| 59 } | |
| 60 | |
| 61 func (cmd *listCommandRun) Run(scApp subcommands.Application, args []string, _ s
ubcommands.Env) int { | |
| 62 a := scApp.(*application) | |
| 63 | |
| 64 if len(args) == 0 { | |
| 65 args = []string{""} | |
| 66 } | |
| 67 | |
| 68 bio := bufio.NewWriter(os.Stdout) | |
| 69 defer bio.Flush() | |
| 70 | |
| 71 coord, err := a.coordinatorClient("") | |
| 72 if err != nil { | |
| 73 errors.Log(a, errors.Annotate(err, "could not create Coordinator
client").Err()) | |
| 74 return 1 | |
| 75 } | |
| 76 | |
| 77 for _, arg := range args { | |
| 78 arg = strings.TrimSpace(arg) | |
| 79 | |
| 80 var ( | |
| 81 project cfgtypes.ProjectName | |
| 82 pathBase string | |
| 83 unified bool | |
| 84 ) | |
| 85 if len(arg) > 0 { | |
| 86 // User-friendly: trim any leading or trailing slashes f
rom the path. | |
| 87 var err error | |
| 88 project, pathBase, unified, err = a.splitPath(string(typ
es.StreamPath(arg).Trim())) | |
| 89 if err != nil { | |
| 90 log.WithError(err).Errorf(a, "Invalid path speci
fier.") | |
| 91 return 1 | |
| 92 } | |
| 93 } | |
| 94 | |
| 95 err := coord.List(a, project, pathBase, cmd.o, func(lr *coordina
tor.ListResult) bool { | |
| 96 p := lr.Name | |
| 97 if cmd.o.State { | |
| 98 // Long listing, show full path. | |
| 99 fp := lr.FullPath() | |
| 100 if unified { | |
| 101 p = makeUnifiedPath(lr.Project, fp) | |
| 102 } else { | |
| 103 p = string(fp) | |
| 104 } | |
| 105 } | |
| 106 | |
| 107 if lr.State == nil { | |
| 108 fmt.Fprintf(bio, "%s\n", p) | |
| 109 } else { | |
| 110 fmt.Fprintf(bio, "%s\t[%s]\n", p, cmd.attributes
(lr)) | |
| 111 } | |
| 112 return true | |
| 113 }) | |
| 114 if err != nil { | |
| 115 log.Fields{ | |
| 116 log.ErrorKey: err, | |
| 117 "pathBase": pathBase, | |
| 118 "project": project, | |
| 119 }.Errorf(a, "Failed to list path base.") | |
| 120 return 1 | |
| 121 } | |
| 122 } | |
| 123 return 0 | |
| 124 } | |
| 125 | |
| 126 func (*listCommandRun) attributes(lr *coordinator.ListResult) string { | |
| 127 var parts []string | |
| 128 | |
| 129 if s := lr.State; s != nil { | |
| 130 p := []string{ | |
| 131 google.TimeFromProto(s.Desc.GetTimestamp()).String(), | |
| 132 fmt.Sprintf("type:%s", s.Desc.StreamType.String()), | |
| 133 } | |
| 134 | |
| 135 if t := s.Desc.GetTags(); len(t) > 0 { | |
| 136 tstr := make([]string, 0, len(t)) | |
| 137 for k, v := range t { | |
| 138 if v == "" { | |
| 139 tstr = append(tstr, k) | |
| 140 } else { | |
| 141 tstr = append(tstr, fmt.Sprintf("%s=%s",
k, v)) | |
| 142 } | |
| 143 } | |
| 144 sort.Strings(tstr) | |
| 145 p = append(p, fmt.Sprintf("tags:{%s}", strings.Join(tstr
, ", "))) | |
| 146 } | |
| 147 | |
| 148 parts = append(parts, p...) | |
| 149 | |
| 150 if s.State.TerminalIndex >= 0 { | |
| 151 parts = append(parts, fmt.Sprintf("terminal:%d", s.State
.TerminalIndex)) | |
| 152 } else { | |
| 153 parts = append(parts, "streaming") | |
| 154 } | |
| 155 | |
| 156 if s.State.Archived { | |
| 157 parts = append(parts, "archived") | |
| 158 } | |
| 159 | |
| 160 if s.State.Purged { | |
| 161 parts = append(parts, "purged") | |
| 162 } | |
| 163 } | |
| 164 | |
| 165 return strings.Join(parts, " ") | |
| 166 } | |
| OLD | NEW |