github.com/m3db/m3@v1.5.1-0.20231129193456-75a402aa583b/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  }