func createDatabase(db *postgres.DB, r render.Render) { username, password, database := random.Hex(16), random.Hex(16), random.Hex(16) if _, err := db.Exec(fmt.Sprintf(`CREATE USER "%s" WITH PASSWORD '%s'`, username, password)); err != nil { log.Println(err) r.JSON(500, struct{}{}) return } if _, err := db.Exec(fmt.Sprintf(`CREATE DATABASE "%s" WITH OWNER = "%s"`, database, username)); err != nil { db.Exec(fmt.Sprintf(`DROP USER "%s"`, username)) log.Println(err) r.JSON(500, struct{}{}) return } r.JSON(200, &resource{ ID: fmt.Sprintf("/databases/%s:%s", username, database), Env: map[string]string{ "FLYNN_POSTGRES": serviceName, "PGUSER": username, "PGPASSWORD": password, "PGDATABASE": database, }, }) }
func (a *API) createDatabase(ctx context.Context, w http.ResponseWriter, req *http.Request) { username, password, database := random.Hex(16), random.Hex(16), random.Hex(16) if _, err := a.db.Exec(fmt.Sprintf("CREATE USER '%s'@'%%' IDENTIFIED BY '%s'", username, password)); err != nil { httphelper.Error(w, err) return } if _, err := a.db.Exec(fmt.Sprintf("CREATE DATABASE `%s`", database)); err != nil { a.db.Exec(fmt.Sprintf("DROP USER '%s'", username)) httphelper.Error(w, err) return } if _, err := a.db.Exec(fmt.Sprintf("GRANT ALL ON `%s`.* TO '%s'@'%%'", database, username)); err != nil { a.db.Exec(fmt.Sprintf("DROP DATABASE `%s`", database)) a.db.Exec(fmt.Sprintf("DROP USER '%s'", username)) httphelper.Error(w, err) return } url := fmt.Sprintf("mysql://%s:%s@%s:3306/%s", username, password, serviceHost, database) httphelper.JSON(w, 200, resource.Resource{ ID: fmt.Sprintf("/databases/%s:%s", username, database), Env: map[string]string{ "FLYNN_MYSQL": serviceName, "MYSQL_HOST": serviceHost, "MYSQL_USER": username, "MYSQL_PWD": password, "MYSQL_DATABASE": database, "DATABASE_URL": url, }, }) }
func (p *pgAPI) createDatabase(ctx context.Context, w http.ResponseWriter, req *http.Request) { username, password, database := random.Hex(16), random.Hex(16), random.Hex(16) if err := p.db.Exec(fmt.Sprintf(`CREATE USER "%s" WITH PASSWORD '%s'`, username, password)); err != nil { httphelper.Error(w, err) return } if err := p.db.Exec(fmt.Sprintf(`CREATE DATABASE "%s"`, database)); err != nil { p.db.Exec(fmt.Sprintf(`DROP USER "%s"`, username)) httphelper.Error(w, err) return } if err := p.db.Exec(fmt.Sprintf(`GRANT ALL ON DATABASE "%s" TO "%s"`, database, username)); err != nil { p.db.Exec(fmt.Sprintf(`DROP DATABASE "%s"`, database)) p.db.Exec(fmt.Sprintf(`DROP USER "%s"`, username)) httphelper.Error(w, err) return } url := fmt.Sprintf("postgres://%s:%s@%s:5432/%s", username, password, serviceHost, database) httphelper.JSON(w, 200, resource.Resource{ ID: fmt.Sprintf("/databases/%s:%s", username, database), Env: map[string]string{ "FLYNN_POSTGRES": serviceName, "PGHOST": serviceHost, "PGUSER": username, "PGPASSWORD": password, "PGDATABASE": database, "DATABASE_URL": url, }, }) }
func (s *ControllerSuite) TestKeyRotation(t *c.C) { cc := s.clusterConf(t) oldKey := cc.Key newKey := random.Hex(16) // allow auth to API with old and new keys set := flynn(t, "/", "-a", "controller", "env", "set", "-t", "web", fmt.Sprintf("AUTH_KEY=%s,%s", newKey, oldKey)) t.Assert(set, Succeeds) // reconfigure components to use new key for _, app := range []string{"gitreceive", "taffy", "dashboard"} { set := flynn(t, "/", "-a", app, "env", "set", "CONTROLLER_KEY="+newKey) t.Assert(set, Succeeds) } // write a new flynnrc cc.Key = newKey conf := &config.Config{} err := conf.Add(cc, true) t.Assert(err, c.IsNil) err = conf.SaveTo(flynnrc) t.Assert(err, c.IsNil) // clear any cached configs s.Helper.config = nil s.Helper.controller = nil // use new key for deployer+controller set = flynn(t, "/", "-a", "controller", "env", "set", "AUTH_KEY="+newKey) t.Assert(set, Succeeds) // remove old key from API set = flynn(t, "/", "-a", "controller", "env", "unset", "-t", "web", "AUTH_KEY") t.Assert(set, Succeeds) }
func (s *httpInstaller) YesNoPrompt(msg string) bool { prompt := &httpPrompt{ ID: random.Hex(16), Type: "yes_no", Message: msg, resChan: make(chan *httpPrompt), api: s.api, } prompt.api.InstallerPromptsMtx.Lock() prompt.api.InstallerPrompts[prompt.ID] = prompt prompt.api.InstallerPromptsMtx.Unlock() s.sendEvent(&httpEvent{ Type: "prompt", Prompt: prompt, }) res := <-prompt.resChan s.sendEvent(&httpEvent{ Type: "prompt", Prompt: prompt, }) return res.Yes }
func (a *GenRandomAction) Run(s *State) error { if a.Length == 0 { a.Length = 16 } data := interpolate(s, a.Data) if data == "" { switch a.Encoding { case "", "hex": data = random.Hex(a.Length) case "base64": data = base64.StdEncoding.EncodeToString(random.Bytes(a.Length)) case "base64safe": data = random.Base64(a.Length) case "uuid": data = random.UUID() default: return fmt.Errorf("bootstrap: unknown random type: %q", a.Encoding) } } s.StepData[a.ID] = &RandomData{Data: data} if a.ControllerKey { s.SetControllerKey(data) } return nil }
func (s *httpInstaller) PromptInput(msg string) string { prompt := &httpPrompt{ ID: random.Hex(16), Type: "input", Message: msg, resChan: make(chan *httpPrompt), api: s.api, } s.api.InstallerPromptsMtx.Lock() s.api.InstallerPrompts[prompt.ID] = prompt s.api.InstallerPromptsMtx.Unlock() s.sendEvent(&httpEvent{ Type: "prompt", Prompt: prompt, }) res := <-prompt.resChan s.sendEvent(&httpEvent{ Type: "prompt", Prompt: prompt, }) return res.Input }
func (a *API) createDatabase(w http.ResponseWriter, req *http.Request, _ httprouter.Params) { // Ensure the cluster has been scaled up before attempting to create a database. if err := a.scaleUp(); err != nil { httphelper.Error(w, err) return } session, err := mgo.DialWithInfo(&mgo.DialInfo{ Addrs: []string{net.JoinHostPort(serviceHost, "27017")}, Username: "******", Password: os.Getenv("MONGO_PWD"), Database: "admin", }) if err != nil { httphelper.Error(w, err) return } defer session.Close() username, password, database := random.Hex(16), random.Hex(16), random.Hex(16) // Create a user if err := session.DB(database).Run(bson.D{ {"createUser", username}, {"pwd", password}, {"roles", []bson.M{ {"role": "dbOwner", "db": database}, }}, }, nil); err != nil { httphelper.Error(w, err) return } url := fmt.Sprintf("mongodb://%s:%s@%s:27017/%s", username, password, serviceHost, database) httphelper.JSON(w, 200, resource.Resource{ ID: fmt.Sprintf("/databases/%s:%s", username, database), Env: map[string]string{ "FLYNN_MONGO": serviceName, "MONGO_HOST": serviceHost, "MONGO_USER": username, "MONGO_PWD": password, "MONGO_DATABASE": database, "DATABASE_URL": url, }, }) }
func (c *BaseCluster) YesNoPrompt(msg string) bool { res := c.sendPrompt(&Prompt{ ID: random.Hex(16), Type: "yes_no", Message: msg, resChan: make(chan *Prompt), cluster: c, }) return res.Yes }
func (c *BaseCluster) PromptInput(msg string) string { res := c.sendPrompt(&Prompt{ ID: random.Hex(16), Type: "input", Message: msg, resChan: make(chan *Prompt), cluster: c, }) return res.Input }
func (c *BaseCluster) prompt(typ, msg string) *Prompt { if c.State != "starting" { return &Prompt{} } res := c.sendPrompt(&Prompt{ ID: random.Hex(16), Type: typ, Message: msg, resChan: make(chan *Prompt), cluster: c, }) return res }
func (api *httpAPI) InstallHandler(w http.ResponseWriter, req *http.Request, params httprouter.Params) { var input *jsonInput if err := httphelper.DecodeJSON(req, &input); err != nil { httphelper.Error(w, err) return } api.InstallerStackMtx.Lock() defer api.InstallerStackMtx.Unlock() if len(api.InstallerStacks) > 0 { httphelper.ObjectExistsError(w, "install already started") return } var id = random.Hex(16) var creds aws.CredentialsProvider if input.Creds.AccessKeyID != "" && input.Creds.SecretAccessKey != "" { creds = aws.Creds(input.Creds.AccessKeyID, input.Creds.SecretAccessKey, "") } else { var err error creds, err = aws.EnvCreds() if err != nil { httphelper.ValidationError(w, "", err.Error()) return } } s := &httpInstaller{ ID: id, PromptOutChan: make(chan *httpPrompt), PromptInChan: make(chan *httpPrompt), logger: log.New(), api: api, } s.Stack = &Stack{ Creds: creds, Region: input.Region, InstanceType: input.InstanceType, NumInstances: input.NumInstances, VpcCidr: input.VpcCidr, SubnetCidr: input.SubnetCidr, PromptInput: s.PromptInput, YesNoPrompt: s.YesNoPrompt, } if err := s.Stack.RunAWS(); err != nil { httphelper.Error(w, err) return } api.InstallerStacks[id] = s go s.handleEvents() httphelper.JSON(w, 200, s) }
func (a *GenRandomAction) Run(s *State) error { if a.Length == 0 { a.Length = 16 } data := interpolate(s, a.Data) if data == "" { data = random.Hex(a.Length) } s.StepData[a.ID] = &RandomData{Data: data} if a.ControllerKey { s.SetControllerKey(data) } return nil }
func (p *pgAPI) createDatabase(w http.ResponseWriter, req *http.Request, _ httprouter.Params) { username, password, database := random.Hex(16), random.Hex(16), random.Hex(16) if err := p.db.Exec(fmt.Sprintf(`CREATE USER "%s" WITH PASSWORD '%s'`, username, password)); err != nil { httphelper.Error(w, err) return } if err := p.db.Exec(fmt.Sprintf(`CREATE DATABASE "%s" WITH OWNER = "%s"`, database, username)); err != nil { p.db.Exec(fmt.Sprintf(`DROP USER "%s"`, username)) httphelper.Error(w, err) return } httphelper.JSON(w, 200, resource.Resource{ ID: fmt.Sprintf("/databases/%s:%s", username, database), Env: map[string]string{ "FLYNN_POSTGRES": serviceName, "PGHOST": serviceHost, "PGUSER": username, "PGPASSWORD": password, "PGDATABASE": database, }, }) }
func (c *BaseCluster) prompt(typ PromptType, msg string) *Prompt { state := c.getState() if state != "starting" && state != "deleting" { return &Prompt{} } res := c.sendPrompt(&Prompt{ ID: random.Hex(16), Type: typ, Message: msg, resChan: make(chan *Prompt), errChan: make(chan error), cluster: c, }) return res }
func testFilesystem(fs Filesystem, testMeta bool, t *testing.T) { srv := httptest.NewServer(handler(fs)) defer srv.Close() var wg sync.WaitGroup wg.Add(concurrency) for i := 0; i < concurrency; i++ { go func() { defer wg.Done() path := srv.URL + "/foo/bar/" + random.Hex(16) res, err := http.Get(path) if err != nil { t.Fatal(err) } res.Body.Close() if res.StatusCode != 404 { t.Errorf("Expected 404 for non-existent file, got %d", res.StatusCode) } res, err = http.Head(path) if err != nil { t.Fatal(err) } res.Body.Close() if res.StatusCode != 404 { t.Errorf("Expected 404 for non-existent file, got %d", res.StatusCode) } data := random.Hex(16) req, err := http.NewRequest("PUT", path, strings.NewReader(data)) if err != nil { t.Fatal(err) } req.Header.Set("Content-Type", "text/plain") res, err = http.DefaultClient.Do(req) if err != nil { t.Fatal(err) } res.Body.Close() if res.StatusCode != 200 { t.Errorf("Expected 200 for successful PUT, got %d", res.StatusCode) } res, err = http.Get(path) if err != nil { t.Fatal(err) } resData, err := ioutil.ReadAll(res.Body) res.Body.Close() if err != nil { t.Fatal(err) } if res.StatusCode != 200 { t.Errorf("Expected 200 for GET, got %d", res.StatusCode) } if string(resData) != data { t.Errorf("Expected data to be %q, got %q", data, string(resData)) } res, err = http.Head(path) if err != nil { t.Fatal(err) } res.Body.Close() if res.StatusCode != 200 { t.Errorf("Expected 200 for HEAD, got %d", res.StatusCode) } if cl := res.Header.Get("Content-Length"); cl != "32" { t.Errorf(`Expected Content-Length to be "32", got %q`, cl) } if testMeta { if ct := res.Header.Get("Content-Type"); ct != "text/plain" { t.Errorf(`Expected Content-Type to be "text/plain", got %q`, ct) } etag := res.Header.Get("Etag") if etag == "" { t.Error("Expected ETag to be set") } req, err := http.NewRequest("GET", path, nil) if err != nil { t.Fatal(err) } req.Header.Set("If-None-Match", etag) res, err = http.DefaultClient.Do(req) if err != nil { t.Fatal(err) } res.Body.Close() if res.StatusCode != http.StatusNotModified { t.Errorf("Expected ETag GET status to be 304, got %d", res.StatusCode) } } newData := random.Hex(32) req, err = http.NewRequest("PUT", path, strings.NewReader(newData)) if err != nil { shutdown.Fatal(err) } req.Header.Set("Content-Type", "application/text") res, err = http.DefaultClient.Do(req) if err != nil { shutdown.Fatal(err) } res.Body.Close() if res.StatusCode != 200 { t.Errorf("Expected 200 for update PUT, got %d", res.StatusCode) } var wg2 sync.WaitGroup wg2.Add(concurrency) for i := 0; i < concurrency; i++ { go func() { defer wg2.Done() res, err := http.Get(path) if err != nil { t.Fatal(err) } resData, err := ioutil.ReadAll(res.Body) res.Body.Close() if err != nil { t.Fatal(err) } if res.StatusCode != 200 { t.Errorf("Expected 200 for update GET, got %d", res.StatusCode) } if string(resData) != newData { t.Errorf("Expected data to be %q, got %q", newData, string(resData)) } res, err = http.Head(path) if err != nil { t.Fatal(err) } res.Body.Close() if res.StatusCode != 200 { t.Errorf("Expected 200 for update HEAD, got %d", res.StatusCode) } if cl := res.Header.Get("Content-Length"); cl != "64" { t.Errorf(`Expected Content-Length to be "64", got %q`, cl) } if testMeta { if ct := res.Header.Get("Content-Type"); ct != "application/text" { t.Errorf(`Expected Content-Type to be "application/text", got %q`, ct) } } }() } wg2.Wait() req, err = http.NewRequest("DELETE", path, nil) if err != nil { shutdown.Fatal(err) } res, err = http.DefaultClient.Do(req) if err != nil { shutdown.Fatal(err) } res.Body.Close() if res.StatusCode != 200 { t.Errorf("Expected 200 for DELETE, got %d", res.StatusCode) } res, err = http.Get(path) if err != nil { t.Fatal(err) } res.Body.Close() if res.StatusCode != 404 { t.Errorf("Expected 200 for deleted GET, got %d", res.StatusCode) } res, err = http.Head(path) if err != nil { t.Fatal(err) } res.Body.Close() if res.StatusCode != 404 { t.Errorf("Expected 200 for deleted HEAD, got %d", res.StatusCode) } }() } wg.Wait() }
func fakeHostID() string { return random.Hex(16) }