github.com/google/cloudprober@v0.11.3/cmd/cloudprober.go (about)

     1  // Copyright 2017 The Cloudprober Authors.
     2  //
     3  // Licensed under the Apache License, Version 2.0 (the "License");
     4  // you may not use this file except in compliance with the License.
     5  // You may obtain a copy of the License at
     6  //
     7  //      http://www.apache.org/licenses/LICENSE-2.0
     8  //
     9  // Unless required by applicable law or agreed to in writing, software
    10  // distributed under the License is distributed on an "AS IS" BASIS,
    11  // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
    12  // See the License for the specific language governing permissions and
    13  // limitations under the License.
    14  
    15  /*
    16  Binary cloudprober is a tool for running a set of probes and metric surfacers
    17  on a GCE VM. Cloudprober takes in a config proto which dictates what probes
    18  and surfacers should be created with what configuration, and then manages the
    19  asynchronous fan-in/fan-out of the data between the probes and the surfacers.
    20  */
    21  package main
    22  
    23  import (
    24  	"context"
    25  	"fmt"
    26  	_ "net/http/pprof"
    27  	"os"
    28  	"os/signal"
    29  	"runtime/pprof"
    30  	"syscall"
    31  	"time"
    32  
    33  	"cloud.google.com/go/compute/metadata"
    34  	"flag"
    35  	"github.com/golang/glog"
    36  	"github.com/google/cloudprober"
    37  	"github.com/google/cloudprober/common/file"
    38  	"github.com/google/cloudprober/config"
    39  	"github.com/google/cloudprober/config/runconfig"
    40  	"github.com/google/cloudprober/sysvars"
    41  	"github.com/google/cloudprober/web"
    42  )
    43  
    44  var (
    45  	configFile       = flag.String("config_file", "", "Config file")
    46  	versionFlag      = flag.Bool("version", false, "Print version and exit")
    47  	stopTime         = flag.Duration("stop_time", 0, "How long to wait for cleanup before process exits on SIGINT and SIGTERM")
    48  	cpuprofile       = flag.String("cpuprof", "", "Write cpu profile to file")
    49  	memprofile       = flag.String("memprof", "", "Write heap profile to file")
    50  	configTest       = flag.Bool("configtest", false, "Dry run to test config file")
    51  	dumpConfig       = flag.Bool("dumpconfig", false, "Dump processed config to stdout")
    52  	testInstanceName = flag.String("test_instance_name", "ig-us-central1-a-01-0000", "Instance name example to be used in tests")
    53  
    54  	// configTestVars provides a sane set of sysvars for config testing.
    55  	configTestVars = map[string]string(nil)
    56  )
    57  
    58  // This gets overwritten by using -ldflags="-X main.version=${VERSION}" at
    59  // the build time.
    60  var version = "undefined"
    61  
    62  func setupConfigTestVars() {
    63  	configTestVars = map[string]string{
    64  		"zone":              "us-central1-a",
    65  		"project":           "fake-domain.com:fake-project",
    66  		"project_id":        "12345678",
    67  		"instance":          *testInstanceName,
    68  		"internal_ip":       "192.168.0.10",
    69  		"external_ip":       "10.10.10.10",
    70  		"instance_template": "ig-us-central1-a-01",
    71  		"machine_type":      "e2-small",
    72  	}
    73  }
    74  
    75  const (
    76  	configMetadataKeyName = "cloudprober_config"
    77  	defaultConfigFile     = "/etc/cloudprober.cfg"
    78  )
    79  
    80  func setupProfiling() {
    81  	sigChan := make(chan os.Signal, 1)
    82  	signal.Notify(sigChan, os.Interrupt)
    83  	var f *os.File
    84  	if *cpuprofile != "" {
    85  		var err error
    86  		f, err = os.Create(*cpuprofile)
    87  		if err != nil {
    88  			glog.Exit(err)
    89  		}
    90  		if err = pprof.StartCPUProfile(f); err != nil {
    91  			glog.Errorf("Could not start CPU profiling: %v", err)
    92  		}
    93  	}
    94  	go func(file *os.File) {
    95  		<-sigChan
    96  		pprof.StopCPUProfile()
    97  		if *cpuprofile != "" {
    98  			if err := file.Close(); err != nil {
    99  				glog.Exit(err)
   100  			}
   101  		}
   102  		if *memprofile != "" {
   103  			f, err := os.Create(*memprofile)
   104  			if err != nil {
   105  				glog.Exit(err)
   106  			}
   107  			if err = pprof.WriteHeapProfile(f); err != nil {
   108  				glog.Exit(err)
   109  			}
   110  			if err := f.Close(); err != nil {
   111  				glog.Exit(err)
   112  			}
   113  		}
   114  		os.Exit(1)
   115  	}(f)
   116  }
   117  
   118  func configFileToString(fileName string) string {
   119  	b, err := file.ReadFile(fileName)
   120  	if err != nil {
   121  		glog.Exitf("Failed to read the config file: %v", err)
   122  	}
   123  	return string(b)
   124  }
   125  
   126  func getConfig() string {
   127  	if *configFile != "" {
   128  		return configFileToString(*configFile)
   129  	}
   130  	// On GCE first check if there is a config in custom metadata
   131  	// attributes.
   132  	if metadata.OnGCE() {
   133  		if config, err := config.ReadFromGCEMetadata(configMetadataKeyName); err != nil {
   134  			glog.Infof("Error reading config from metadata. Err: %v", err)
   135  		} else {
   136  			return config
   137  		}
   138  	}
   139  	// If config not found in metadata, check default config on disk
   140  	if _, err := os.Stat(defaultConfigFile); !os.IsNotExist(err) {
   141  		return configFileToString(defaultConfigFile)
   142  	}
   143  	glog.Warningf("Config file %s not found. Using default config.", defaultConfigFile)
   144  	return config.DefaultConfig()
   145  }
   146  
   147  func main() {
   148  	flag.Parse()
   149  
   150  	runconfig.SetVersion(version)
   151  
   152  	if *versionFlag {
   153  		fmt.Println(version)
   154  		return
   155  	}
   156  
   157  	setupConfigTestVars()
   158  
   159  	if *dumpConfig {
   160  		sysvars.Init(nil, configTestVars)
   161  		text, err := config.ParseTemplate(getConfig(), sysvars.Vars())
   162  		if err != nil {
   163  			glog.Exitf("Error parsing config file. Err: %v", err)
   164  		}
   165  		fmt.Println(text)
   166  		return
   167  	}
   168  
   169  	if *configTest {
   170  		sysvars.Init(nil, configTestVars)
   171  		_, err := config.ParseForTest(configFileToString(*configFile), sysvars.Vars())
   172  		if err != nil {
   173  			glog.Exitf("Error parsing config file. Err: %v", err)
   174  		}
   175  		return
   176  	}
   177  
   178  	setupProfiling()
   179  
   180  	err := cloudprober.InitFromConfig(getConfig())
   181  	if err != nil {
   182  		glog.Exitf("Error initializing cloudprober. Err: %v", err)
   183  	}
   184  
   185  	// web.Init sets up web UI for cloudprober.
   186  	web.Init()
   187  	startCtx := context.Background()
   188  
   189  	if *stopTime == 0 {
   190  		*stopTime = time.Duration(cloudprober.GetConfig().GetStopTimeSec()) * time.Second
   191  	}
   192  
   193  	if *stopTime != 0 {
   194  		// Set up signal handling for the cancelation of the start context.
   195  		sigs := make(chan os.Signal, 1)
   196  		signal.Notify(sigs, syscall.SIGINT, syscall.SIGTERM)
   197  		ctx, cancelF := context.WithCancel(startCtx)
   198  		startCtx = ctx
   199  
   200  		go func() {
   201  			sig := <-sigs
   202  			glog.Warningf("Received signal \"%v\", canceling the start context and waiting for %v before closing", sig, *stopTime)
   203  			cancelF()
   204  			time.Sleep(*stopTime)
   205  			os.Exit(0)
   206  		}()
   207  	}
   208  	cloudprober.Start(startCtx)
   209  
   210  	// Wait forever
   211  	select {}
   212  }