func main() { username, password := waitForPostgres(serviceName) db, err := postgres.Open(serviceName, fmt.Sprintf("dbname=postgres user=%s password=%s", username, password)) if err != nil { log.Fatal(err) } r := martini.NewRouter() m := martini.New() m.Use(martini.Logger()) m.Use(martini.Recovery()) m.Use(render.Renderer()) m.Action(r.Handle) m.Map(db) r.Post("/databases", createDatabase) r.Get("/ping", ping) port := os.Getenv("PORT") if port == "" { port = "3000" } addr := ":" + port if err := discoverd.Register(serviceName+"-api", addr); err != nil { log.Fatal(err) } log.Fatal(http.ListenAndServe(addr, m)) }
func main() { port := os.Getenv("PORT") if port == "" { port = "3000" } addr := ":" + port db, err := postgres.Open("", "") if err != nil { log.Fatal(err) } if err := migrateDB(db.DB); err != nil { log.Fatal(err) } cc, err := cluster.NewClient() if err != nil { log.Fatal(err) } sc, err := strowgerc.New() if err != nil { log.Fatal(err) } if err := discoverd.Register("flynn-controller", addr); err != nil { log.Fatal(err) } handler, _ := appHandler(handlerConfig{db: db, cc: cc, sc: sc, dc: discoverd.DefaultClient, key: os.Getenv("AUTH_KEY")}) log.Fatal(http.ListenAndServe(addr, handler)) }
func main() { flag.Parse() addr := os.Getenv("PORT") if addr == "" { addr = *listenPort } addr = ":" + addr var fs Filesystem var storageDesc string if *storageDir != "" { fs = NewOSFilesystem(*storageDir) storageDesc = *storageDir } else { db, err := postgres.Open("", "") if err != nil { log.Fatal(err) } fs, err = NewPostgresFilesystem(db.DB) if err != nil { log.Fatal(err) } storageDesc = "Postgres" if err := discoverd.Register("shelf", addr); err != nil { log.Fatal(err) } } log.Println("Shelf serving files on " + addr + " from " + storageDesc) log.Fatal(http.ListenAndServe(addr, handler(fs))) }
func main() { port := os.Getenv("PORT") if port == "" { port = "3000" } addr := ":" + port if seed := os.Getenv("NAME_SEED"); seed != "" { s, err := hex.DecodeString(seed) if err != nil { log.Fatalln("error decoding NAME_SEED:", err) } name.SetSeed(s) } db, err := postgres.Open("", "") if err != nil { log.Fatal(err) } if err := migrateDB(db.DB); err != nil { log.Fatal(err) } cc, err := cluster.NewClient() if err != nil { log.Fatal(err) } sc, err := routerc.New() if err != nil { log.Fatal(err) } if err := discoverd.Register("flynn-controller", addr); err != nil { log.Fatal(err) } shutdown.BeforeExit(func() { discoverd.Unregister("flynn-controller", addr) }) handler, _ := appHandler(handlerConfig{db: db, cc: cc, sc: sc, dc: discoverd.DefaultClient, key: os.Getenv("AUTH_KEY")}) log.Fatal(http.ListenAndServe(addr, handler)) }
func main() { defer shutdown.Exit() flag.Parse() addr := os.Getenv("PORT") if addr == "" { addr = *listenPort } addr = ":" + addr var fs Filesystem var storageDesc string if *storageDir != "" { fs = NewOSFilesystem(*storageDir) storageDesc = *storageDir } else { db, err := postgres.Open("", "") if err != nil { shutdown.Fatal(err) } fs, err = NewPostgresFilesystem(db.DB) if err != nil { shutdown.Fatal(err) } storageDesc = "Postgres" } if *serviceDiscovery { hb, err := discoverd.AddServiceAndRegister("blobstore", addr) if err != nil { shutdown.Fatal(err) } shutdown.BeforeExit(func() { hb.Close() }) } log.Println("Blobstore serving files on " + addr + " from " + storageDesc) http.Handle("/", handler(fs)) status.AddHandler(fs.Status) shutdown.Fatal(http.ListenAndServe(addr, nil)) }
func main() { defer shutdown.Exit() var cookieKey *[32]byte if key := os.Getenv("COOKIE_KEY"); key != "" { res, err := base64.StdEncoding.DecodeString(key) if err != nil { shutdown.Fatalf("error decoding COOKIE_KEY: %s", err) } if len(res) != 32 { shutdown.Fatalf("decoded %d bytes from COOKIE_KEY, expected 32", len(res)) } var k [32]byte copy(k[:], res) cookieKey = &k } if cookieKey == nil { shutdown.Fatal("Missing random 32 byte base64-encoded COOKIE_KEY") } httpPort := flag.String("http-port", "8080", "http listen port") httpsPort := flag.String("https-port", "4433", "https listen port") tcpIP := flag.String("tcp-ip", os.Getenv("LISTEN_IP"), "tcp router listen ip") tcpRangeStart := flag.Int("tcp-range-start", 3000, "tcp port range start") tcpRangeEnd := flag.Int("tcp-range-end", 3500, "tcp port range end") certFile := flag.String("tls-cert", "", "TLS (SSL) cert file in pem format") keyFile := flag.String("tls-key", "", "TLS (SSL) key file in pem format") apiPort := flag.String("api-port", "", "api listen port") flag.Parse() if *apiPort == "" { *apiPort = os.Getenv("PORT") if *apiPort == "" { *apiPort = "5000" } } keypair := tls.Certificate{} var err error if *certFile != "" { if keypair, err = tls.LoadX509KeyPair(*certFile, *keyFile); err != nil { shutdown.Fatal(err) } } else if tlsCert := os.Getenv("TLSCERT"); tlsCert != "" { if tlsKey := os.Getenv("TLSKEY"); tlsKey != "" { os.Setenv("TLSKEY", fmt.Sprintf("md5^(%s)", md5sum(tlsKey))) if keypair, err = tls.X509KeyPair([]byte(tlsCert), []byte(tlsKey)); err != nil { shutdown.Fatal(err) } } } log := logger.New("fn", "main") log.Info("connecting to postgres") db, err := postgres.Open("", "") if err != nil { log.Error("error connecting to postgres", "err", err) shutdown.Fatal(err) } log.Info("running DB migrations") if err := migrateDB(db.DB); err != nil { log.Error("error running DB migrations", "err", err) shutdown.Fatal(err) } var pgport int if port := os.Getenv("PGPORT"); port != "" { var err error if pgport, err = strconv.Atoi(port); err != nil { shutdown.Fatal(err) } } log.Info("creating postgres connection pool") pgxpool, err := pgx.NewConnPool(pgx.ConnPoolConfig{ ConnConfig: pgx.ConnConfig{ Host: os.Getenv("PGHOST"), Port: uint16(pgport), Database: os.Getenv("PGDATABASE"), User: os.Getenv("PGUSER"), Password: os.Getenv("PGPASSWORD"), }, }) if err != nil { log.Error("error creating postgres connection pool", "err", err) shutdown.Fatal(err) } shutdown.BeforeExit(func() { pgxpool.Close() }) httpAddr := net.JoinHostPort(os.Getenv("LISTEN_IP"), *httpPort) httpsAddr := net.JoinHostPort(os.Getenv("LISTEN_IP"), *httpsPort) r := Router{ TCP: &TCPListener{ IP: *tcpIP, startPort: *tcpRangeStart, endPort: *tcpRangeEnd, ds: NewPostgresDataStore("tcp", pgxpool), discoverd: discoverd.DefaultClient, }, HTTP: &HTTPListener{ Addr: httpAddr, TLSAddr: httpsAddr, cookieKey: cookieKey, keypair: keypair, ds: NewPostgresDataStore("http", pgxpool), discoverd: discoverd.DefaultClient, }, } if err := r.Start(); err != nil { shutdown.Fatal(err) } shutdown.BeforeExit(r.Close) apiAddr := net.JoinHostPort(os.Getenv("LISTEN_IP"), *apiPort) log.Info("starting API listener") listener, err := listenFunc("tcp4", apiAddr) if err != nil { log.Error("error starting API listener", "err", err) shutdown.Fatal(listenErr{apiAddr, err}) } services := map[string]string{ "router-api": apiAddr, "router-http": httpAddr, } for service, addr := range services { log.Info("registering service", "name", service, "addr", addr) hb, err := discoverd.AddServiceAndRegister(service, addr) if err != nil { log.Error("error registering service", "name", service, "addr", addr, "err", err) shutdown.Fatal(err) } shutdown.BeforeExit(func() { hb.Close() }) } log.Info("serving API requests") shutdown.Fatal(http.Serve(listener, apiHandler(&r))) }
func (s *PostgresSuite) testDeploy(t *c.C, d *pgDeploy) { // create postgres app client := s.controllerClient(t) app := &ct.App{Name: d.name, Strategy: "postgres"} t.Assert(client.CreateApp(app), c.IsNil) // copy release from default postgres app release, err := client.GetAppRelease("postgres") t.Assert(err, c.IsNil) release.ID = "" proc := release.Processes["postgres"] delete(proc.Env, "SINGLETON") proc.Env["FLYNN_POSTGRES"] = d.name proc.Service = d.name release.Processes["postgres"] = proc t.Assert(client.CreateRelease(release), c.IsNil) t.Assert(client.SetAppRelease(app.ID, release.ID), c.IsNil) oldRelease := release.ID // create formation discEvents := make(chan *discoverd.Event) discStream, err := s.discoverdClient(t).Service(d.name).Watch(discEvents) t.Assert(err, c.IsNil) defer discStream.Close() jobEvents := make(chan *ct.Job) jobStream, err := client.StreamJobEvents(d.name, jobEvents) t.Assert(err, c.IsNil) defer jobStream.Close() t.Assert(client.PutFormation(&ct.Formation{ AppID: app.ID, ReleaseID: release.ID, Processes: map[string]int{"postgres": d.pgJobs, "web": d.webJobs}, }), c.IsNil) // watch cluster state changes type stateChange struct { state *state.State err error } stateCh := make(chan stateChange) go func() { for event := range discEvents { if event.Kind != discoverd.EventKindServiceMeta { continue } var state state.State if err := json.Unmarshal(event.ServiceMeta.Data, &state); err != nil { stateCh <- stateChange{err: err} return } primary := "" if state.Primary != nil { primary = state.Primary.Addr } sync := "" if state.Sync != nil { sync = state.Sync.Addr } var async []string for _, a := range state.Async { async = append(async, a.Addr) } debugf(t, "got pg cluster state: index=%d primary=%s sync=%s async=%s", event.ServiceMeta.Index, primary, sync, strings.Join(async, ",")) stateCh <- stateChange{state: &state} } }() // wait for correct cluster state and number of web processes var pgState state.State var webJobs int ready := func() bool { if webJobs != d.webJobs { return false } if pgState.Primary == nil { return false } if d.pgJobs > 1 && pgState.Sync == nil { return false } if d.pgJobs > 2 && len(pgState.Async) != d.pgJobs-2 { return false } return true } for { if ready() { break } select { case s := <-stateCh: t.Assert(s.err, c.IsNil) pgState = *s.state case e, ok := <-jobEvents: if !ok { t.Fatalf("job event stream closed: %s", jobStream.Err()) } debugf(t, "got job event: %s %s %s", e.Type, e.ID, e.State) if e.Type == "web" && e.State == "up" { webJobs++ } case <-time.After(30 * time.Second): t.Fatal("timed out waiting for postgres formation") } } // connect to the db so we can test writes db := postgres.Wait(d.name, fmt.Sprintf("dbname=postgres user=flynn password=%s", release.Env["PGPASSWORD"])) dbname := "deploy-test" t.Assert(db.Exec(fmt.Sprintf(`CREATE DATABASE "%s" WITH OWNER = "flynn"`, dbname)), c.IsNil) db.Close() db, err = postgres.Open(d.name, fmt.Sprintf("dbname=%s user=flynn password=%s", dbname, release.Env["PGPASSWORD"])) t.Assert(err, c.IsNil) defer db.Close() t.Assert(db.Exec(`CREATE TABLE deploy_test ( data text)`), c.IsNil) assertWriteable := func() { debug(t, "writing to postgres database") t.Assert(db.Exec(`INSERT INTO deploy_test (data) VALUES ('data')`), c.IsNil) } // check currently writeable assertWriteable() // check a deploy completes with expected cluster state changes release.ID = "" t.Assert(client.CreateRelease(release), c.IsNil) newRelease := release.ID deployment, err := client.CreateDeployment(app.ID, newRelease) t.Assert(err, c.IsNil) deployEvents := make(chan *ct.DeploymentEvent) deployStream, err := client.StreamDeployment(deployment, deployEvents) t.Assert(err, c.IsNil) defer deployStream.Close() // assertNextState checks that the next state received is in the remaining states // that were expected, so handles the fact that some states don't happen, but the // states that do happen are expected and in-order. assertNextState := func(remaining []expectedPgState) int { var state state.State loop: for { select { case s := <-stateCh: t.Assert(s.err, c.IsNil) if len(s.state.Async) < d.expectedAsyncs() { // we shouldn't usually receive states with less asyncs than // expected, but they can occur as an intermediate state between // two expected states (e.g. when a sync does a takeover at the // same time as a new async is started) so just ignore them. debug(t, "ignoring state with too few asyncs") continue } state = *s.state break loop case <-time.After(60 * time.Second): t.Fatal("timed out waiting for postgres cluster state") } } if state.Primary == nil { t.Fatal("no primary configured") } log := func(format string, v ...interface{}) { debugf(t, "skipping expected state: %s", fmt.Sprintf(format, v...)) } outer: for i, expected := range remaining { if state.Primary.Meta["FLYNN_RELEASE_ID"] != expected.Primary { log("primary has incorrect release") continue } if state.Sync == nil { if expected.Sync == "" { return i } log("state has no sync node") continue } if state.Sync.Meta["FLYNN_RELEASE_ID"] != expected.Sync { log("sync has incorrect release") continue } if state.Async == nil { if expected.Async == nil { return i } log("state has no async nodes") continue } if len(state.Async) != len(expected.Async) { log("expected %d asyncs, got %d", len(expected.Async), len(state.Async)) continue } for i, release := range expected.Async { if state.Async[i].Meta["FLYNN_RELEASE_ID"] != release { log("async[%d] has incorrect release", i) continue outer } } return i } t.Fatal("unexpected pg state") return -1 } expected := d.expected(oldRelease, newRelease) var expectedIndex, newWebJobs int loop: for { select { case e, ok := <-deployEvents: if !ok { t.Fatal("unexpected close of deployment event stream") } switch e.Status { case "complete": break loop case "failed": t.Fatalf("deployment failed: %s", e.Error) } debugf(t, "got deployment event: %s %s", e.JobType, e.JobState) if e.JobState != "up" && e.JobState != "down" { continue } switch e.JobType { case "postgres": // move on if we have seen all the expected events if expectedIndex >= len(expected) { continue } skipped := assertNextState(expected[expectedIndex:]) expectedIndex += 1 + skipped case "web": if e.JobState == "up" && e.ReleaseID == newRelease { newWebJobs++ } } case <-time.After(2 * time.Minute): t.Fatal("timed out waiting for deployment") } } // check we have the correct number of new web jobs t.Assert(newWebJobs, c.Equals, d.webJobs) // check writeable now deploy is complete assertWriteable() }
func main() { defer shutdown.Exit() apiPort := os.Getenv("PORT") if apiPort == "" { apiPort = "5000" } var cookieKey *[32]byte if key := os.Getenv("COOKIE_KEY"); key != "" { res, err := base64.StdEncoding.DecodeString(key) if err != nil { shutdown.Fatal("error decoding COOKIE_KEY:", err) } var k [32]byte copy(k[:], res) cookieKey = &k } httpAddr := flag.String("httpaddr", ":8080", "http listen address") httpsAddr := flag.String("httpsaddr", ":4433", "https listen address") tcpIP := flag.String("tcpip", "", "tcp router listen ip") tcpRangeStart := flag.Int("tcp-range-start", 3000, "tcp port range start") tcpRangeEnd := flag.Int("tcp-range-end", 3500, "tcp port range end") certFile := flag.String("tlscert", "", "TLS (SSL) cert file in pem format") keyFile := flag.String("tlskey", "", "TLS (SSL) key file in pem format") apiAddr := flag.String("apiaddr", ":"+apiPort, "api listen address") flag.Parse() keypair := tls.Certificate{} var err error if *certFile != "" { if keypair, err = tls.LoadX509KeyPair(*certFile, *keyFile); err != nil { shutdown.Fatal(err) } } else if tlsCert := os.Getenv("TLSCERT"); tlsCert != "" { if tlsKey := os.Getenv("TLSKEY"); tlsKey != "" { os.Setenv("TLSKEY", fmt.Sprintf("md5^(%s)", md5sum(tlsKey))) if keypair, err = tls.X509KeyPair([]byte(tlsCert), []byte(tlsKey)); err != nil { shutdown.Fatal(err) } } } db, err := postgres.Open("", "") if err != nil { shutdown.Fatal(err) } if err := migrateDB(db.DB); err != nil { shutdown.Fatal(err) } var pgport int if port := os.Getenv("PGPORT"); port != "" { var err error if pgport, err = strconv.Atoi(port); err != nil { shutdown.Fatal(err) } } pgxpool, err := pgx.NewConnPool(pgx.ConnPoolConfig{ ConnConfig: pgx.ConnConfig{ Host: os.Getenv("PGHOST"), Port: uint16(pgport), Database: os.Getenv("PGDATABASE"), User: os.Getenv("PGUSER"), Password: os.Getenv("PGPASSWORD"), }, }) if err != nil { shutdown.Fatal(err) } shutdown.BeforeExit(func() { pgxpool.Close() }) r := Router{ TCP: &TCPListener{ IP: *tcpIP, startPort: *tcpRangeStart, endPort: *tcpRangeEnd, ds: NewPostgresDataStore("tcp", pgxpool), discoverd: discoverd.DefaultClient, }, HTTP: &HTTPListener{ Addr: *httpAddr, TLSAddr: *httpsAddr, cookieKey: cookieKey, keypair: keypair, ds: NewPostgresDataStore("http", pgxpool), discoverd: discoverd.DefaultClient, }, } if err := r.Start(); err != nil { shutdown.Fatal(err) } listener, err := listenFunc("tcp4", *apiAddr) if err != nil { shutdown.Fatal(listenErr{*apiAddr, err}) } services := map[string]string{ "router-api": *apiAddr, "router-http": *httpAddr, } for service, addr := range services { hb, err := discoverd.AddServiceAndRegister(service, addr) if err != nil { shutdown.Fatal(err) } shutdown.BeforeExit(func() { hb.Close() }) } shutdown.Fatal(http.Serve(listener, apiHandler(&r))) }