Merge pull request #1357 from weaveworks/1312-arg-parsing

Move all the flag parsing to main.go; implement `scope help`
This commit is contained in:
Paul Bellamy
2016-04-20 17:03:13 +01:00
7 changed files with 206 additions and 168 deletions

View File

@@ -1,4 +1,4 @@
#!/bin/sh
#!/bin/bash
usage() {
echo "$0 --app.foo bar --probe.foo bar"
@@ -6,89 +6,37 @@ usage() {
}
mkdir -p /var/run/weave
APP_ARGS=""
PROBE_ARGS=""
TOKEN_PROVIDED=false
if [ "$1" = version ]; then
/home/weave/scope version
if [ "$1" = "version" -o "$1" = "help" ]; then
exec -a scope /home/weave/scope --mode $1
exit 0
fi
while true; do
case "$1" in
--app.*)
if echo "$1" | grep "=" 1>/dev/null; then
ARG_NAME=$(echo "$1" | sed 's/\-\-app\.\([^=]*\)=\(.*\)/\1/')
ARG_VALUE=$(echo "$1" | sed 's/\-\-app\.\([^=]*\)=\(.*\)/\2/')
else
[ $# -gt 1 ] || usage
ARG_NAME=$(echo "$1" | sed 's/\-\-app\.//')
ARG_VALUE="$2"
shift
fi
APP_ARGS="$APP_ARGS -$ARG_NAME=$ARG_VALUE"
;;
--debug)
APP_ARGS="$APP_ARGS -log.level=debug"
PROBE_ARGS="$PROBE_ARGS -log.level=debug"
;;
for arg in $@; do
case "$arg" in
--no-app|--probe-only)
touch /etc/service/app/down
;;
--no-probe|--app-only)
touch /etc/service/probe/down
;;
--weave.hostname*)
if echo "$1" | grep "=" 1>/dev/null; then
ARG_VALUE=$(echo "$1" | sed 's/\-\-weave.hostname=\(.*\)/\1/')
else
[ $# -gt 1 ] || usage
ARG_VALUE="$2"
shift
fi
PROBE_ARGS="$PROBE_ARGS -weave.hostname=$ARG_VALUE"
APP_ARGS="$APP_ARGS -weave.hostname=$ARG_VALUE"
;;
--probe.*)
if echo "$1" | grep "=" 1>/dev/null; then
ARG_NAME=$(echo "$1" | sed 's/\-\-probe\.\([^=]*\)=\(.*\)/\1/')
ARG_VALUE=$(echo "$1" | sed 's/\-\-probe\.\([^=]*\)=\(.*\)/\2/')
else
[ $# -gt 1 ] || usage
ARG_NAME=$(echo "$1" | sed 's/\-\-probe\.//')
ARG_VALUE="$2"
shift
fi
PROBE_ARGS="$PROBE_ARGS -$ARG_NAME=$ARG_VALUE"
;;
--service-token*)
if echo "$1" | grep "=" 1>/dev/null; then
ARG_VALUE=$(echo "$1" | sed 's/\-\-service-token=\(.*\)/\1/')
else
[ $# -gt 1 ] || usage
ARG_VALUE="$2"
shift
fi
PROBE_ARGS="$PROBE_ARGS -token=$ARG_VALUE"
TOKEN_PROVIDED=true
touch /etc/service/app/down
;;
*)
break
;;
esac
shift
done
echo "$APP_ARGS" >/var/run/weave/scope-app.args
echo "$PROBE_ARGS" >/var/run/weave/scope-probe.args
echo "$@" >/var/run/weave/scope-app.args
echo "$@" >/var/run/weave/scope-probe.args
# End of the command line can optionally be some
# addresses of apps to connect to, for people not
# using Weave DNS. We stick these in /var/run/weave/apps
# for the run-probe script to pick up.
MANUAL_APPS=$@
MANUAL_APPS=""
# Implicitly target the Scope Service if a service token was provided with
# no explicit manual app.

View File

@@ -1,3 +1,3 @@
#!/bin/bash
exec -a scope-app /home/weave/scope app $(cat /var/run/weave/scope-app.args)
exec -a scope-app /home/weave/scope --mode app $(cat /var/run/weave/scope-app.args)

View File

