func RestGetPuzzle(name string) { log.Logf(l4g.DEBUG, "RestGetPuzzle: requesting token to fetch %v", name) httpRestReqLimiter <- 1 // put a token in the limiting channel defer func() { log.Logf(l4g.DEBUG, "RestGetPuzzle: releasing token from fetching %v", name) <-httpRestReqLimiter // release a token from the limiting channel }() log.Logf(l4g.DEBUG, "RestGetPuzzle: preparing request for %v", name) //client := restclient.New() req := restclient.RestRequest{ Url: pbRestUri + "/puzzles/" + name, Method: restclient.GET, Result: new(Puzzle), } log.Logf(l4g.DEBUG, "RestGetPuzzle: sending request for %v", name) status, err := client.Do(&req) if err != nil { log.Logf(l4g.ERROR, "RestGetPuzzle: error %v", err) // TODO: do something... retry? l4g.Crashf("RestGetPuzzle: could not get puzzle [%v] - bailing out", name) } log.Logf(l4g.DEBUG, "RestGetPuzzle: received response for %v", name) if status == 200 { // send result on puzzleChan log.Logf(l4g.DEBUG, "RestGetPuzzle: sending puzzle on puzzleChan for %v", name) puzzleChan <- req.Result.(*Puzzle) } else { log.Logf(l4g.ERROR, "RestGetPuzzle: got status %v", status) // TODO: do something... retry? l4g.Crashf("RestGetPuzzle: could not get puzzle [%v] - bailing out", name) } }
func OpenDrive(googleClientId string, googleClientSecret string, googleDomain string, cacheFile string) { googleWriterDomain = googleDomain GcacheFile = cacheFile GgoogleClientId = googleClientId GgoogleClientSecret = googleClientSecret // Settings for Google authorization. oauthConfig := &oauth.Config{ ClientId: googleClientId, ClientSecret: googleClientSecret, Scope: "https://www.googleapis.com/auth/drive", RedirectURL: "urn:ietf:wg:oauth:2.0:oob", AuthURL: "https://accounts.google.com/o/oauth2/auth", TokenURL: "https://accounts.google.com/o/oauth2/token", TokenCache: oauth.CacheFile(cacheFile), } // Set up a Transport using the oauthConfig. oauthTransport = &oauth.Transport{ Config: oauthConfig, Transport: http.DefaultTransport, } // Try to pull the token from the cache; if this fails, we need to get one. token, err := oauthConfig.TokenCache.Token() if err != nil { // Get an authorization code from the user authUrl := oauthConfig.AuthCodeURL("state") log.Logf(l4g.INFO, "Go to the following link in your browser: %v", authUrl) // Read the code, and exchange it for a token. log.Logf(l4g.INFO, "Enter verification code: ") var code string fmt.Scanln(&code) token, err = oauthTransport.Exchange(code) if err != nil { l4g.Crashf("An error occurred exchanging the code: %v", err) } log.Logf(l4g.INFO, "Token is cached in %v", oauthConfig.TokenCache) } oauthTransport.Token = token oauthClient = oauthTransport.Client() // Create a new authorized Drive client. driveSvc, err = drive.New(oauthClient) if err != nil { l4g.Crashf("An error occurred creating Drive client: %v", err) } }
func startHttpControl() { // Start an http server for control http.HandleFunc("/"+httpControlPath+"/", func(w http.ResponseWriter, r *http.Request) { if r.Method == "POST" { log.Logf(l4g.INFO, "Got POST on control port to %v", html.EscapeString(r.URL.Path)) switch r.URL.Path { case "/" + httpControlPath + "/version": { defer r.Body.Close() var data BigJimmyControlData err := json.NewDecoder(r.Body).Decode(&data) if err != nil { log.Logf(l4g.ERROR, "Error decoding JSON from body of POST: %v", err) w.WriteHeader(http.StatusBadRequest) fmt.Fprintf(w, "Error decoding JSON from body of POST: %v", err) } log.Logf(l4g.INFO, "Processed data from version POST: %+v", data) newVersion, err := strconv.ParseInt(data.Version, 10, 0) if err != nil { log.Logf(l4g.ERROR, "Could not parse version as int: %v", err) w.WriteHeader(http.StatusBadRequest) fmt.Fprintf(w, "Could not parse version as int: %v", err) } if newVersion > Version { // get and process version diff //go PbGetVersionDiff(Version) // send the version down the channel for processing versionChan <- newVersion } w.WriteHeader(http.StatusOK) fmt.Fprintf(w, "Processed version POST: %+v", data) } default: { w.WriteHeader(http.StatusNotFound) fmt.Fprintf(w, "Got POST on control port to %v", html.EscapeString(r.URL.Path)) } } } else { log.Logf(l4g.ERROR, "method %v not supported on control port (requested %v)", r.Method, html.EscapeString(r.URL.Path)) w.WriteHeader(http.StatusNotImplemented) fmt.Fprintf(w, "method %v not supported on control port (requested %v)", r.Method, html.EscapeString(r.URL.Path)) } }) http.HandleFunc("/", func(w http.ResponseWriter, r *http.Request) { w.WriteHeader(http.StatusNotFound) log.Logf(l4g.ERROR, "returning StatusNotFound (requested %v)", html.EscapeString(r.URL.Path)) }) log.Logf(l4g.INFO, "starting http server on port %v", httpControlPort) l4g.Crashf("http server died: %v", http.ListenAndServe(":"+httpControlPort, nil)) }
// Open/Close DB func OpenDB(dbUser string, dbPassword string, dbProtocol string, dbHost string, dbPort string, dbName string) { // Connect to database mysqlDsn := dbUser + ":" + dbPassword + "@" + dbProtocol + "(" + dbHost + ":" + dbPort + ")/" + dbName var err error dbCon, err = sql.Open("mysql", mysqlDsn) if err != nil { l4g.Crashf("could not connect to mysql database with DSN %v: %v", mysqlDsn, err) } }
func init() { roundChan = make(chan *Round, 10) restGetRoundsDone = make(chan int) // must not be buffered! rounds = make(map[string]*Round, 500) // get rounds from roundChan and shovel them into rounds map go func() { for true { log.Logf(l4g.TRACE, "roundChan listener: waiting for new round") round := <-roundChan // this will block waiting for new rounds log.Logf(l4g.INFO, "roundChan listener: got new round %+v", round) if round.Drive_id == "" { // don't have a drive_id for this round // create round folder in Google Drive roundFolderId, roundFolderUri, err := CreateRound(round.Name, huntFolderId) if err != nil { log.Logf(l4g.ERROR, "roundChan listener: could not create round [%v] in huntFolderId=[%v]: %v", round.Name, huntFolderId, err) // retry 3 times retry := 3 for err != nil && retry > 0 { // set timer roundWaitTimer := time.NewTimer(500 * time.Millisecond) // block on timer channel <-roundWaitTimer.C log.Logf(l4g.ERROR, "roundChan listener: retrying CreateRound(%v, %v)", round.Name, huntFolderId) roundFolderId, roundFolderUri, err = CreateRound(round.Name, huntFolderId) } if err != nil { l4g.Crashf("roundChan listener: retried CreateRound(%v, %v) 3 times and all failed -- bailing out!", round.Name, huntFolderId) } } // creation should have succeeded // update local and remote drive_id and drive_uri round.Drive_id = roundFolderId go PbRestPost("rounds/"+round.Name+"/drive_id", PartPost{Data: roundFolderId}) round.Drive_uri = roundFolderUri go PbRestPost("rounds/"+round.Name+"/drive_uri", PartPost{Data: roundFolderUri}) } rounds[round.Name] = round roundsArrived++ log.Logf(l4g.DEBUG, "roundChan listener: %v >= %v ?", roundsArrived, RoundCount) if roundsArrived >= RoundCount { restGetRoundsDone <- 1 } // start a bigjimmy google drive monitor for this round (if we needed round folder monitoring) // BigJimmyRoundActivityMonitor(round) } }() }
// Run starts the web application and serves HTTP requests for s func (s *Server) Run(addr string) { s.initServer() mux := http.NewServeMux() if s.Config.Profiler { mux.Handle("/debug/pprof/cmdline", http.HandlerFunc(pprof.Cmdline)) mux.Handle("/debug/pprof/profile", http.HandlerFunc(pprof.Profile)) mux.Handle("/debug/pprof/heap", pprof.Handler("heap")) mux.Handle("/debug/pprof/symbol", http.HandlerFunc(pprof.Symbol)) } mux.Handle("/", s) s.Logger.Info("web.go serving %s", addr) l, err := net.Listen("tcp", addr) if err != nil { l4g.Crashf("ListenAndServe: %s", err) } s.l = l err = http.Serve(s.l, mux) s.l.Close() }
func main() { flag.Parse() // Initialize logger logLevel = strings.ToLower(logLevel) switch { case logLevel == "trace": log = l4g.NewDefaultLogger(l4g.TRACE) case logLevel == "debug": log = l4g.NewDefaultLogger(l4g.DEBUG) case logLevel == "info": log = l4g.NewDefaultLogger(l4g.INFO) case logLevel == "warning": log = l4g.NewDefaultLogger(l4g.WARNING) case logLevel == "error": log = l4g.NewDefaultLogger(l4g.ERROR) } //log.AddFilter("log", l4g.FINE, l4g.NewFileLogWriter("example.log", true)) bigjimmybot.SetLog(log) // Connect to DB log.Logf(l4g.TRACE, "main(): before OpenDB, %v goroutines.", runtime.NumGoroutine()) bigjimmybot.OpenDB(dbUser, dbPassword, dbProtocol, dbHost, dbPort, dbName) defer bigjimmybot.CloseDB() log.Logf(l4g.TRACE, "main(): after OpenDB, %v goroutines.", runtime.NumGoroutine()) var err error // Connect to Drive // Ensure we have googleClientId and googleClientSecret (from command-line args or DB) log.Logf(l4g.TRACE, "main(): before getting client id and secret, %v goroutines.", runtime.NumGoroutine()) if googleClientId == "" { googleClientId, err = bigjimmybot.DbGetConfig("GOOGLE_CLIENT_ID") if err != nil { log.Logf(l4g.ERROR, "attempt to get googleClientId from DB failed: %v", err) } } if googleClientSecret == "" { googleClientSecret, err = bigjimmybot.DbGetConfig("GOOGLE_CLIENT_SECRET") if err != nil { log.Logf(l4g.ERROR, "attempt to get googleClientSecret from DB failed: %v", err) } } if googleClientId == "" || googleClientSecret == "" { log.Logf(l4g.ERROR, "Please specify -google_client_id and -google_client_secret (or ensure they are in the DB and database connection is working)") flag.Usage() l4g.Crashf(usageMsg) } else { log.Logf(l4g.INFO, "Using google_client_id=%v and google_client_secret=%v", googleClientId, googleClientSecret) } // Try to get google_domain (for permissions) from DB if googleDomain == "" { googleDomain, err = bigjimmybot.DbGetConfig("GOOGLE_DOMAIN") if err != nil { l4g.Crashf("Could not get pb_rest_uri from DB: %v", err) } } log.Logf(l4g.TRACE, "main(): before open drive, %v goroutines.", runtime.NumGoroutine()) bigjimmybot.OpenDrive(googleClientId, googleClientSecret, googleDomain, cacheFile) log.Logf(l4g.TRACE, "main(): after open drive, %v goroutines.", runtime.NumGoroutine()) // Setup PB REST client // Get pbRestUri if we don't have it if pbRestUri == "" { pbRestUri, err = bigjimmybot.DbGetConfig("PB_REST_URI") if err != nil { l4g.Crashf("Could not get pb_rest_uri from DB: %v", err) } } bigjimmybot.SetPbRestUri(pbRestUri) // Get huntFolderId (either from command-line, from database, or from Google Drive if we have title) // Ensure we can get huntFolderId (from command-line arg, DB, or via huntFolderTitle) if huntFolderId == "" { huntFolderId, _ = bigjimmybot.DbGetConfig("google_hunt_folder_id") } if huntFolderId == "" && huntFolderTitle == "" { // still don't have huntFolderId, and we don't have title either // try to get title from DB huntFolderTitle, _ = bigjimmybot.DbGetConfig("PB_HUNT") } else if huntFolderId != "" && huntFolderTitle != "" { log.Logf(l4g.INFO, "you specified hunt_folder_title but we have hunt_folder_id so it is being ignored.") huntFolderTitle = "" } log.Logf(l4g.TRACE, "main(): before getting hunt folder id, %v goroutines.", runtime.NumGoroutine()) if huntFolderId == "" && huntFolderTitle != "" { // huntFolderId neither specified nor in DB, but we do have title // so get hunt folder ID from Google by looking it up by title // or create it if it does not exist log.Logf(l4g.INFO, "looking up google docs folder id for title %v", huntFolderTitle) huntFolderId, err = bigjimmybot.GetFolderIdByTitle(huntFolderTitle) if err != nil { if err, ok := err.(*bigjimmybot.ListError); ok { if err.Found > 1 { l4g.Crashf("more than one document matches %v", huntFolderTitle) } else if err.Found == 0 { //l4g.Crashf("no hunt folder found for %v", huntFolderTitle) log.Logf(l4g.INFO, "no hunt folder found for %v, creating it", huntFolderTitle) var cferr error huntFolderId, _, cferr = bigjimmybot.CreateHunt(huntFolderTitle) if cferr != nil { l4g.Crashf("could not create hunt folder for title [%v]: %v", huntFolderTitle, cferr) } log.Logf(l4g.INFO, "hunt folder created") } } else { l4g.Crashf("an error occurred getting hunt folder ID: %v", err) } } log.Logf(l4g.INFO, "hunt_folder_id: %v", huntFolderId) // DB doesn't yet have huntFolderId, set it if we have it if huntFolderId != "" { err = bigjimmybot.DbSetConfig("google_hunt_folder_id", huntFolderId) if err != nil { l4g.Crashf("could not set hunt_folder_id in DB") } } } log.Logf(l4g.TRACE, "main(): after getting hunt folder id, %v goroutines.", runtime.NumGoroutine()) // set huntFolderId in bigjimmybot bigjimmybot.SetHuntFolderId(huntFolderId) // get initial version diff log.Logf(l4g.TRACE, "main(): before bigjimmybot.PbGetInitialVersionDiff %v goroutines.", runtime.NumGoroutine()) bigjimmybot.PbGetInitialVersionDiff() log.Logf(l4g.TRACE, "main(): after bigjimmybot.PbGetInitialVersionDiff %v goroutines.", runtime.NumGoroutine()) // Start ControlServer main loop // Ensure we have httpControlPort and httpControlPath log.Logf(l4g.TRACE, "main(): before ensuring httpControlPort and httpControlPath %v goroutines.", runtime.NumGoroutine()) if httpControlPort == "" { httpControlPort, err = bigjimmybot.DbGetConfig("BIGJIMMY_CONTROL_PORT") if err != nil { log.Logf(l4g.ERROR, "attempt to get httpControlPort from DB failed: %v", err) } } if httpControlPath == "" { httpControlPath, err = bigjimmybot.DbGetConfig("BIGJIMMY_CONTROL_PATH") if err != nil { log.Logf(l4g.ERROR, "attempt to get httpControlPath from DB failed: %v", err) } } if httpControlPort == "" || httpControlPath == "" { log.Logf(l4g.ERROR, "Please specify -http_control_port and -http_control_path (or ensure they are in the DB and database connection is working)") flag.Usage() l4g.Crashf(usageMsg) } else { log.Logf(l4g.INFO, "Using http_control_port=%v and http_control_path=%v", httpControlPort, httpControlPath) } log.Logf(l4g.TRACE, "main(): after ensuring httpControlPort and httpControlPath %v goroutines.", runtime.NumGoroutine()) time.Sleep(5 * time.Second) log.Logf(l4g.TRACE, "main(): before bigjimmybot.ControlServer %v goroutines.", runtime.NumGoroutine()) bigjimmybot.ControlServer(httpControlPort, httpControlPath) log.Logf(l4g.TRACE, "main(): after bigjimmybot.ControlServer %v goroutines.", runtime.NumGoroutine()) }