github.com/blixtra/nomad@v0.7.2-0.20171221000451-da9a1d7bb050/command/ui.go (about) 1 package command 2 3 import ( 4 "fmt" 5 "net/url" 6 "strings" 7 8 "github.com/hashicorp/nomad/api/contexts" 9 "github.com/posener/complete" 10 "github.com/skratchdot/open-golang/open" 11 ) 12 13 var ( 14 // uiContexts is the contexts the ui can open automatically. 15 uiContexts = []contexts.Context{contexts.Jobs, contexts.Allocs, contexts.Nodes} 16 ) 17 18 type UiCommand struct { 19 Meta 20 } 21 22 func (c *UiCommand) Help() string { 23 helpText := ` 24 Usage: nomad ui [options] <identifier> 25 26 Open the Nomad Web UI in the default browser. An optional identifier may be 27 provided, in which case the UI will be opened to view the details for that 28 object. Supported identifiers are jobs, allocations and nodes. 29 30 General Options: 31 32 ` + generalOptionsUsage() 33 34 return strings.TrimSpace(helpText) 35 } 36 37 func (c *UiCommand) AutocompleteFlags() complete.Flags { 38 return c.Meta.AutocompleteFlags(FlagSetClient) 39 } 40 41 func (c *UiCommand) AutocompleteArgs() complete.Predictor { 42 return complete.PredictFunc(func(a complete.Args) []string { 43 client, err := c.Meta.Client() 44 if err != nil { 45 return nil 46 } 47 48 resp, _, err := client.Search().PrefixSearch(a.Last, contexts.All, nil) 49 if err != nil { 50 return []string{} 51 } 52 53 final := make([]string, 0) 54 55 for _, allowed := range uiContexts { 56 matches, ok := resp.Matches[allowed] 57 if !ok { 58 continue 59 } 60 if len(matches) == 0 { 61 continue 62 } 63 64 final = append(final, matches...) 65 } 66 67 return final 68 }) 69 } 70 71 func (c *UiCommand) Synopsis() string { 72 return "Open the Nomad Web UI" 73 } 74 75 func (c *UiCommand) Run(args []string) int { 76 flags := c.Meta.FlagSet("deployment list", FlagSetClient) 77 flags.Usage = func() { c.Ui.Output(c.Help()) } 78 79 if err := flags.Parse(args); err != nil { 80 return 1 81 } 82 83 // Check that we got no more than one argument 84 args = flags.Args() 85 if l := len(args); l > 1 { 86 c.Ui.Error(c.Help()) 87 return 1 88 } 89 90 // Get the HTTP client 91 client, err := c.Meta.Client() 92 if err != nil { 93 c.Ui.Error(fmt.Sprintf("Error initializing client: %s", err)) 94 return 1 95 } 96 97 url, err := url.Parse(client.Address()) 98 if err != nil { 99 c.Ui.Error(fmt.Sprintf("Error parsing Nomad address %q: %s", client.Address(), err)) 100 return 1 101 } 102 103 // We were given an id so look it up 104 if len(args) == 1 { 105 id := args[0] 106 107 // Query for the context associated with the id 108 res, _, err := client.Search().PrefixSearch(id, contexts.All, nil) 109 if err != nil { 110 c.Ui.Error(fmt.Sprintf("Error querying search with id: %q", err)) 111 return 1 112 } 113 114 if res.Matches == nil { 115 c.Ui.Error(fmt.Sprintf("No matches returned for query: %q", err)) 116 return 1 117 } 118 119 var match contexts.Context 120 var fullID string 121 matchCount := 0 122 for _, ctx := range uiContexts { 123 vers, ok := res.Matches[ctx] 124 if !ok { 125 continue 126 } 127 128 if l := len(vers); l == 1 { 129 match = ctx 130 fullID = vers[0] 131 matchCount++ 132 } else if l > 0 && vers[0] == id { 133 // Exact match 134 match = ctx 135 fullID = vers[0] 136 break 137 } 138 139 // Only a single result should return, as this is a match against a full id 140 if matchCount > 1 || len(vers) > 1 { 141 c.logMultiMatchError(id, res.Matches) 142 return 1 143 } 144 } 145 146 switch match { 147 case contexts.Nodes: 148 url.Path = fmt.Sprintf("ui/nodes/%s", fullID) 149 case contexts.Allocs: 150 url.Path = fmt.Sprintf("ui/allocations/%s", fullID) 151 case contexts.Jobs: 152 url.Path = fmt.Sprintf("ui/jobs/%s", fullID) 153 default: 154 c.Ui.Error(fmt.Sprintf("Unable to resolve ID: %q", id)) 155 return 1 156 } 157 } 158 159 c.Ui.Output(fmt.Sprintf("Opening URL %q", url.String())) 160 if err := open.Start(url.String()); err != nil { 161 c.Ui.Error(fmt.Sprintf("Error opening URL: %s", err)) 162 return 1 163 } 164 165 return 0 166 } 167 168 // logMultiMatchError is used to log an error message when multiple matches are 169 // found. The error message logged displays the matched IDs per context. 170 func (c *UiCommand) logMultiMatchError(id string, matches map[contexts.Context][]string) { 171 c.Ui.Error(fmt.Sprintf("Multiple matches found for id %q", id)) 172 for _, ctx := range uiContexts { 173 vers, ok := matches[ctx] 174 if !ok { 175 continue 176 } 177 if len(vers) == 0 { 178 continue 179 } 180 181 c.Ui.Error(fmt.Sprintf("\n%s:", strings.Title(string(ctx)))) 182 c.Ui.Error(fmt.Sprintf("%s", strings.Join(vers, ", "))) 183 } 184 }