github.com/m3db/m3@v1.5.0/src/cmd/services/m3em_agent/agentmain/agent.go (about) 1 // Copyright (c) 2017 Uber Technologies, Inc. 2 // 3 // Permission is hereby granted, free of charge, to any person obtaining a copy 4 // of this software and associated documentation files (the "Software"), to deal 5 // in the Software without restriction, including without limitation the rights 6 // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 7 // copies of the Software, and to permit persons to whom the Software is 8 // furnished to do so, subject to the following conditions: 9 // 10 // The above copyright notice and this permission notice shall be included in 11 // all copies or substantial portions of the Software. 12 // 13 // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 14 // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 15 // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 16 // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 17 // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 18 // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 19 // THE SOFTWARE. 20 21 package agentmain 22 23 import ( 24 "log" 25 "net/http" 26 _ "net/http/pprof" // pprof import 27 oexec "os/exec" 28 "strings" 29 "time" 30 31 m3emconfig "github.com/m3db/m3/src/cmd/services/m3em_agent/config" 32 "github.com/m3db/m3/src/m3em/agent" 33 "github.com/m3db/m3/src/m3em/generated/proto/m3em" 34 "github.com/m3db/m3/src/m3em/os/exec" 35 xgrpc "github.com/m3db/m3/src/m3em/x/grpc" 36 xconfig "github.com/m3db/m3/src/x/config" 37 "github.com/m3db/m3/src/x/instrument" 38 xtcp "github.com/m3db/m3/src/x/tcp" 39 40 "github.com/pborman/getopt" 41 "github.com/uber-go/tally" 42 "go.uber.org/zap" 43 "google.golang.org/grpc/credentials" 44 ) 45 46 // Run runs a m3em_agent process 47 func Run() { 48 var ( 49 configFile = getopt.StringLong("config-file", 'f', "", "Configuration file") 50 ) 51 getopt.Parse() 52 if len(*configFile) == 0 { 53 getopt.Usage() 54 return 55 } 56 57 logger, err := zap.NewProduction() 58 if err != nil { 59 log.Fatalf("unable to create logger: %v", err) 60 } 61 62 var conf m3emconfig.Configuration 63 err = xconfig.LoadFile(&conf, *configFile, xconfig.Options{}) 64 if err != nil { 65 logger.Fatal("unable to read configuration file", zap.Error(err)) 66 } 67 68 xconfig.WarnOnDeprecation(conf, logger) 69 70 // pprof server 71 go func() { 72 if err := http.ListenAndServe(conf.Server.DebugAddress, nil); err != nil { 73 logger.Fatal("unable to serve debug server", zap.Error(err)) 74 } 75 }() 76 logger.Info("serving pprof endpoints", zap.String("address", conf.Server.DebugAddress)) 77 78 reporter, err := conf.Metrics.M3.NewReporter() 79 if err != nil { 80 logger.Fatal("could not connect to metrics", zap.Error(err)) 81 } 82 scope, scopeCloser := tally.NewRootScope(tally.ScopeOptions{ 83 Prefix: conf.Metrics.Prefix, 84 CachedReporter: reporter, 85 }, time.Second) 86 defer scopeCloser.Close() 87 88 listener, err := xtcp.NewTCPListener(conf.Server.ListenAddress, 3*time.Minute) 89 if err != nil { 90 logger.Fatal("could not create TCP Listener", zap.Error(err)) 91 } 92 93 iopts := instrument.NewOptions(). 94 SetLogger(logger). 95 SetMetricsScope(scope). 96 SetTimerOptions(instrument.TimerOptions{StandardSampleRate: conf.Metrics.SampleRate}) 97 98 agentOpts := agent.NewOptions(iopts). 99 SetWorkingDirectory(conf.Agent.WorkingDir). 100 SetInitHostResourcesFn(hostFnMaker("startup", conf.Agent.StartupCmds, logger)). 101 SetReleaseHostResourcesFn(hostFnMaker("release", conf.Agent.ReleaseCmds, logger)). 102 SetExecGenFn(execGenFn). 103 SetEnvMap(envMap(logger, conf.Agent.TestEnvVars)) 104 105 agentService, err := agent.New(agentOpts) 106 if err != nil { 107 logger.Fatal("unable to create agentService", zap.Error(err)) 108 } 109 110 var serverCreds credentials.TransportCredentials 111 if tls := conf.Server.TLS; tls != nil { 112 logger.Info("using provided TLS config", zap.Any("config", tls)) 113 serverCreds, err = tls.Credentials() 114 if err != nil { 115 logger.Fatal("unable to create transport credentials", zap.Error(err)) 116 } 117 } 118 server := xgrpc.NewServer(serverCreds) 119 m3em.RegisterOperatorServer(server, agentService) 120 logger.Info("serving agent endpoints", zap.Stringer("address", listener.Addr())) 121 if err := server.Serve(listener); err != nil { 122 logger.Fatal("could not serve", zap.Error(err)) 123 } 124 } 125 126 // HACK(prateek): YAML un-marshalling returns lower-case keys for everything, 127 // setting to upper-case explicitly here. 128 func envMap(logger *zap.Logger, envMap map[string]string) exec.EnvMap { 129 logger.Warn("transforming keys set in YAML for testEnvVars to UPPER_CASE") 130 if envMap == nil { 131 return nil 132 } 133 newMap := make(map[string]string, len(envMap)) 134 for key, value := range envMap { 135 newMap[strings.ToUpper(key)] = value 136 } 137 return exec.EnvMap(newMap) 138 } 139 140 func hostFnMaker(mode string, cmds []m3emconfig.ExecCommand, logger *zap.Logger) agent.HostResourcesFn { 141 return func() error { 142 if len(cmds) == 0 { 143 logger.Info("no commands specified, skipping.", zap.String("mode", mode)) 144 return nil 145 } 146 for _, cmd := range cmds { 147 osCmd := oexec.Command(cmd.Path, cmd.Args...) 148 logger.Info("attempting to execute", zap.String("mode", mode), zap.Any("command", osCmd)) 149 output, err := osCmd.CombinedOutput() 150 if err != nil { 151 logger.Error("unable to execute cmd", zap.Error(err)) 152 return err 153 } 154 logger.Info("successfully ran cmd", zap.String("output", string(output))) 155 } 156 return nil 157 } 158 } 159 160 func execGenFn(binary string, config string) (string, []string) { 161 return binary, []string{"-f", config} 162 }