aboutsummaryrefslogtreecommitdiff
path: root/dashboard/cmd
diff options
context:
space:
mode:
Diffstat (limited to 'dashboard/cmd')
-rw-r--r--dashboard/cmd/buildlet/Makefile12
-rw-r--r--dashboard/cmd/buildlet/buildlet.go65
-rw-r--r--dashboard/cmd/buildlet/stage0/Makefile2
-rw-r--r--dashboard/cmd/coordinator/Makefile2
-rw-r--r--dashboard/cmd/coordinator/coordinator.go2
-rw-r--r--dashboard/cmd/gomote/auth.go87
-rw-r--r--dashboard/cmd/gomote/create.go67
-rw-r--r--dashboard/cmd/gomote/destroy.go42
-rw-r--r--dashboard/cmd/gomote/gomote.go105
-rw-r--r--dashboard/cmd/gomote/list.go57
-rw-r--r--dashboard/cmd/gomote/put.go83
-rw-r--r--dashboard/cmd/gomote/run.go42
-rw-r--r--dashboard/cmd/upload/upload.go21
13 files changed, 556 insertions, 31 deletions
diff --git a/dashboard/cmd/buildlet/Makefile b/dashboard/cmd/buildlet/Makefile
index 833a17c..078a4e9 100644
--- a/dashboard/cmd/buildlet/Makefile
+++ b/dashboard/cmd/buildlet/Makefile
@@ -3,24 +3,24 @@ buildlet: buildlet.go
buildlet.linux-amd64: buildlet.go
GOOS=linux GOARCH=amd64 go build -o $@ --tags=extdep
- cat $@ | (cd ../upload && go run upload.go --public go-builder-data/$@)
+ cat $@ | (cd ../upload && go run --tags=extdep upload.go --public go-builder-data/$@)
buildlet.openbsd-amd64: buildlet.go
GOOS=openbsd GOARCH=amd64 go build -o $@ --tags=extdep
- cat $@ | (cd ../upload && go run upload.go --public go-builder-data/$@)
+ cat $@ | (cd ../upload && go run --tags=extdep upload.go --public go-builder-data/$@)
buildlet.plan9-386: buildlet.go
GOOS=plan9 GOARCH=386 go build -o $@ --tags=extdep
- cat $@ | (cd ../upload && go run upload.go --public go-builder-data/$@)
+ cat $@ | (cd ../upload && go run --tags=extdep upload.go --public go-builder-data/$@)
buildlet.windows-amd64: buildlet.go
GOOS=windows GOARCH=amd64 go build -o $@ --tags=extdep
- cat $@ | (cd ../upload && go run upload.go --public go-builder-data/$@)
+ cat $@ | (cd ../upload && go run --tags=extdep upload.go --public go-builder-data/$@)
buildlet.darwin-amd64: buildlet.go
GOOS=darwin GOARCH=amd64 go build -o $@ --tags=extdep
- cat $@ | (cd ../upload && go run upload.go --public go-builder-data/$@)
+ cat $@ | (cd ../upload && go run --tags=extdep upload.go --public go-builder-data/$@)
buildlet.netbsd-amd64: buildlet.go
GOOS=netbsd GOARCH=amd64 go build -o $@ --tags=extdep
- cat $@ | (cd ../upload && go run upload.go --public go-builder-data/$@)
+ cat $@ | (cd ../upload && go run --tags=extdep upload.go --public go-builder-data/$@)
diff --git a/dashboard/cmd/buildlet/buildlet.go b/dashboard/cmd/buildlet/buildlet.go
index 29c60ee..63ec7f4 100644
--- a/dashboard/cmd/buildlet/buildlet.go
+++ b/dashboard/cmd/buildlet/buildlet.go
@@ -18,6 +18,7 @@ import (
"archive/tar"
"compress/gzip"
"crypto/tls"
+ "errors"
"flag"
"fmt"
"io"
@@ -37,8 +38,9 @@ import (
)
var (
- scratchDir = flag.String("scratchdir", "", "Temporary directory to use. The contents of this directory may be deleted at any time. If empty, TempDir is used to create one.")
- listenAddr = flag.String("listen", defaultListenAddr(), "address to listen on. Warning: this service is inherently insecure and offers no protection of its own. Do not expose this port to the world.")
+ haltEntireOS = flag.Bool("halt", true, "halt OS in /halt handler. If false, the buildlet process just ends.")
+ scratchDir = flag.String("scratchdir", "", "Temporary directory to use. The contents of this directory may be deleted at any time. If empty, TempDir is used to create one.")
+ listenAddr = flag.String("listen", defaultListenAddr(), "address to listen on. Warning: this service is inherently insecure and offers no protection of its own. Do not expose this port to the world.")
)
func defaultListenAddr() string {
@@ -59,6 +61,8 @@ func defaultListenAddr() string {
return ":80"
}
+var osHalt func() // set by some machines
+
func main() {
flag.Parse()
if !metadata.OnGCE() && !strings.HasPrefix(*listenAddr, "localhost:") {
@@ -87,8 +91,12 @@ func main() {
http.HandleFunc("/", handleRoot)
password := metadataValue("password")
- http.Handle("/writetgz", requirePassword{http.HandlerFunc(handleWriteTGZ), password})
- http.Handle("/exec", requirePassword{http.HandlerFunc(handleExec), password})
+ requireAuth := func(handler func(w http.ResponseWriter, r *http.Request)) http.Handler {
+ return requirePasswordHandler{http.HandlerFunc(handler), password}
+ }
+ http.Handle("/writetgz", requireAuth(handleWriteTGZ))
+ http.Handle("/exec", requireAuth(handleExec))
+ http.Handle("/halt", requireAuth(handleHalt))
// TODO: removeall
tlsCert, tlsKey := metadataValue("tls-cert"), metadataValue("tls-key")
@@ -293,6 +301,51 @@ func handleExec(w http.ResponseWriter, r *http.Request) {
log.Printf("Run = %s", state)
}
+func handleHalt(w http.ResponseWriter, r *http.Request) {
+ if r.Method != "POST" {
+ http.Error(w, "requires POST method", http.StatusBadRequest)
+ return
+ }
+ log.Printf("Halting in 1 second.")
+ // do the halt in 1 second, to give the HTTP response time to complete:
+ time.AfterFunc(1*time.Second, haltMachine)
+}
+
+func haltMachine() {
+ if !*haltEntireOS {
+ log.Printf("Ending buildlet process due to halt.")
+ os.Exit(0)
+ return
+ }
+ log.Printf("Halting machine.")
+ time.AfterFunc(5*time.Second, func() { os.Exit(0) })
+ if osHalt != nil {
+ // TODO: Windows: http://msdn.microsoft.com/en-us/library/windows/desktop/aa376868%28v=vs.85%29.aspx
+ osHalt()
+ os.Exit(0)
+ }
+ // Backup mechanism, if exec hangs for any reason:
+ var err error
+ switch runtime.GOOS {
+ case "openbsd":
+ // Quick, no fs flush, and power down:
+ err = exec.Command("halt", "-q", "-n", "-p").Run()
+ case "freebsd":
+ // Power off (-p), via halt (-o), now.
+ err = exec.Command("shutdown", "-p", "-o", "now").Run()
+ case "linux":
+ // Don't sync (-n), force without shutdown (-f), and power off (-p).
+ err = exec.Command("/bin/halt", "-n", "-f", "-p").Run()
+ case "plan9":
+ err = exec.Command("fshalt").Run()
+ default:
+ err = errors.New("No system-specific halt command run; will just end buildlet process.")
+ }
+ log.Printf("Shutdown: %v", err)
+ log.Printf("Ending buildlet process post-halt")
+ os.Exit(0)
+}
+
// flushWriter is an io.Writer wrapper that writes to w and
// Flushes the output immediately, if w is an http.Flusher.
type flushWriter struct {
@@ -336,12 +389,12 @@ func badRequest(msg string) error {
// requirePassword is an http.Handler auth wrapper that enforces a
// HTTP Basic password. The username is ignored.
-type requirePassword struct {
+type requirePasswordHandler struct {
h http.Handler
password string // empty means no password
}
-func (h requirePassword) ServeHTTP(w http.ResponseWriter, r *http.Request) {
+func (h requirePasswordHandler) ServeHTTP(w http.ResponseWriter, r *http.Request) {
_, gotPass, _ := r.BasicAuth()
if h.password != "" && h.password != gotPass {
http.Error(w, "invalid password", http.StatusForbidden)
diff --git a/dashboard/cmd/buildlet/stage0/Makefile b/dashboard/cmd/buildlet/stage0/Makefile
index b69cacf..bf66be3 100644
--- a/dashboard/cmd/buildlet/stage0/Makefile
+++ b/dashboard/cmd/buildlet/stage0/Makefile
@@ -1,3 +1,3 @@
buildlet-stage0.windows-amd64: stage0.go
GOOS=windows GOARCH=amd64 go build -o $@ --tags=extdep
- cat $@ | (cd ../../upload && go run upload.go --public go-builder-data/$@)
+ cat $@ | (cd ../../upload && go run --tags=extdep upload.go --public go-builder-data/$@)
diff --git a/dashboard/cmd/coordinator/Makefile b/dashboard/cmd/coordinator/Makefile
index d004fa8..56887c0 100644
--- a/dashboard/cmd/coordinator/Makefile
+++ b/dashboard/cmd/coordinator/Makefile
@@ -6,4 +6,4 @@ coordinator: coordinator.go
# And watch its logs with:
# sudo journalctl -f -u gobuild.service
upload: coordinator
- cat coordinator | (cd ../upload && go run upload.go --public go-builder-data/coordinator)
+ cat coordinator | (cd ../upload && go run --tags=extdep upload.go --public go-builder-data/coordinator)
diff --git a/dashboard/cmd/coordinator/coordinator.go b/dashboard/cmd/coordinator/coordinator.go
index e21f7f7..900b848 100644
--- a/dashboard/cmd/coordinator/coordinator.go
+++ b/dashboard/cmd/coordinator/coordinator.go
@@ -1113,7 +1113,7 @@ func hasComputeScope() bool {
return false
}
for _, v := range scopes {
- if v == compute.DevstorageFull_controlScope {
+ if v == compute.ComputeScope {
return true
}
}
diff --git a/dashboard/cmd/gomote/auth.go b/dashboard/cmd/gomote/auth.go
new file mode 100644
index 0000000..56ca0d4
--- /dev/null
+++ b/dashboard/cmd/gomote/auth.go
@@ -0,0 +1,87 @@
+// Copyright 2015 The Go Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style
+// license that can be found in the LICENSE file.
+
+// +build extdep
+
+package main
+
+import (
+ "io/ioutil"
+ "log"
+ "os"
+ "path/filepath"
+ "runtime"
+
+ "golang.org/x/oauth2"
+ "golang.org/x/tools/dashboard/auth"
+ "golang.org/x/tools/dashboard/buildlet"
+ "google.golang.org/api/compute/v1"
+)
+
+func username() string {
+ if runtime.GOOS == "windows" {
+ return os.Getenv("USERNAME")
+ }
+ return os.Getenv("USER")
+}
+
+func homeDir() string {
+ if runtime.GOOS == "windows" {
+ return os.Getenv("HOMEDRIVE") + os.Getenv("HOMEPATH")
+ }
+ return os.Getenv("HOME")
+}
+
+func configDir() string {
+ if runtime.GOOS == "windows" {
+ return filepath.Join(os.Getenv("APPDATA"), "Gomote")
+ }
+ if xdg := os.Getenv("XDG_CONFIG_HOME"); xdg != "" {
+ return filepath.Join(xdg, "gomote")
+ }
+ return filepath.Join(homeDir(), ".config", "gomote")
+}
+
+func projTokenSource() oauth2.TokenSource {
+ ts, err := auth.ProjectTokenSource(*proj, compute.ComputeScope)
+ if err != nil {
+ log.Fatalf("Failed to get OAuth2 token source for project %s: %v", *proj, err)
+ }
+ return ts
+}
+
+func userKeyPair() buildlet.KeyPair {
+ keyDir := configDir()
+ crtFile := filepath.Join(keyDir, "gomote.crt")
+ keyFile := filepath.Join(keyDir, "gomote.key")
+ _, crtErr := os.Stat(crtFile)
+ _, keyErr := os.Stat(keyFile)
+ if crtErr == nil && keyErr == nil {
+ return buildlet.KeyPair{
+ CertPEM: slurpString(crtFile),
+ KeyPEM: slurpString(keyFile),
+ }
+ }
+ check := func(what string, err error) {
+ if err != nil {
+ log.Printf("%s: %v", what, err)
+ }
+ }
+ check("making key dir", os.MkdirAll(keyDir, 0700))
+ kp, err := buildlet.NewKeyPair()
+ if err != nil {
+ log.Fatalf("Error generating new key pair: %v", err)
+ }
+ check("writing cert file: ", ioutil.WriteFile(crtFile, []byte(kp.CertPEM), 0600))
+ check("writing key file: ", ioutil.WriteFile(keyFile, []byte(kp.KeyPEM), 0600))
+ return kp
+}
+
+func slurpString(f string) string {
+ slurp, err := ioutil.ReadFile(f)
+ if err != nil {
+ log.Fatal(err)
+ }
+ return string(slurp)
+}
diff --git a/dashboard/cmd/gomote/create.go b/dashboard/cmd/gomote/create.go
new file mode 100644
index 0000000..bb25a8e
--- /dev/null
+++ b/dashboard/cmd/gomote/create.go
@@ -0,0 +1,67 @@
+// Copyright 2015 The Go Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style
+// license that can be found in the LICENSE file.
+
+// +build extdep
+
+package main
+
+import (
+ "flag"
+ "fmt"
+ "log"
+ "os"
+ "sort"
+ "time"
+
+ "golang.org/x/tools/dashboard"
+ "golang.org/x/tools/dashboard/buildlet"
+)
+
+func create(args []string) error {
+ fs := flag.NewFlagSet("create", flag.ContinueOnError)
+ fs.Usage = func() {
+ fmt.Fprintln(os.Stderr, "create usage: gomote create [create-opts] <type>\n\n")
+ fs.PrintDefaults()
+ os.Exit(1)
+ }
+ var timeout time.Duration
+ fs.DurationVar(&timeout, "timeout", 60*time.Minute, "how long the VM will live before being deleted.")
+
+ fs.Parse(args)
+ if fs.NArg() != 1 {
+ fs.Usage()
+ }
+ builderType := fs.Arg(0)
+ conf, ok := dashboard.Builders[builderType]
+ if !ok || !conf.UsesVM() {
+ var valid []string
+ for k, conf := range dashboard.Builders {
+ if conf.UsesVM() {
+ valid = append(valid, k)
+ }
+ }
+ sort.Strings(valid)
+ return fmt.Errorf("Invalid builder type %q. Valid options include: %q", builderType, valid)
+ }
+
+ instName := fmt.Sprintf("mote-%s-%s", username(), builderType)
+ client, err := buildlet.StartNewVM(projTokenSource(), instName, builderType, buildlet.VMOpts{
+ Zone: *zone,
+ ProjectID: *proj,
+ TLS: userKeyPair(),
+ DeleteIn: timeout,
+ Description: fmt.Sprintf("gomote buildlet for %s", username()),
+ OnInstanceRequested: func() {
+ log.Printf("Sent create request. Waiting for operation.")
+ },
+ OnInstanceCreated: func() {
+ log.Printf("Instance created.")
+ },
+ })
+ if err != nil {
+ return fmt.Errorf("failed to create VM: %v", err)
+ }
+ fmt.Printf("%s\t%s\n", builderType, client.URL())
+ return nil
+}
diff --git a/dashboard/cmd/gomote/destroy.go b/dashboard/cmd/gomote/destroy.go
new file mode 100644
index 0000000..ca9b4de
--- /dev/null
+++ b/dashboard/cmd/gomote/destroy.go
@@ -0,0 +1,42 @@
+// Copyright 2015 The Go Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style
+// license that can be found in the LICENSE file.
+
+// +build extdep
+
+package main
+
+import (
+ "flag"
+ "fmt"
+ "os"
+
+ "golang.org/x/tools/dashboard/buildlet"
+)
+
+func destroy(args []string) error {
+ fs := flag.NewFlagSet("destroy", flag.ContinueOnError)
+ fs.Usage = func() {
+ fmt.Fprintln(os.Stderr, "create usage: gomote destroy <instance>\n\n")
+ fs.PrintDefaults()
+ os.Exit(1)
+ }
+
+ fs.Parse(args)
+ if fs.NArg() != 1 {
+ fs.Usage()
+ }
+ name := fs.Arg(0)
+ bc, err := namedClient(name)
+ if err != nil {
+ return err
+ }
+
+ // First ask it to kill itself, and then tell GCE to kill it too:
+ shutErr := bc.Destroy()
+ gceErr := buildlet.DestroyVM(projTokenSource(), *proj, *zone, fmt.Sprintf("mote-%s-%s", username(), name))
+ if shutErr != nil {
+ return shutErr
+ }
+ return gceErr
+}
diff --git a/dashboard/cmd/gomote/gomote.go b/dashboard/cmd/gomote/gomote.go
new file mode 100644
index 0000000..7ede494
--- /dev/null
+++ b/dashboard/cmd/gomote/gomote.go
@@ -0,0 +1,105 @@
+// Copyright 2015 The Go Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style
+// license that can be found in the LICENSE file.
+
+// +build extdep
+
+/*
+The gomote command is a client for the Go builder infrastructure.
+It's a remote control for remote Go builder machines.
+
+Usage:
+
+ gomote [global-flags] cmd [cmd-flags]
+
+ For example,
+ $ gomote create openbsd-amd64-gce56
+ $ gomote push
+ $ gomote run openbsd-amd64-gce56 src/make.bash
+
+TODO: document more, and figure out the CLI interface more.
+*/
+package main
+
+import (
+ "flag"
+ "fmt"
+ "os"
+ "sort"
+)
+
+var (
+ proj = flag.String("project", "symbolic-datum-552", "GCE project owning builders")
+ zone = flag.String("zone", "us-central1-a", "GCE zone")
+)
+
+type command struct {
+ name string
+ des string
+ run func([]string) error
+}
+
+var commands = map[string]command{}
+
+func sortedCommands() []string {
+ s := make([]string, 0, len(commands))
+ for name := range commands {
+ s = append(s, name)
+ }
+ sort.Strings(s)
+ return s
+}
+
+func usage() {
+ fmt.Fprintf(os.Stderr, `Usage of gomote: gomote [global-flags] <cmd> [cmd-flags]
+
+Global flags:
+`)
+ flag.PrintDefaults()
+ fmt.Fprintf(os.Stderr, "Commands:\n\n")
+ for _, name := range sortedCommands() {
+ fmt.Fprintf(os.Stderr, " %-10s %s\n", name, commands[name].des)
+ }
+ os.Exit(1)
+}
+
+func registerCommand(name, des string, run func([]string) error) {
+ if _, dup := commands[name]; dup {
+ panic("duplicate registration of " + name)
+ }
+ commands[name] = command{
+ name: name,
+ des: des,
+ run: run,
+ }
+}
+
+func registerCommands() {
+ registerCommand("create", "create a buildlet", create)
+ registerCommand("destroy", "destroy a buildlet", destroy)
+ registerCommand("list", "list buildlets", list)
+ registerCommand("run", "run a command on a buildlet", run)
+ registerCommand("put", "put files on a buildlet", put)
+ registerCommand("puttar", "extract a tar.gz to a buildlet", putTar)
+}
+
+func main() {
+ registerCommands()
+ flag.Usage = usage
+ flag.Parse()
+ args := flag.Args()
+ if len(args) == 0 {
+ usage()
+ }
+ cmdName := args[0]
+ cmd, ok := commands[cmdName]
+ if !ok {
+ fmt.Fprintf(os.Stderr, "Unknown command %q\n", cmdName)
+ usage()
+ }
+ err := cmd.run(args[1:])
+ if err != nil {
+ fmt.Fprintf(os.Stderr, "Error running %s: %v\n", cmdName, err)
+ os.Exit(1)
+ }
+}
diff --git a/dashboard/cmd/gomote/list.go b/dashboard/cmd/gomote/list.go
new file mode 100644
index 0000000..cd3d1f8
--- /dev/null
+++ b/dashboard/cmd/gomote/list.go
@@ -0,0 +1,57 @@
+// Copyright 2015 The Go Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style
+// license that can be found in the LICENSE file.
+
+// +build extdep
+
+package main
+
+import (
+ "flag"
+ "fmt"
+ "os"
+ "strings"
+
+ "golang.org/x/tools/dashboard/buildlet"
+)
+
+func list(args []string) error {
+ fs := flag.NewFlagSet("list", flag.ContinueOnError)
+ fs.Usage = func() {
+ fmt.Fprintln(os.Stderr, "list usage: gomote list\n\n")
+ fs.PrintDefaults()
+ os.Exit(1)
+ }
+ fs.Parse(args)
+ if fs.NArg() != 0 {
+ fs.Usage()
+ }
+
+ prefix := fmt.Sprintf("mote-%s-", username())
+ vms, err := buildlet.ListVMs(projTokenSource(), *proj, *zone)
+ if err != nil {
+ return fmt.Errorf("failed to list VMs: %v", err)
+ }
+ for _, vm := range vms {
+ if !strings.HasPrefix(vm.Name, prefix) {
+ continue
+ }
+ fmt.Printf("%s\thttps://%s\n", vm.Type, strings.TrimSuffix(vm.IPPort, ":443"))
+ }
+ return nil
+}
+
+func namedClient(name string) (*buildlet.Client, error) {
+ // TODO(bradfitz): cache the list on disk and avoid the API call?
+ vms, err := buildlet.ListVMs(projTokenSource(), *proj, *zone)
+ if err != nil {
+ return nil, fmt.Errorf("error listing VMs while looking up %q: %v", name, err)
+ }
+ wantName := fmt.Sprintf("mote-%s-%s", username(), name)
+ for _, vm := range vms {
+ if vm.Name == wantName {
+ return buildlet.NewClient(vm.IPPort, vm.TLS), nil
+ }
+ }
+ return nil, fmt.Errorf("buildlet %q not running", name)
+}
diff --git a/dashboard/cmd/gomote/put.go b/dashboard/cmd/gomote/put.go
new file mode 100644
index 0000000..31595c2
--- /dev/null
+++ b/dashboard/cmd/gomote/put.go
@@ -0,0 +1,83 @@
+// Copyright 2015 The Go Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style
+// license that can be found in the LICENSE file.
+
+// +build extdep
+
+package main
+
+import (
+ "flag"
+ "fmt"
+ "io"
+ "net/http"
+ "os"
+)
+
+// put a .tar.gz
+func putTar(args []string) error {
+ fs := flag.NewFlagSet("put", flag.ContinueOnError)
+ fs.Usage = func() {
+ fmt.Fprintln(os.Stderr, "create usage: gomote puttar [put-opts] <buildlet-name> [tar.gz file or '-' for stdin]\n")
+ fs.PrintDefaults()
+ os.Exit(1)
+ }
+ var rev string
+ fs.StringVar(&rev, "gorev", "", "If non-empty, git hash to download from gerrit and put to the buildlet. e.g. 886b02d705ff for Go 1.4.1")
+
+ fs.Parse(args)
+ if fs.NArg() < 1 || fs.NArg() > 2 {
+ fs.Usage()
+ }
+
+ name := fs.Arg(0)
+ bc, err := namedClient(name)
+ if err != nil {
+ return err
+ }
+
+ var tgz io.Reader = os.Stdin
+ if rev != "" {
+ if fs.NArg() != 1 {
+ fs.Usage()
+ }
+ // TODO(bradfitz): tell the buildlet to do this
+ // itself, to avoid network to & from home networks.
+ // Staying Google<->Google will be much faster.
+ res, err := http.Get("https://go.googlesource.com/go/+archive/" + rev + ".tar.gz")
+ if err != nil {
+ return fmt.Errorf("Error fetching rev %s from Gerrit: %v", rev, err)
+ }
+ defer res.Body.Close()
+ if res.StatusCode != 200 {
+ return fmt.Errorf("Error fetching rev %s from Gerrit: %v", rev, res.Status)
+ }
+ tgz = res.Body
+ } else if fs.NArg() == 2 && fs.Arg(1) != "-" {
+ f, err := os.Open(fs.Arg(1))
+ if err != nil {
+ return err
+ }
+ defer f.Close()
+ tgz = f
+ }
+ return bc.PutTarball(tgz)
+}
+
+// put single files
+func put(args []string) error {
+ fs := flag.NewFlagSet("put", flag.ContinueOnError)
+ fs.Usage = func() {
+ fmt.Fprintln(os.Stderr, "create usage: gomote put [put-opts] <type>\n\n")
+ fs.PrintDefaults()
+ os.Exit(1)
+ }
+ fs.Parse(args)
+ if fs.NArg() != 1 {
+ fs.Usage()
+ }
+ return fmt.Errorf("TODO")
+ builderType := fs.Arg(0)
+ _ = builderType
+ return nil
+}
diff --git a/dashboard/cmd/gomote/run.go b/dashboard/cmd/gomote/run.go
new file mode 100644
index 0000000..6be9346
--- /dev/null
+++ b/dashboard/cmd/gomote/run.go
@@ -0,0 +1,42 @@
+// Copyright 2015 The Go Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style
+// license that can be found in the LICENSE file.
+
+// +build extdep
+
+package main
+
+import (
+ "flag"
+ "fmt"
+ "os"
+
+ "golang.org/x/tools/dashboard/buildlet"
+)
+
+func run(args []string) error {
+ fs := flag.NewFlagSet("run", flag.ContinueOnError)
+ fs.Usage = func() {
+ fmt.Fprintln(os.Stderr, "create usage: gomote run [run-opts] <buildlet-name> <cmd> [args...]")
+ fs.PrintDefaults()
+ os.Exit(1)
+ }
+
+ fs.Parse(args)
+ if fs.NArg() < 2 {
+ fs.Usage()
+ }
+ name, cmd := fs.Arg(0), fs.Arg(1)
+ bc, err := namedClient(name)
+ if err != nil {
+ return err
+ }
+
+ remoteErr, execErr := bc.Exec(cmd, buildlet.ExecOpts{
+ Output: os.Stdout,
+ })
+ if execErr != nil {
+ return fmt.Errorf("Error trying to execute %s: %v", cmd, execErr)
+ }
+ return remoteErr
+}
diff --git a/dashboard/cmd/upload/upload.go b/dashboard/cmd/upload/upload.go
index 880b879..087be49 100644
--- a/dashboard/cmd/upload/upload.go
+++ b/dashboard/cmd/upload/upload.go
@@ -15,15 +15,13 @@ import (
"flag"
"fmt"
"io"
- "io/ioutil"
"log"
"net/http"
"os"
- "path/filepath"
"strings"
"golang.org/x/oauth2"
- "golang.org/x/oauth2/google"
+ "golang.org/x/tools/dashboard/auth"
"google.golang.org/cloud"
"google.golang.org/cloud/storage"
)
@@ -111,18 +109,9 @@ var bucketProject = map[string]string{
}
func tokenSource(bucket string) (oauth2.TokenSource, error) {
- proj := bucketProject[bucket]
- fileName := filepath.Join(os.Getenv("HOME"), "keys", proj+".key.json")
- jsonConf, err := ioutil.ReadFile(fileName)
- if err != nil {
- if os.IsNotExist(err) {
- return nil, fmt.Errorf("Missing JSON key configuration. Download the Service Account JSON key from https://console.developers.google.com/project/%s/apiui/credential and place it at %s", proj, fileName)
- }
- return nil, err
- }
- conf, err := google.JWTConfigFromJSON(jsonConf, storage.ScopeReadWrite)
- if err != nil {
- return nil, fmt.Errorf("reading JSON config from %s: %v", fileName, err)
+ proj, ok := bucketProject[bucket]
+ if !ok {
+ return nil, fmt.Errorf("unknown project for bucket %q", bucket)
}
- return conf.TokenSource(oauth2.NoContext), nil
+ return auth.ProjectTokenSource(proj, storage.ScopeReadWrite)
}