// 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.

/*
The gomote command is a client for the Go builder infrastructure.
It's a remote control for remote Go builder machines.

See https://golang.org/wiki/Gomote

Usage:

  gomote [global-flags] cmd [cmd-flags]

  For example,
  $ gomote create openbsd-amd64-60
  user-username-openbsd-amd64-60-0
  $ gomote push user-username-openbsd-amd64-60-0
  $ gomote run user-username-openbsd-amd64-60-0 go/src/make.bash
  $ gomote run user-username-openbsd-amd64-60-0 go/bin/go test -v -short os

To list the subcommands, run "gomote" without arguments:

  Commands:

    create     create a buildlet; with no args, list types of buildlets
    destroy    destroy a buildlet
    gettar     extract a tar.gz from a buildlet
    list       list active buildlets
    ls         list the contents of a directory on a buildlet
    ping       test whether a buildlet is alive and reachable
    push       sync your GOROOT directory to the buildlet
    put        put files on a buildlet
    put14      put Go 1.4 in place
    puttar     extract a tar.gz to a buildlet
    rm         delete files or directories
    rdp        RDP (Remote Desktop Protocol) to a Windows buildlet
    run        run a command on a buildlet
    ssh        ssh to a buildlet

To list all the builder types available, run "create" with no arguments:

  $ gomote create
  (list tons of buildlet types)

The "gomote run" command has many of its own flags:

  $ gomote run -h
  run usage: gomote run [run-opts] <instance> <cmd> [args...]
    -builderenv string
          Optional alternate builder to act like. Must share the same
          underlying buildlet host type, or it's an error. For
          instance, linux-amd64-race or linux-386-387 are compatible
          with linux-amd64, but openbsd-amd64 and openbsd-386 are
          different hosts.
    -debug
          write debug info about the command's execution before it begins
    -dir string
          Directory to run from. Defaults to the directory of the
          command, or the work directory if -system is true.
    -e value
          Environment variable KEY=value. The -e flag may be repeated
          multiple times to add multiple things to the environment.
    -path string
          Comma-separated list of ExecOpts.Path elements. The special
          string 'EMPTY' means to run without any $PATH. The empty
          string (default) does not modify the $PATH. Otherwise, the
          following expansions apply: the string '$PATH' expands to
          the current PATH element(s), the substring '$WORKDIR'
          expands to the buildlet's temp workdir.
    -system
          run inside the system, and not inside the workdir; this is implicit if cmd starts with '/'

Debugging buildlets directly

Using "gomote create" contacts the build coordinator
(farmer.golang.org) and requests that it create the buildlet on your
behalf. All subsequent commands (such as "gomote run" or "gomote ls")
then proxy your request via the coordinator.  To access a buildlet
directly (for example, when working on the buildlet code), you can
skip the "gomote create" step and use the special builder name
"<build-config-name>@ip[:port>", such as "windows-amd64-2008@10.1.5.3".

*/
package main

import (
	"flag"
	"fmt"
	"os"
	"sort"

	"golang.org/x/build/buildenv"
	"golang.org/x/build/buildlet"
)

var (
	buildEnv *buildenv.Environment
)

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; with no args, list types of buildlets", create)
	registerCommand("destroy", "destroy a buildlet", destroy)
	registerCommand("gettar", "extract a tar.gz from a buildlet", getTar)
	registerCommand("ls", "list the contents of a directory on a buildlet", ls)
	registerCommand("list", "list active buildlets", list)
	registerCommand("ping", "test whether a buildlet is alive and reachable ", ping)
	registerCommand("push", "sync your GOROOT directory to the buildlet", push)
	registerCommand("put", "put files on a buildlet", put)
	registerCommand("put14", "put Go 1.4 in place", put14)
	registerCommand("puttar", "extract a tar.gz to a buildlet", putTar)
	registerCommand("rdp", "RDP (Remote Desktop Protocol) to a Windows buildlet", rdp)
	registerCommand("rm", "delete files or directories", rm)
	registerCommand("run", "run a command on a buildlet", run)
	registerCommand("ssh", "ssh to a buildlet", ssh)
}

func main() {
	buildlet.RegisterFlags()
	registerCommands()
	flag.Usage = usage
	flag.Parse()
	buildEnv = buildenv.FromFlags()
	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)
	}
}
