[major] The MIG Command Line Utility

This commit is contained in:
Julien Vehent 2014-11-24 14:40:03 -05:00
Родитель d456497d0b
Коммит bfcdf3b361
3 изменённых файлов: 148 добавлений и 14 удалений

Просмотреть файл

@ -97,7 +97,7 @@ mig-cmd:
if [ ! -r $(AVAILMODS) ]; then echo "$(AGTCONF) configuration file is missing" ; exit 1; fi
cp $(AVAILMODS) src/mig/client/cmd/available_modules.go
$(MKDIR) -p $(BINDIR)
$(GO) build $(GOOPTS) -o $(BINDIR)/mig-cmd $(GOLDFLAGS) mig/client/cmd
$(GO) build $(GOOPTS) -o $(BINDIR)/mig $(GOLDFLAGS) mig/client/cmd
mig-agentsearch:
$(MKDIR) -p $(BINDIR)

Просмотреть файл

@ -8,30 +8,159 @@ package main
import (
"flag"
"fmt"
"mig"
"mig/client"
"os"
"time"
)
func usage() {
fmt.Printf(`%s - Mozilla InvestiGator command line client
usage: %s <module> <global options> <module parameters>
--- Global options ---
-c <path> path to an alternative config file. If not set, use ~/.migrc
-e <duration> time after which the action expires. 60 seconds by default.
example: -e 300s (5 minutes)
-show <mode> type of results to show. if not set, default is 'found'.
* found: only print positive results
* notfound: only print negative results
* all: print all results
-t <target> target to launch the action on. Defaults to all active agents.
examples:
* linux agents: -t "os='linux'"
* agents named *mysql*: -t "name like '%%mysql%%'"
* proxied linux agents: -t "os='linux' AND environment->>'isproxied' = 'true'"
* agents operated by IT: -t "tags#>>'{operator}'='IT'"
--- Modules documentation ---
Each module provides its own set of parameters. Module parameters must be set *after*
global options for the parsing to work correctly. The following modules are available:
`, os.Args[0], os.Args[0])
for module, _ := range mig.AvailableModules {
fmt.Printf("* %s\n", module)
}
fmt.Printf("To access a module documentation, use: %s <module> help\n", os.Args[0])
os.Exit(1)
}
func continueOnFlagError() {
return
}
func main() {
var err error
var (
err error
op mig.Operation
a mig.Action
migrc, show, target, expiration string
modargs []string
)
defer func() {
if e := recover(); e != nil {
err = fmt.Errorf("FATAL: %v", e)
}
}()
homedir := client.FindHomedir()
// command line options
var config = flag.String("c", homedir+"/.migrc", "Load configuration from file")
var aid = flag.Float64("aid", float64(1234567890), "Retrieve and print action by ID")
flag.Parse()
fs := flag.NewFlagSet("mig flag", flag.ContinueOnError)
fs.Usage = continueOnFlagError
fs.StringVar(&migrc, "c", homedir+"/.migrc", "alternative configuration file")
fs.StringVar(&show, "show", "found", "type of results to show")
fs.StringVar(&target, "t", `status='online'`, "action target")
fs.StringVar(&expiration, "e", "60s", "expiration")
// if first argument is missing, or is help, print help
// otherwise, pass the remainder of the arguments to the module for parsing
// this client is agnostic to module parameters
if len(os.Args) < 2 || os.Args[1] == "help" || os.Args[1] == "-h" || os.Args[1] == "--help" {
usage()
}
// arguments parsing works as follow:
// * os.Args[1] must contain the name of the module to launch. we first verify
// that a module exist for this name and then continue parsing
// * os.Args[2:] contains both global options and module parameters. We parse the
// whole []string to extract global options, and module parameters will be left
// unparsed in fs.Args()
// * fs.Args() with the module parameters is passed as a string to the module parser
// which will return a module operation to store in the action
op.Module = os.Args[1]
if _, ok := mig.AvailableModules[op.Module]; !ok {
panic("Unknown module " + op.Module)
}
err = fs.Parse(os.Args[2:])
if err != nil {
// ignore the flag not defined error, which is expected because
// module parameters are defined in modules and not in main
if len(err.Error()) > 30 && err.Error()[0:29] == "flag provided but not defined" {
// requeue the parameter that failed
modargs = append(modargs, err.Error()[31:])
} else {
// if it's another error, panic
panic(err)
}
}
for _, arg := range fs.Args() {
modargs = append(modargs, arg)
}
modRunner := mig.AvailableModules[op.Module]()
if _, ok := modRunner.(mig.HasParamsParser); !ok {
fmt.Fprintf(os.Stderr, "[error] module '%s' does not support command line invocation\n", op.Module)
os.Exit(2)
}
op.Parameters, err = modRunner.(mig.HasParamsParser).ParamsParser(modargs)
if err != nil {
panic(err)
}
a.Operations = append(a.Operations, op)
// instanciate an API client
conf, err := client.ReadConfiguration(*config)
conf, err := client.ReadConfiguration(migrc)
if err != nil {
panic(err)
}
cli := client.NewClient(conf)
if *aid != float64(1234567890) {
a, _, err := cli.GetAction(*aid)
if err != nil {
fmt.Println(err)
} else {
fmt.Printf("%.0f; %s; %s; %s\n", a.ID, a.Name, a.Target, a.Status)
}
a.Name = op.Module + " on '" + target + "'"
a.Target = target
// set the validity 60 second in the past to deal with clock skew
a.ValidFrom = time.Now().Add(-60 * time.Second).UTC()
period, err := time.ParseDuration(expiration)
a.ExpireAfter = a.ValidFrom.Add(period)
// add extra 60 seconds taken for clock skew
a.ExpireAfter = a.ExpireAfter.Add(60 * time.Second).UTC()
asig, err := cli.SignAction(a)
if err != nil {
panic(err)
}
a = asig
// evaluate target before launch, give a change to cancel before going out to agents
agents, err := cli.EvaluateAgentTarget(a.Target)
if err != nil {
panic(err)
}
fmt.Fprintf(os.Stderr, "%d agents will be targeted. ctrl+c to cancel. launching in ", len(agents))
for i := 5; i > 0; i-- {
time.Sleep(1 * time.Second)
fmt.Fprintf(os.Stderr, "%d ", i)
}
fmt.Fprintf(os.Stderr, "GO\n")
// launch and follow
a, err = cli.PostAction(a)
if err != nil {
panic(err)
}
err = cli.FollowAction(a)
if err != nil {
panic(err)
}
err = cli.PrintActionResults(a, show)
if err != nil {
panic(err)
}
}

Просмотреть файл

@ -62,3 +62,8 @@ type HasResultsPrinter interface {
type HasParamsCreator interface {
ParamsCreator() (interface{}, error)
}
// HasParamsParser implements a function that parses command line parameters
type HasParamsParser interface {
ParamsParser([]string) (interface{}, error)
}