1package main
2
3import (
4	"flag"
5	"fmt"
6	"os"
7	"path/filepath"
8	"sync"
9	"syscall"
10
11	"github.com/BurntSushi/toml"
12	"github.com/judwhite/go-svc"
13	"github.com/mreiferson/go-options"
14	"github.com/nsqio/nsq/internal/app"
15	"github.com/nsqio/nsq/internal/lg"
16	"github.com/nsqio/nsq/internal/version"
17	"github.com/nsqio/nsq/nsqadmin"
18)
19
20func nsqadminFlagSet(opts *nsqadmin.Options) *flag.FlagSet {
21	flagSet := flag.NewFlagSet("nsqadmin", flag.ExitOnError)
22
23	flagSet.String("config", "", "path to config file")
24	flagSet.Bool("version", false, "print version string")
25
26	logLevel := opts.LogLevel
27	flagSet.Var(&logLevel, "log-level", "set log verbosity: debug, info, warn, error, or fatal")
28	flagSet.String("log-prefix", "[nsqadmin] ", "log message prefix")
29	flagSet.Bool("verbose", false, "[deprecated] has no effect, use --log-level")
30
31	flagSet.String("http-address", opts.HTTPAddress, "<addr>:<port> to listen on for HTTP clients")
32	flagSet.String("base-path", opts.BasePath, "URL base path")
33
34	flagSet.String("graphite-url", opts.GraphiteURL, "graphite HTTP address")
35	flagSet.Bool("proxy-graphite", false, "proxy HTTP requests to graphite")
36
37	flagSet.String("statsd-counter-format", opts.StatsdCounterFormat, "The counter stats key formatting applied by the implementation of statsd. If no formatting is desired, set this to an empty string.")
38	flagSet.String("statsd-gauge-format", opts.StatsdGaugeFormat, "The gauge stats key formatting applied by the implementation of statsd. If no formatting is desired, set this to an empty string.")
39	flagSet.String("statsd-prefix", opts.StatsdPrefix, "prefix used for keys sent to statsd (%s for host replacement, must match nsqd)")
40	flagSet.Duration("statsd-interval", opts.StatsdInterval, "time interval nsqd is configured to push to statsd (must match nsqd)")
41
42	flagSet.String("notification-http-endpoint", "", "HTTP endpoint (fully qualified) to which POST notifications of admin actions will be sent")
43
44	flagSet.Duration("http-client-connect-timeout", opts.HTTPClientConnectTimeout, "timeout for HTTP connect")
45	flagSet.Duration("http-client-request-timeout", opts.HTTPClientRequestTimeout, "timeout for HTTP request")
46
47	flagSet.Bool("http-client-tls-insecure-skip-verify", false, "configure the HTTP client to skip verification of TLS certificates")
48	flagSet.String("http-client-tls-root-ca-file", "", "path to CA file for the HTTP client")
49	flagSet.String("http-client-tls-cert", "", "path to certificate file for the HTTP client")
50	flagSet.String("http-client-tls-key", "", "path to key file for the HTTP client")
51
52	flagSet.String("allow-config-from-cidr", opts.AllowConfigFromCIDR, "A CIDR from which to allow HTTP requests to the /config endpoint")
53	flagSet.String("acl-http-header", opts.AclHttpHeader, "HTTP header to check for authenticated admin users")
54
55	nsqlookupdHTTPAddresses := app.StringArray{}
56	flagSet.Var(&nsqlookupdHTTPAddresses, "lookupd-http-address", "lookupd HTTP address (may be given multiple times)")
57	nsqdHTTPAddresses := app.StringArray{}
58	flagSet.Var(&nsqdHTTPAddresses, "nsqd-http-address", "nsqd HTTP address (may be given multiple times)")
59	adminUsers := app.StringArray{}
60	flagSet.Var(&adminUsers, "admin-user", "admin user (may be given multiple times; if specified, only these users will be able to perform privileged actions; acl-http-header is used to determine the authenticated user)")
61
62	return flagSet
63}
64
65type program struct {
66	once     sync.Once
67	nsqadmin *nsqadmin.NSQAdmin
68}
69
70func main() {
71	prg := &program{}
72	if err := svc.Run(prg, syscall.SIGINT, syscall.SIGTERM); err != nil {
73		logFatal("%s", err)
74	}
75}
76
77func (p *program) Init(env svc.Environment) error {
78	if env.IsWindowsService() {
79		dir := filepath.Dir(os.Args[0])
80		return os.Chdir(dir)
81	}
82	return nil
83}
84
85func (p *program) Start() error {
86	opts := nsqadmin.NewOptions()
87
88	flagSet := nsqadminFlagSet(opts)
89	flagSet.Parse(os.Args[1:])
90
91	if flagSet.Lookup("version").Value.(flag.Getter).Get().(bool) {
92		fmt.Println(version.String("nsqadmin"))
93		os.Exit(0)
94	}
95
96	var cfg config
97	configFile := flagSet.Lookup("config").Value.String()
98	if configFile != "" {
99		_, err := toml.DecodeFile(configFile, &cfg)
100		if err != nil {
101			logFatal("failed to load config file %s - %s", configFile, err)
102		}
103	}
104	cfg.Validate()
105
106	options.Resolve(opts, flagSet, cfg)
107	nsqadmin, err := nsqadmin.New(opts)
108	if err != nil {
109		logFatal("failed to instantiate nsqadmin - %s", err)
110	}
111	p.nsqadmin = nsqadmin
112
113	go func() {
114		err := p.nsqadmin.Main()
115		if err != nil {
116			p.Stop()
117			os.Exit(1)
118		}
119	}()
120
121	return nil
122}
123
124func (p *program) Stop() error {
125	p.once.Do(func() {
126		p.nsqadmin.Exit()
127	})
128	return nil
129}
130
131func logFatal(f string, args ...interface{}) {
132	lg.LogFatal("[nsqadmin] ", f, args...)
133}
134