commit
a2a543114e
|
@ -325,6 +325,11 @@ func NewClient(config *Config) (*Client, error) {
|
|||
return client, nil
|
||||
}
|
||||
|
||||
// Address return the address of the Nomad agent
|
||||
func (c *Client) Address() string {
|
||||
return c.config.Address
|
||||
}
|
||||
|
||||
// SetRegion sets the region to forward API requests to.
|
||||
func (c *Client) SetRegion(region string) {
|
||||
c.config.Region = region
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
//+build linux
|
||||
// +build linux
|
||||
|
||||
package driver
|
||||
|
||||
|
|
|
@ -1,3 +1,5 @@
|
|||
// +build linux
|
||||
|
||||
package driver
|
||||
|
||||
import (
|
||||
|
|
|
@ -33,7 +33,7 @@ func (c *StatusCommand) Synopsis() string {
|
|||
}
|
||||
|
||||
func (c *StatusCommand) AutocompleteFlags() complete.Flags {
|
||||
return mergeAutocompleteFlags(c.Meta.AutocompleteFlags(FlagSetClient), nil)
|
||||
return c.Meta.AutocompleteFlags(FlagSetClient)
|
||||
}
|
||||
|
||||
func (c *StatusCommand) AutocompleteArgs() complete.Predictor {
|
||||
|
|
184
command/ui.go
Normal file
184
command/ui.go
Normal file
|
@ -0,0 +1,184 @@
|
|||
package command
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"net/url"
|
||||
"strings"
|
||||
|
||||
"github.com/hashicorp/nomad/api/contexts"
|
||||
"github.com/posener/complete"
|
||||
"github.com/skratchdot/open-golang/open"
|
||||
)
|
||||
|
||||
var (
|
||||
// uiContexts is the contexts the ui can open automatically.
|
||||
uiContexts = []contexts.Context{contexts.Jobs, contexts.Allocs, contexts.Nodes}
|
||||
)
|
||||
|
||||
type UiCommand struct {
|
||||
Meta
|
||||
}
|
||||
|
||||
func (c *UiCommand) Help() string {
|
||||
helpText := `
|
||||
Usage: nomad ui [options] <identifier>
|
||||
|
||||
Open the Nomad Web UI in the default browser. An optional identifier may be
|
||||
provided in which case the UI will be opened to view the details for that
|
||||
object. Supported identifiers are jobs, allocations and nodes.
|
||||
|
||||
General Options:
|
||||
|
||||
` + generalOptionsUsage()
|
||||
|
||||
return strings.TrimSpace(helpText)
|
||||
}
|
||||
|
||||
func (c *UiCommand) AutocompleteFlags() complete.Flags {
|
||||
return c.Meta.AutocompleteFlags(FlagSetClient)
|
||||
}
|
||||
|
||||
func (c *UiCommand) AutocompleteArgs() complete.Predictor {
|
||||
return complete.PredictFunc(func(a complete.Args) []string {
|
||||
client, err := c.Meta.Client()
|
||||
if err != nil {
|
||||
return nil
|
||||
}
|
||||
|
||||
resp, _, err := client.Search().PrefixSearch(a.Last, contexts.All, nil)
|
||||
if err != nil {
|
||||
return []string{}
|
||||
}
|
||||
|
||||
final := make([]string, 0)
|
||||
|
||||
for _, allowed := range uiContexts {
|
||||
matches, ok := resp.Matches[allowed]
|
||||
if !ok {
|
||||
continue
|
||||
}
|
||||
if len(matches) == 0 {
|
||||
continue
|
||||
}
|
||||
|
||||
final = append(final, matches...)
|
||||
}
|
||||
|
||||
return final
|
||||
})
|
||||
}
|
||||
|
||||
func (c *UiCommand) Synopsis() string {
|
||||
return "Open the Nomad Web UI"
|
||||
}
|
||||
|
||||
func (c *UiCommand) Run(args []string) int {
|
||||
flags := c.Meta.FlagSet("deployment list", FlagSetClient)
|
||||
flags.Usage = func() { c.Ui.Output(c.Help()) }
|
||||
|
||||
if err := flags.Parse(args); err != nil {
|
||||
return 1
|
||||
}
|
||||
|
||||
// Check that we got no more than one argument
|
||||
args = flags.Args()
|
||||
if l := len(args); l > 1 {
|
||||
c.Ui.Error(c.Help())
|
||||
return 1
|
||||
}
|
||||
|
||||
// Get the HTTP client
|
||||
client, err := c.Meta.Client()
|
||||
if err != nil {
|
||||
c.Ui.Error(fmt.Sprintf("Error initializing client: %s", err))
|
||||
return 1
|
||||
}
|
||||
|
||||
url, err := url.Parse(client.Address())
|
||||
if err != nil {
|
||||
c.Ui.Error(fmt.Sprintf("Error parsing Nomad address %q: %s", client.Address(), err))
|
||||
return 1
|
||||
}
|
||||
|
||||
// We were given an id so look it up
|
||||
if len(args) == 1 {
|
||||
id := args[0]
|
||||
|
||||
// Query for the context associated with the id
|
||||
res, _, err := client.Search().PrefixSearch(id, contexts.All, nil)
|
||||
if err != nil {
|
||||
c.Ui.Error(fmt.Sprintf("Error querying search with id: %q", err))
|
||||
return 1
|
||||
}
|
||||
|
||||
if res.Matches == nil {
|
||||
c.Ui.Error(fmt.Sprintf("No matches returned for query: %q", err))
|
||||
return 1
|
||||
}
|
||||
|
||||
var match contexts.Context
|
||||
var fullID string
|
||||
matchCount := 0
|
||||
for _, ctx := range uiContexts {
|
||||
vers, ok := res.Matches[ctx]
|
||||
if !ok {
|
||||
continue
|
||||
}
|
||||
|
||||
if l := len(vers); l == 1 {
|
||||
match = ctx
|
||||
fullID = vers[0]
|
||||
matchCount++
|
||||
} else if l > 0 && vers[0] == id {
|
||||
// Exact match
|
||||
match = ctx
|
||||
fullID = vers[0]
|
||||
break
|
||||
}
|
||||
|
||||
// Only a single result should return, as this is a match against a full id
|
||||
if matchCount > 1 || len(vers) > 1 {
|
||||
c.logMultiMatchError(id, res.Matches)
|
||||
return 1
|
||||
}
|
||||
}
|
||||
|
||||
switch match {
|
||||
case contexts.Nodes:
|
||||
url.Path = fmt.Sprintf("ui/nodes/%s", fullID)
|
||||
case contexts.Allocs:
|
||||
url.Path = fmt.Sprintf("ui/allocations/%s", fullID)
|
||||
case contexts.Jobs:
|
||||
url.Path = fmt.Sprintf("ui/jobs/%s", fullID)
|
||||
default:
|
||||
c.Ui.Error(fmt.Sprintf("Unable to resolve ID: %q", id))
|
||||
return 1
|
||||
}
|
||||
}
|
||||
|
||||
c.Ui.Output(fmt.Sprintf("Opening URL %q", url.String()))
|
||||
if err := open.Start(url.String()); err != nil {
|
||||
c.Ui.Error(fmt.Sprintf("Error opening URL: %s", err))
|
||||
return 1
|
||||
}
|
||||
|
||||
return 0
|
||||
}
|
||||
|
||||
// logMultiMatchError is used to log an error message when multiple matches are
|
||||
// found. The error message logged displays the matched IDs per context.
|
||||
func (c *UiCommand) logMultiMatchError(id string, matches map[contexts.Context][]string) {
|
||||
c.Ui.Error(fmt.Sprintf("Multiple matches found for id %q", id))
|
||||
for _, ctx := range uiContexts {
|
||||
vers, ok := matches[ctx]
|
||||
if !ok {
|
||||
continue
|
||||
}
|
||||
if len(vers) == 0 {
|
||||
continue
|
||||
}
|
||||
|
||||
c.Ui.Error(fmt.Sprintf("\n%s:", strings.Title(string(ctx))))
|
||||
c.Ui.Error(fmt.Sprintf("%s", strings.Join(vers, ", ")))
|
||||
}
|
||||
}
|
|
@ -334,6 +334,11 @@ func Commands(metaPtr *command.Meta) map[string]cli.CommandFactory {
|
|||
Meta: meta,
|
||||
}, nil
|
||||
},
|
||||
"ui": func() (cli.Command, error) {
|
||||
return &command.UiCommand{
|
||||
Meta: meta,
|
||||
}, nil
|
||||
},
|
||||
"validate": func() (cli.Command, error) {
|
||||
return &command.ValidateCommand{
|
||||
Meta: meta,
|
||||
|
|
18
vendor/github.com/skratchdot/open-golang/open/exec.go
generated
vendored
Normal file
18
vendor/github.com/skratchdot/open-golang/open/exec.go
generated
vendored
Normal file
|
@ -0,0 +1,18 @@
|
|||
// +build !windows,!darwin
|
||||
|
||||
package open
|
||||
|
||||
import (
|
||||
"os/exec"
|
||||
)
|
||||
|
||||
// http://sources.debian.net/src/xdg-utils/1.1.0~rc1%2Bgit20111210-7.1/scripts/xdg-open/
|
||||
// http://sources.debian.net/src/xdg-utils/1.1.0~rc1%2Bgit20111210-7.1/scripts/xdg-mime/
|
||||
|
||||
func open(input string) *exec.Cmd {
|
||||
return exec.Command("xdg-open", input)
|
||||
}
|
||||
|
||||
func openWith(input string, appName string) *exec.Cmd {
|
||||
return exec.Command(appName, input)
|
||||
}
|
15
vendor/github.com/skratchdot/open-golang/open/exec_darwin.go
generated
vendored
Normal file
15
vendor/github.com/skratchdot/open-golang/open/exec_darwin.go
generated
vendored
Normal file
|
@ -0,0 +1,15 @@
|
|||
// +build darwin
|
||||
|
||||
package open
|
||||
|
||||
import (
|
||||
"os/exec"
|
||||
)
|
||||
|
||||
func open(input string) *exec.Cmd {
|
||||
return exec.Command("open", input)
|
||||
}
|
||||
|
||||
func openWith(input string, appName string) *exec.Cmd {
|
||||
return exec.Command("open", "-a", appName, input)
|
||||
}
|
28
vendor/github.com/skratchdot/open-golang/open/exec_windows.go
generated
vendored
Normal file
28
vendor/github.com/skratchdot/open-golang/open/exec_windows.go
generated
vendored
Normal file
|
@ -0,0 +1,28 @@
|
|||
// +build windows
|
||||
|
||||
package open
|
||||
|
||||
import (
|
||||
"os"
|
||||
"os/exec"
|
||||
"path/filepath"
|
||||
"strings"
|
||||
)
|
||||
|
||||
var (
|
||||
cmd = "url.dll,FileProtocolHandler"
|
||||
runDll32 = filepath.Join(os.Getenv("SYSTEMROOT"), "System32", "rundll32.exe")
|
||||
)
|
||||
|
||||
func cleaninput(input string) string {
|
||||
r := strings.NewReplacer("&", "^&")
|
||||
return r.Replace(input)
|
||||
}
|
||||
|
||||
func open(input string) *exec.Cmd {
|
||||
return exec.Command(runDll32, cmd, input)
|
||||
}
|
||||
|
||||
func openWith(input string, appName string) *exec.Cmd {
|
||||
return exec.Command("cmd", "/C", "start", "", appName, cleaninput(input))
|
||||
}
|
50
vendor/github.com/skratchdot/open-golang/open/open.go
generated
vendored
Normal file
50
vendor/github.com/skratchdot/open-golang/open/open.go
generated
vendored
Normal file
|
@ -0,0 +1,50 @@
|
|||
/*
|
||||
|
||||
Open a file, directory, or URI using the OS's default
|
||||
application for that object type. Optionally, you can
|
||||
specify an application to use.
|
||||
|
||||
This is a proxy for the following commands:
|
||||
|
||||
OSX: "open"
|
||||
Windows: "start"
|
||||
Linux/Other: "xdg-open"
|
||||
|
||||
This is a golang port of the node.js module: https://github.com/pwnall/node-open
|
||||
|
||||
*/
|
||||
package open
|
||||
|
||||
/*
|
||||
Open a file, directory, or URI using the OS's default
|
||||
application for that object type. Wait for the open
|
||||
command to complete.
|
||||
*/
|
||||
func Run(input string) error {
|
||||
return open(input).Run()
|
||||
}
|
||||
|
||||
/*
|
||||
Open a file, directory, or URI using the OS's default
|
||||
application for that object type. Don't wait for the
|
||||
open command to complete.
|
||||
*/
|
||||
func Start(input string) error {
|
||||
return open(input).Start()
|
||||
}
|
||||
|
||||
/*
|
||||
Open a file, directory, or URI using the specified application.
|
||||
Wait for the open command to complete.
|
||||
*/
|
||||
func RunWith(input string, appName string) error {
|
||||
return openWith(input, appName).Run()
|
||||
}
|
||||
|
||||
/*
|
||||
Open a file, directory, or URI using the specified application.
|
||||
Don't wait for the open command to complete.
|
||||
*/
|
||||
func StartWith(input string, appName string) error {
|
||||
return openWith(input, appName).Start()
|
||||
}
|
6
vendor/vendor.json
vendored
6
vendor/vendor.json
vendored
|
@ -1375,6 +1375,12 @@
|
|||
"revision": "bb4de0191aa41b5507caa14b0650cdbddcd9280b",
|
||||
"revisionTime": "2016-09-30T03:27:40Z"
|
||||
},
|
||||
{
|
||||
"checksumSHA1": "h/HMhokbQHTdLUbruoBBTee+NYw=",
|
||||
"path": "github.com/skratchdot/open-golang/open",
|
||||
"revision": "75fb7ed4208cf72d323d7d02fd1a5964a7a9073c",
|
||||
"revisionTime": "2016-03-02T14:40:31Z"
|
||||
},
|
||||
{
|
||||
"checksumSHA1": "Q52Y7t0lEtk/wcDn5q7tS7B+jqs=",
|
||||
"path": "github.com/spf13/pflag",
|
||||
|
|
Loading…
Reference in a new issue