@@ -1,3 +1,3 @@
#!/bin/bash
exec -a scope-probe /home/weave/scope probe $(cat /var/run/weave/scope-probe.args) $(cat /var/run/weave/apps)
exec -a scope-probe /home/weave/scope --mode probe $(cat /var/run/weave/scope-probe.args) $(cat /var/run/weave/apps)

View File

@@ -1,7 +1,6 @@
package main
import (
"flag"
"fmt"
"math/rand"
"net/http"
@@ -25,7 +24,6 @@ import (
"github.com/weaveworks/scope/common/middleware"
"github.com/weaveworks/scope/common/network"
"github.com/weaveworks/scope/common/weave"
"github.com/weaveworks/scope/common/xfer"
"github.com/weaveworks/scope/probe/docker"
)
@@ -142,50 +140,28 @@ func pipeRouterFactory(userIDer multitenant.UserIDer, pipeRouterURL, consulInf s
}
// Main runs the app
func appMain() {
var (
window = flag.Duration("window", 15*time.Second, "window")
listen = flag.String("http.address", ":"+strconv.Itoa(xfer.AppPort), "webserver listen address")
logLevel = flag.String("log.level", "info", "logging threshold level: debug|info|warn|error|fatal|panic")
logPrefix = flag.String("log.prefix", "<app>", "prefix for each log line")
logHTTP = flag.Bool("log.http", false, "Log individual HTTP requests")
weaveAddr = flag.String("weave.addr", app.DefaultWeaveURL, "Address on which to contact WeaveDNS")
weaveHostname = flag.String("weave.hostname", app.DefaultHostname, "Hostname to advertise in WeaveDNS")
containerName = flag.String("container.name", app.DefaultContainerName, "Name of this container (to lookup container ID)")
dockerEndpoint = flag.String("docker", app.DefaultDockerEndpoint, "Location of docker endpoint (to lookup container ID)")
collectorURL = flag.String("collector", "local", "Collector to use (local of dynamodb)")
controlRouterURL = flag.String("control.router", "local", "Control router to use (local or sqs)")
pipeRouterURL = flag.String("pipe.router", "local", "Pipe router to use (local)")
userIDHeader = flag.String("userid.header", "", "HTTP header to use as userid")
awsCreateTables = flag.Bool("aws.create.tables", false, "Create the tables in DynamoDB")
consulInf = flag.String("consul.inf", "", "The interface who's address I should advertise myself under in consul")
)
flag.Parse()
setLogLevel(*logLevel)
setLogFormatter(*logPrefix)
func appMain(flags appFlags) {
setLogLevel(flags.logLevel)
setLogFormatter(flags.logPrefix)
userIDer := multitenant.NoopUserIDer
if *userIDHeader != "" {
userIDer = multitenant.UserIDHeader(*userIDHeader)
if flags.userIDHeader != "" {
userIDer = multitenant.UserIDHeader(flags.userIDHeader)
}
collector, err := collectorFactory(userIDer, *collectorURL, *window, *awsCreateTables)
collector, err := collectorFactory(userIDer, flags.collectorURL, flags.window, flags.awsCreateTables)
if err != nil {
log.Fatalf("Error creating collector: %v", err)
return
}
controlRouter, err := controlRouterFactory(userIDer, *controlRouterURL)
controlRouter, err := controlRouterFactory(userIDer, flags.controlRouterURL)
if err != nil {
log.Fatalf("Error creating control router: %v", err)
return
}
pipeRouter, err := pipeRouterFactory(userIDer, *pipeRouterURL, *consulInf)
pipeRouter, err := pipeRouterFactory(userIDer, flags.pipeRouterURL, flags.consulInf)
if err != nil {
log.Fatalf("Error creating pipe router: %v", err)
return
@@ -213,10 +189,10 @@ func appMain() {
// If user supplied a weave router address, periodically try and register
// out IP address in WeaveDNS.
if *weaveAddr != "" {
if flags.weaveAddr != "" {
weave, err := newWeavePublisher(
*dockerEndpoint, *weaveAddr,
*weaveHostname, *containerName)
flags.dockerEndpoint, flags.weaveAddr,
flags.weaveHostname, flags.containerName)
if err != nil {
log.Println("Failed to start weave integration:", err)
} else {
@@ -225,12 +201,12 @@ func appMain() {
}
handler := router(collector, controlRouter, pipeRouter)
if *logHTTP {
if flags.logHTTP {
handler = middleware.Logging.Wrap(handler)
}
go func() {
log.Infof("listening on %s", *listen)
log.Info(http.ListenAndServe(*listen, handler))
log.Infof("listening on %s", flags.listen)
log.Info(http.ListenAndServe(flags.listen, handler))
}()
common.SignalHandlerLoop()

View File

@@ -1,12 +1,17 @@
package main
import (
"flag"
"fmt"
"os"
"strconv"
"strings"
"time"
log "github.com/Sirupsen/logrus"
"github.com/weaveworks/scope/app"
"github.com/weaveworks/scope/common/xfer"
"github.com/weaveworks/weave/common"
)
@@ -45,27 +50,142 @@ func setLogLevel(levelname string) {
log.SetLevel(level)
}
func usage() {
fmt.Fprintf(os.Stderr, "usage: %s (app|probe|version) args...\n", os.Args[0])
os.Exit(1)
type flags struct {
probe probeFlags
app appFlags
}
type probeFlags struct {
token string
httpListen string
publishInterval time.Duration
spyInterval time.Duration
spyProcs bool
procRoot string
pluginsRoot string
useConntrack bool
insecure bool
logPrefix string
logLevel string
dockerEnabled bool
dockerInterval time.Duration
dockerBridge string
kubernetesEnabled bool
kubernetesAPI string
kubernetesInterval time.Duration
weaveAddr string
weaveHostname string
}
type appFlags struct {
window time.Duration
listen string
logLevel string
logPrefix string
logHTTP bool
weaveAddr string
weaveHostname string
containerName string
dockerEndpoint string
collectorURL string
controlRouterURL string
pipeRouterURL string
userIDHeader string
awsCreateTables bool
consulInf string
}
func main() {
if len(os.Args) < 2 {
usage()
var (
flags = flags{}
mode string
debug bool
weaveHostname string
)
// Flags that apply to both probe and app
flag.StringVar(&mode, "mode", "help", "For internal use.")
flag.BoolVar(&debug, "debug", false, "Force debug logging.")
flag.StringVar(&weaveHostname, "weave.hostname", "", "Hostname to advertise/lookup in WeaveDNS")
// We can ignore these - we need to know how to parse them, but they are interpreted by the entrypoint script.
// They are also here so they are included in usage.
flag.Bool("no-app", false, "Don't run the app.")
flag.Bool("probe-only", false, "Only run the probe.")
flag.Bool("no-probe", false, "Don't run the probe.")
flag.Bool("app-only", false, "Only run the app")
flag.String("service-token", "", "Token to use to authenticate with scope.weave.works")
// Probe flags
flag.StringVar(&flags.probe.token, "probe.token", "default-token", "Token to use to authenticate with scope.weave.works")
flag.StringVar(&flags.probe.httpListen, "probe.http.listen", "", "listen address for HTTP profiling and instrumentation server")
flag.DurationVar(&flags.probe.publishInterval, "probe.publish.interval", 3*time.Second, "publish (output) interval")
flag.DurationVar(&flags.probe.spyInterval, "probe.spy.interval", time.Second, "spy (scan) interval")
flag.BoolVar(&flags.probe.spyProcs, "probe.processes", true, "report processes (needs root)")
flag.StringVar(&flags.probe.procRoot, "probe.proc.root", "/proc", "location of the proc filesystem")
flag.StringVar(&flags.probe.pluginsRoot, "probe.plugins.root", "/var/run/scope/plugins", "Root directory to search for plugins")
flag.BoolVar(&flags.probe.useConntrack, "probe.conntrack", true, "also use conntrack to track connections")
flag.BoolVar(&flags.probe.insecure, "probe.insecure", false, "(SSL) explicitly allow \"insecure\" SSL connections and transfers")
flag.StringVar(&flags.probe.logPrefix, "probe.log.prefix", "<probe>", "prefix for each log line")
flag.StringVar(&flags.probe.logLevel, "probe.log.level", "info", "logging threshold level: debug|info|warn|error|fatal|panic")
flag.BoolVar(&flags.probe.dockerEnabled, "probe.docker", false, "collect Docker-related attributes for processes")
flag.DurationVar(&flags.probe.dockerInterval, "probe.docker.interval", 10*time.Second, "how often to update Docker attributes")
flag.StringVar(&flags.probe.dockerBridge, "probe.docker.bridge", "docker0", "the docker bridge name")
flag.BoolVar(&flags.probe.kubernetesEnabled, "probe.kubernetes", false, "collect kubernetes-related attributes for containers, should only be enabled on the master node")
flag.StringVar(&flags.probe.kubernetesAPI, "probe.kubernetes.api", "", "Address of kubernetes master api")
flag.DurationVar(&flags.probe.kubernetesInterval, "probe.kubernetes.interval", 10*time.Second, "how often to do a full resync of the kubernetes data")
flag.StringVar(&flags.probe.weaveAddr, "probe.weave.addr", "127.0.0.1:6784", "IP address & port of the Weave router")
flag.StringVar(&flags.probe.weaveHostname, "probe.weave.hostname", app.DefaultHostname, "Hostname to lookup in WeaveDNS")
// App flags
flag.DurationVar(&flags.app.window, "app.window", 15*time.Second, "window")
flag.StringVar(&flags.app.listen, "app.http.address", ":"+strconv.Itoa(xfer.AppPort), "webserver listen address")
flag.StringVar(&flags.app.logLevel, "app.log.level", "info", "logging threshold level: debug|info|warn|error|fatal|panic")
flag.StringVar(&flags.app.logPrefix, "app.log.prefix", "<app>", "prefix for each log line")
flag.BoolVar(&flags.app.logHTTP, "app.log.http", false, "Log individual HTTP requests")
flag.StringVar(&flags.app.weaveAddr, "app.weave.addr", app.DefaultWeaveURL, "Address on which to contact WeaveDNS")
flag.StringVar(&flags.app.weaveHostname, "app.weave.hostname", app.DefaultHostname, "Hostname to advertise in WeaveDNS")
flag.StringVar(&flags.app.containerName, "app.container.name", app.DefaultContainerName, "Name of this container (to lookup container ID)")
flag.StringVar(&flags.app.dockerEndpoint, "app.docker", app.DefaultDockerEndpoint, "Location of docker endpoint (to lookup container ID)")
flag.StringVar(&flags.app.collectorURL, "app.collector", "local", "Collector to use (local of dynamodb)")
flag.StringVar(&flags.app.controlRouterURL, "app.control.router", "local", "Control router to use (local or sqs)")
flag.StringVar(&flags.app.pipeRouterURL, "app.pipe.router", "local", "Pipe router to use (local)")
flag.StringVar(&flags.app.userIDHeader, "app.userid.header", "", "HTTP header to use as userid")
flag.BoolVar(&flags.app.awsCreateTables, "app.aws.create.tables", false, "Create the tables in DynamoDB")
flag.StringVar(&flags.app.consulInf, "app.consul.inf", "", "The interface who's address I should advertise myself under in consul")
flag.Parse()
// Deal with common args
if debug {
flags.probe.logLevel = "debug"
flags.app.logLevel = "debug"
}
if weaveHostname != "" {
flags.probe.weaveHostname = weaveHostname
flags.app.weaveHostname = weaveHostname
}
module := os.Args[1]
os.Args = append([]string{os.Args[0]}, os.Args[2:]...)
switch module {
switch mode {
case "app":
appMain()
appMain(flags.app)
case "probe":
probeMain()
probeMain(flags.probe)
case "version":
fmt.Println("Weave Scope version", version)
case "help":
flag.PrintDefaults()
default:
usage()
fmt.Printf("command '%s' not recognices", mode)
os.Exit(1)
}
}

View File

@@ -63,36 +63,9 @@ func check() {
}
// Main runs the probe
func probeMain() {
var (
targets = []string{fmt.Sprintf("localhost:%d", xfer.AppPort)}
token = flag.String("token", "default-token", "probe token")
httpListen = flag.String("http.listen", "", "listen address for HTTP profiling and instrumentation server")
publishInterval = flag.Duration("publish.interval", 3*time.Second, "publish (output) interval")
spyInterval = flag.Duration("spy.interval", time.Second, "spy (scan) interval")
spyProcs = flag.Bool("processes", true, "report processes (needs root)")
procRoot = flag.String("proc.root", "/proc", "location of the proc filesystem")
pluginsRoot = flag.String("plugins.root", "/var/run/scope/plugins", "Root directory to search for plugins")
useConntrack = flag.Bool("conntrack", true, "also use conntrack to track connections")
insecure = flag.Bool("insecure", false, "(SSL) explicitly allow \"insecure\" SSL connections and transfers")
logPrefix = flag.String("log.prefix", "<probe>", "prefix for each log line")
logLevel = flag.String("log.level", "info", "logging threshold level: debug|info|warn|error|fatal|panic")
dockerEnabled = flag.Bool("docker", false, "collect Docker-related attributes for processes")
dockerInterval = flag.Duration("docker.interval", 10*time.Second, "how often to update Docker attributes")
dockerBridge = flag.String("docker.bridge", "docker0", "the docker bridge name")
kubernetesEnabled = flag.Bool("kubernetes", false, "collect kubernetes-related attributes for containers, should only be enabled on the master node")
kubernetesAPI = flag.String("kubernetes.api", "", "Address of kubernetes master api")
kubernetesInterval = flag.Duration("kubernetes.interval", 10*time.Second, "how often to do a full resync of the kubernetes data")
weaveAddr = flag.String("weave.addr", "127.0.0.1:6784", "IP address & port of the Weave router")
weaveDNSTarget = flag.String("weave.hostname", fmt.Sprintf("scope.weave.local:%d", xfer.AppPort), "Hostname to lookup in weaveDNS")
)
flag.Parse()
setLogLevel(*logLevel)
setLogFormatter(*logPrefix)
func probeMain(flags probeFlags) {
setLogLevel(flags.logLevel)
setLogFormatter(flags.logPrefix)
// Setup in memory metrics sink
inm := metrics.NewInmemSink(time.Minute, 2*time.Minute)
@@ -102,28 +75,30 @@ func probeMain() {
defer log.Info("probe exiting")
if *spyProcs && os.Getegid() != 0 {
log.Warn("-process=true, but that requires root to find everything")
if flags.spyProcs && os.Getegid() != 0 {
log.Warn("--probe.process=true, but that requires root to find everything")
}
rand.Seed(time.Now().UnixNano())
probeID := strconv.FormatInt(rand.Int63(), 16)
var (
probeID = strconv.FormatInt(rand.Int63(), 16)
hostName = hostname.Get()
hostID = hostName // TODO(pb): we should sanitize the hostname
)
log.Infof("probe starting, version %s, ID %s", version, probeID)
log.Infof("command line: %v", os.Args)
go check()
var targets = []string{fmt.Sprintf("localhost:%d", xfer.AppPort)}
if len(flag.Args()) > 0 {
targets = flag.Args()
}
log.Infof("publishing to: %s", strings.Join(targets, ", "))
probeConfig := appclient.ProbeConfig{
Token: *token,
Token: flags.token,
ProbeID: probeID,
Insecure: *insecure,
Insecure: flags.insecure,
}
clients := appclient.NewMultiAppClient(func(hostname, endpoint string) (appclient.AppClient, error) {
return appclient.NewAppClient(
@@ -136,13 +111,13 @@ func probeMain() {
resolver := appclient.NewResolver(targets, net.LookupIP, clients.Set)
defer resolver.Stop()
processCache := process.NewCachingWalker(process.NewWalker(*procRoot))
processCache := process.NewCachingWalker(process.NewWalker(flags.procRoot))
scanner := procspy.NewConnectionScanner(processCache)
endpointReporter := endpoint.NewReporter(hostID, hostName, *spyProcs, *useConntrack, scanner)
endpointReporter := endpoint.NewReporter(hostID, hostName, flags.spyProcs, flags.useConntrack, scanner)
defer endpointReporter.Stop()
p := probe.New(probeID, *spyInterval, *publishInterval, clients)
p := probe.New(probeID, flags.spyInterval, flags.publishInterval, clients)
p.AddTicker(processCache)
hostReporter := host.NewReporter(hostID, hostName, probeID, clients)
defer hostReporter.Stop()
@@ -153,11 +128,11 @@ func probeMain() {
)
p.AddTagger(probe.NewTopologyTagger(), host.NewTagger(hostID))
if *dockerEnabled {
if err := report.AddLocalBridge(*dockerBridge); err != nil {
log.Errorf("Docker: problem with bridge %s: %v", *dockerBridge, err)
if flags.dockerEnabled {
if err := report.AddLocalBridge(flags.dockerBridge); err != nil {
log.Errorf("Docker: problem with bridge %s: %v", flags.dockerBridge, err)
}
if registry, err := docker.NewRegistry(*dockerInterval, clients, true); err == nil {
if registry, err := docker.NewRegistry(flags.dockerInterval, clients, true); err == nil {
defer registry.Stop()
p.AddTagger(docker.NewTagger(registry, processCache))
p.AddReporter(docker.NewReporter(registry, hostID, probeID, p))
@@ -166,8 +141,8 @@ func probeMain() {
}
}
if *kubernetesEnabled {
if client, err := kubernetes.NewClient(*kubernetesAPI, *kubernetesInterval); err == nil {
if flags.kubernetesEnabled {
if client, err := kubernetes.NewClient(flags.kubernetesAPI, flags.kubernetesInterval); err == nil {
defer client.Stop()
p.AddReporter(kubernetes.NewReporter(client))
} else {
@@ -176,25 +151,25 @@ func probeMain() {
}
}
if *weaveAddr != "" {
client := weave.NewClient(sanitize.URL("http://", 6784, "")(*weaveAddr))
if flags.weaveAddr != "" {
client := weave.NewClient(sanitize.URL("http://", 6784, "")(flags.weaveAddr))
weave := overlay.NewWeave(hostID, client)
defer weave.Stop()
p.AddTagger(weave)
p.AddReporter(weave)
dockerBridgeIP, err := network.GetFirstAddressOf(*dockerBridge)
dockerBridgeIP, err := network.GetFirstAddressOf(flags.dockerBridge)
if err != nil {
log.Println("Error getting docker bridge ip:", err)
} else {
weaveDNSLookup := appclient.LookupUsing(dockerBridgeIP + ":53")
weaveResolver := appclient.NewResolver([]string{*weaveDNSTarget}, weaveDNSLookup, clients.Set)
weaveResolver := appclient.NewResolver([]string{flags.weaveHostname}, weaveDNSLookup, clients.Set)
defer weaveResolver.Stop()
}
}
pluginRegistry, err := plugins.NewRegistry(
*pluginsRoot,
flags.pluginsRoot,
pluginAPIVersion,
map[string]string{
"probe_id": probeID,
@@ -208,11 +183,11 @@ func probeMain() {
p.AddReporter(pluginRegistry)
}
if *httpListen != "" {
if flags.httpListen != "" {
go func() {
log.Infof("Profiling data being exported to %s", *httpListen)
log.Infof("go tool pprof http://%s/debug/pprof/{profile,heap,block}", *httpListen)
log.Infof("Profiling endpoint %s terminated: %v", *httpListen, http.ListenAndServe(*httpListen, nil))
log.Infof("Profiling data being exported to %s", flags.httpListen)
log.Infof("go tool pprof http://%s/debug/pprof/{profile,heap,block}", flags.httpListen)
log.Infof("Profiling endpoint %s terminated: %v", flags.httpListen, http.ListenAndServe(flags.httpListen, nil))
}()
}

23
scope
View File

@@ -94,7 +94,7 @@ launch_command() {
-v /var/run/docker.sock:/var/run/docker.sock \
-v /var/run/scope/plugins:/var/run/scope/plugins \
-e CHECKPOINT_DISABLE \
$WEAVESCOPE_DOCKER_ARGS $SCOPE_IMAGE --probe.docker true "$@"
$WEAVESCOPE_DOCKER_ARGS $SCOPE_IMAGE --probe.docker=true "$@"
}
check_docker_version
@@ -105,7 +105,26 @@ case "$COMMAND" in
;;
version)
docker run $SCOPE_IMAGE version
docker run --rm -e CHECKPOINT_DISABLE $SCOPE_IMAGE version
;;
help)
cat >&2 <<EOF
Usage:
scope help - Print this
scope launch - Launch Scope
EOF
docker run --rm -e CHECKPOINT_DISABLE $SCOPE_IMAGE help
cat >&2 <<EOF
scope stop - Stop Scope
scope command - Print the docker command used to start Scope
EOF
;;
launch)