github.com/Cloud-Foundations/Dominator@v0.3.4/cmd/mdbd/main.go (about)

     1  package main
     2  
     3  import (
     4  	"flag"
     5  	"fmt"
     6  	"os"
     7  	"os/signal"
     8  	"sync"
     9  	"syscall"
    10  	"time"
    11  
    12  	"github.com/Cloud-Foundations/Dominator/lib/constants"
    13  	"github.com/Cloud-Foundations/Dominator/lib/flags/loadflags"
    14  	"github.com/Cloud-Foundations/Dominator/lib/fsutil"
    15  	"github.com/Cloud-Foundations/Dominator/lib/log"
    16  	"github.com/Cloud-Foundations/Dominator/lib/log/serverlogger"
    17  	"github.com/Cloud-Foundations/Dominator/lib/mdb"
    18  	"github.com/Cloud-Foundations/Dominator/lib/srpc"
    19  	"github.com/Cloud-Foundations/Dominator/lib/srpc/setupserver"
    20  	"github.com/Cloud-Foundations/tricorder/go/tricorder"
    21  )
    22  
    23  var (
    24  	datacentre = flag.String("datacentre", "",
    25  		"Datacentre to limit results to (may not be supported by all drivers)")
    26  	debug         = flag.Bool("debug", false, "If true, show debugging output")
    27  	fetchInterval = flag.Uint("fetchInterval", 59,
    28  		"Interval between fetches from the MDB source, in seconds")
    29  	hostnameRegex = flag.String("hostnameRegex", ".*",
    30  		"A regular expression to match the desired hostnames")
    31  	mdbFile = flag.String("mdbFile", constants.DefaultMdbFile,
    32  		"Name of file to write filtered MDB data to")
    33  	portNum = flag.Uint("portNum", constants.SimpleMdbServerPortNumber,
    34  		"Port number to allocate and listen on for HTTP/RPC")
    35  	sourcesFile = flag.String("sourcesFile", "/var/lib/mdbd/mdb.sources.list",
    36  		"Name of file list of driver url pairs")
    37  	stateDir = flag.String("stateDir", "/var/lib/mdbd",
    38  		"Name of state directory")
    39  	pidfile = flag.String("pidfile", "", "Name of file to write my PID to")
    40  )
    41  
    42  func printUsage() {
    43  	fmt.Fprintln(os.Stderr,
    44  		"Usage: mdbd [flags...]")
    45  	fmt.Fprintln(os.Stderr, "Common flags:")
    46  	flag.PrintDefaults()
    47  	fmt.Fprintln(os.Stderr, "Drivers:")
    48  	fmt.Fprintln(os.Stderr,
    49  		"  aws: region account")
    50  	fmt.Fprintln(os.Stderr,
    51  		"    Query Amazon AWS")
    52  	fmt.Fprintln(os.Stderr,
    53  		"    region: a datacentre like 'us-east-1'")
    54  	fmt.Fprintln(os.Stderr,
    55  		"    account: the profile to use out of ~/.aws/credentials which")
    56  	fmt.Fprintln(os.Stderr,
    57  		"             contains the amazon aws credentials. For additional")
    58  	fmt.Fprintln(os.Stderr,
    59  		"             information see:")
    60  	fmt.Fprintln(os.Stderr,
    61  		"             http://docs.aws.amazon.com/sdk-for-go/latest/v1/developerguide/sdkforgo-dg.pdf")
    62  	fmt.Fprintln(os.Stderr,
    63  		"  aws-filtered: targets filter-tags-file")
    64  	fmt.Fprintln(os.Stderr,
    65  		"    Query Amazon AWS")
    66  	fmt.Fprintln(os.Stderr,
    67  		"    targets: a list of targets, i.e. 'prod,us-east-1;dev,us-east-1'")
    68  	fmt.Fprintln(os.Stderr,
    69  		"    filter-tags-file: a JSON file of tags to filter for")
    70  	fmt.Fprintln(os.Stderr,
    71  		"  aws-local")
    72  	fmt.Fprintln(os.Stderr,
    73  		"    Query Amazon AWS for all acccounts for the local region")
    74  	fmt.Fprintln(os.Stderr,
    75  		"  cis: url")
    76  	fmt.Fprintln(os.Stderr,
    77  		"    url: Cloud Intelligence Service endpoint search query")
    78  	fmt.Fprintln(os.Stderr,
    79  		"  ds.host.fqdn: url")
    80  	fmt.Fprintln(os.Stderr,
    81  		"    url: URL which yields JSON with map of map of hosts with fqdn entries")
    82  	fmt.Fprintln(os.Stderr,
    83  		"  fleet-manager: manager-hostname [location]")
    84  	fmt.Fprintln(os.Stderr,
    85  		"    Query Fleet Manager")
    86  	fmt.Fprintln(os.Stderr,
    87  		"    manager-hostname: hostname of the Fleet Manager")
    88  	fmt.Fprintln(os.Stderr,
    89  		"    location: optional location to limit query to")
    90  	fmt.Fprintln(os.Stderr,
    91  		"  hypervisor")
    92  	fmt.Fprintln(os.Stderr,
    93  		"    Query Hypervisor on this machine")
    94  	fmt.Fprintln(os.Stderr,
    95  		"  json: url")
    96  	fmt.Fprintln(os.Stderr,
    97  		"    url: URL which yields a JSON-formatted list of machines and tags")
    98  	fmt.Fprintln(os.Stderr,
    99  		"  text: url")
   100  	fmt.Fprintln(os.Stderr,
   101  		"    url: URL which yields lines. Each line contains:")
   102  	fmt.Fprintln(os.Stderr,
   103  		"         host [required-image [planned-image]]")
   104  	fmt.Fprintln(os.Stderr,
   105  		"  topology: url [location]")
   106  	fmt.Fprintln(os.Stderr,
   107  		"    Load Topology (only one permitted)")
   108  	fmt.Fprintln(os.Stderr,
   109  		"    url: directory or Git URL containing the Topology")
   110  	fmt.Fprintln(os.Stderr,
   111  		"    location: optional subdirectory containing the Topology")
   112  }
   113  
   114  type driver struct {
   115  	name      string
   116  	minArgs   int
   117  	maxArgs   int
   118  	setupFunc makeGeneratorFunc
   119  }
   120  
   121  var drivers = []driver{
   122  	{"aws", 2, 2, newAwsGenerator},
   123  	{"aws-filtered", 2, 2, newAwsFilteredGenerator},
   124  	{"aws-local", 0, 0, newAwsLocalGenerator},
   125  	{"cis", 1, 1, newCisGenerator},
   126  	{"ds.host.fqdn", 1, 1, newDsHostFqdnGenerator},
   127  	{"fleet-manager", 1, 2, newFleetManagerGenerator},
   128  	{"hypervisor", 0, 0, newHypervisorGenerator},
   129  	{"json", 1, 1, newJsonGenerator},
   130  	{"text", 1, 1, newTextGenerator},
   131  	{"topology", 1, 2, newTopologyGenerator},
   132  }
   133  
   134  func gracefulCleanup() {
   135  	os.Remove(*pidfile)
   136  	os.Exit(1)
   137  }
   138  
   139  func writePidfile() {
   140  	file, err := os.Create(*pidfile)
   141  	if err != nil {
   142  		return
   143  	}
   144  	defer file.Close()
   145  	fmt.Fprintln(file, os.Getpid())
   146  }
   147  
   148  func handleSignals(logger log.Logger) {
   149  	if *pidfile == "" {
   150  		return
   151  	}
   152  	sigtermChannel := make(chan os.Signal)
   153  	signal.Notify(sigtermChannel, syscall.SIGTERM, syscall.SIGINT)
   154  	writePidfile()
   155  	go func() {
   156  		for {
   157  			select {
   158  			case <-sigtermChannel:
   159  				gracefulCleanup()
   160  			}
   161  		}
   162  	}()
   163  }
   164  
   165  func showErrorAndDie(err error) {
   166  	fmt.Fprintln(os.Stderr, err)
   167  	os.Exit(2)
   168  }
   169  
   170  func main() {
   171  	if os.Geteuid() == 0 {
   172  		fmt.Fprintln(os.Stderr, "Do not run the MDB daemon as root")
   173  		os.Exit(1)
   174  	}
   175  	if err := loadflags.LoadForDaemon("mdbd"); err != nil {
   176  		fmt.Fprintln(os.Stderr, err)
   177  		os.Exit(1)
   178  	}
   179  	flag.Usage = printUsage
   180  	flag.Parse()
   181  	tricorder.RegisterFlags()
   182  	logger := serverlogger.New("")
   183  	srpc.SetDefaultLogger(logger)
   184  	// We have to have inputs.
   185  	if *sourcesFile == "" {
   186  		printUsage()
   187  		os.Exit(2)
   188  	}
   189  	params := setupserver.Params{ClientOnly: true, Logger: logger}
   190  	setupserver.SetupTlsWithParams(params)
   191  	handleSignals(logger)
   192  	readerChannel := fsutil.WatchFile(*sourcesFile, logger)
   193  	file, err := os.Open(*sourcesFile)
   194  	if err != nil {
   195  		showErrorAndDie(err)
   196  	}
   197  	(<-readerChannel).Close()
   198  	eventChannel := make(chan struct{}, 1)
   199  	waitGroup := &sync.WaitGroup{}
   200  	generatorParams := makeGeneratorParams{
   201  		eventChannel: eventChannel,
   202  		logger:       logger,
   203  		waitGroup:    waitGroup,
   204  	}
   205  	generators, err := setupGenerators(file, drivers, generatorParams)
   206  	file.Close()
   207  	if err != nil {
   208  		showErrorAndDie(err)
   209  	}
   210  	httpSrv, err := startHttpServer(*portNum)
   211  	if err != nil {
   212  		showErrorAndDie(err)
   213  	}
   214  	httpSrv.AddHtmlWriter(logger)
   215  	// Wait a minute for any asynronous generators to yield first data.
   216  	waitTimer := time.NewTimer(time.Minute)
   217  	waitChannel := make(chan struct{}, 1)
   218  	go func() {
   219  		waitGroup.Wait()
   220  		waitChannel <- struct{}{}
   221  	}()
   222  	select {
   223  	case <-waitChannel:
   224  		logger.Println("Asynchronous generators completed initial generation")
   225  	case <-waitTimer.C:
   226  		logger.Println("Timed out waiting for initial data")
   227  	}
   228  	rpcd := startRpcd(logger)
   229  	go runDaemon(generators, eventChannel, *mdbFile, *hostnameRegex,
   230  		*datacentre, *fetchInterval, func(old, new *mdb.Mdb) {
   231  			rpcd.pushUpdateToAll(old, new)
   232  			httpSrv.UpdateMdb(new)
   233  		},
   234  		logger, *debug)
   235  	<-readerChannel
   236  	fsutil.WatchFileStop()
   237  	if err := syscall.Exec(os.Args[0], os.Args, os.Environ()); err != nil {
   238  		logger.Printf("Unable to Exec:%s: %s\n", os.Args[0], err)
   239  	}
   240  }