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 }