func httpAuth(h http.HandlerFunc) http.HandlerFunc { return func(w http.ResponseWriter, request *http.Request) { if len(request.Header["Authorization"]) == 0 { log.Trace(fmt.Sprintf("httpAuth(): Authorization Required")) http.Error(w, "Authorization Required", http.StatusUnauthorized) return } auth := strings.SplitN(request.Header["Authorization"][0], " ", 2) if len(auth) != 2 || auth[0] != "Basic" { log.Error(fmt.Sprintf("httpAuth(): Unhandled Authorization Type, Expected Basic")) http.Error(w, "Unhandled Authroization Type, Expected Basic\n", http.StatusBadRequest) return } payload, err := base64.StdEncoding.DecodeString(auth[1]) if err != nil { log.Error(fmt.Sprintf("httpAuth(): Authorization Failed")) http.Error(w, "Authorization Failed\n", http.StatusUnauthorized) return } nv := strings.SplitN(string(payload), ":", 2) if (len(nv) != 2) || !isAuthorized(nv[0], nv[1]) { log.Error(fmt.Sprintf("httpAuth(): Authorization Failed")) http.Error(w, "Authorization Failed\n", http.StatusUnauthorized) return } h(w, request) } }
// TODO: Instead pass back *sql.DB func (r *RDPG) OpenDB(dbname string) error { if r.DB == nil { u, err := url.Parse(r.URI) if err != nil { log.Error(fmt.Sprintf("Failed parsing URI %s err: %s", r.URI, err)) } u.Path = dbname r.URI = u.String() db, err := sqlx.Connect("postgres", r.URI) if err != nil { log.Error(fmt.Sprintf("Failed connecting to %s err: %s", rdpgURI, err)) return err } r.DB = db } else { err := r.DB.Ping() if err != nil { db, err := sqlx.Connect("postgres", r.URI) if err != nil { log.Error(fmt.Sprintf("Failed connecting to %s err: %s", rdpgURI, err)) proc, _ := os.FindProcess(os.Getpid()) proc.Signal(syscall.SIGTERM) return err } r.DB = db } } return nil }
func (c *Catalog) Fetch() (err error) { r := rdpg.New() err = r.OpenDB("rdpg") if err != nil { log.Error(fmt.Sprintf("Failed fetching catalog from database: %s", err)) return } db := r.DB err = db.Select(&c.Services, `SELECT service_id,name,description,bindable FROM cfsb.services;`) if err != nil { log.Error(fmt.Sprintf("Catalog#Fetch() selecting from cfsb.services %s", err.Error())) return } // TODO: Account for plans being associated with a service. for i, _ := range c.Services { service := &c.Services[i] err = db.Select(&service.Plans, `SELECT plan_id,name,description FROM cfsb.plans;`) if err != nil { log.Error(fmt.Sprintf("Catalog#Fetch() Service Plans %s", err.Error())) return } c.Services[i].Tags = []string{"rdpg", "postgresql"} // c.Services[i].Dashboard = DashboardClient{} } return }
func (n *Node) CreateDatabase(dbname, owner string) (err error) { n.Database = "postgres" db, err := n.Connect() if err != nil { log.Error(fmt.Sprintf("Node#CreateDatabase(%s) %s ! %s", dbname, n.Host, err)) return } defer db.Close() sq := fmt.Sprintf(`CREATE DATABASE %s WITH OWNER %s TEMPLATE template0 ENCODING 'UTF8'`, dbname, owner) log.Trace(fmt.Sprintf(`Node#CreateDatabase(%s) %s > %s`, dbname, n.Host, sq)) _, err = db.Query(sq) if err != nil { log.Error(fmt.Sprintf("Node#CreateDatabase(%s) %s ! %s", dbname, n.Host, err)) return } sq = fmt.Sprintf(`REVOKE ALL ON DATABASE "%s" FROM public`, dbname) log.Trace(fmt.Sprintf(`Node#CreateDatabase(%s) %s > %s`, dbname, n.Host, sq)) _, err = db.Exec(sq) if err != nil { log.Error(fmt.Sprintf("Node#CreateDatabase(%s) %s ! %s", dbname, n.Host, err)) } sq = fmt.Sprintf(`GRANT ALL PRIVILEGES ON DATABASE %s TO %s`, dbname, owner) log.Trace(fmt.Sprintf(`Node#CreateDatabase(%s) %s > %s`, dbname, n.Host, sq)) _, err = db.Query(sq) if err != nil { log.Error(fmt.Sprintf(`Node#CreateDatabase(%s) %s ! %s`, dbname, n.Host, err)) return } return nil }
/* (FC) GET /v2/catalog */ func CatalogHandler(w http.ResponseWriter, request *http.Request) { log.Trace(fmt.Sprintf("%s /v2/catalog", request.Method)) switch request.Method { case "GET": c := Catalog{} err := c.Fetch() if err != nil { msg := fmt.Sprintf(`{"status": %d, "description": "%s"}`, http.StatusInternalServerError, err) log.Error(msg) http.Error(w, msg, http.StatusInternalServerError) return } jsonCatalog, err := json.Marshal(c) if err != nil { msg := fmt.Sprintf(`{"status": %d, "description": "%s"}`, http.StatusInternalServerError, err) log.Error(msg) http.Error(w, msg, http.StatusInternalServerError) } else { w.Header().Set("Content-Type", "application/json; charset=UTF-8") w.WriteHeader(http.StatusOK) w.Write(jsonCatalog) } default: msg := fmt.Sprintf(`{"status": %d, "description": "Allowed Methods: GET"}`, http.StatusMethodNotAllowed) log.Error(msg) http.Error(w, msg, http.StatusMethodNotAllowed) } return }
func (r *RDPG) CreateUser(username, password string) (err error) { for _, node := range r.Nodes() { node.Database = `postgres` db, err := node.Connect() if err != nil { log.Error(fmt.Sprintf(`RDPG#CreateUser(%s) %s ! %s`, username, node.Host, err)) return err } // TODO: Check if user exists first sq := fmt.Sprintf(`CREATE USER %s;`, username) log.Trace(fmt.Sprintf(`RDPG#CreateUser(%s) %s > %s`, username, node.Host, sq)) _, err = db.Exec(sq) if err != nil { log.Error(fmt.Sprintf("RDPG#CreateUser(%s) %s ! %s", username, node.Host, err)) db.Close() return err } sq = fmt.Sprintf(`ALTER USER %s ENCRYPTED PASSWORD '%s'`, username, password) log.Trace(fmt.Sprintf(`RDPG#CreateUser(%s) %s > %s`, username, node.Host, sq)) _, err = db.Exec(sq) if err != nil { log.Error(fmt.Sprintf("RDPG#CreateUser(%s) %s ! %s", username, node.Host, err)) } db.Close() } return nil }
/* POST /services/{service}/{action} */ func ServiceHandler(w http.ResponseWriter, request *http.Request) { vars := mux.Vars(request) log.Trace(fmt.Sprintf("%s /services/%s/%s", request.Method, vars["service"], vars["action"])) switch request.Method { case "PUT": service, err := NewService(vars["service"]) if err != nil { log.Error(fmt.Sprintf("ServiceHandler(): NewService(%s)")) http.Error(w, `{"status": %d, "description": "%s"}`, http.StatusInternalServerError) return } switch vars["action"] { case "configure": err := service.Configure() if err != nil { msg := fmt.Sprintf(`{"status": %d, "description": "%s"}`, http.StatusInternalServerError, err) log.Error(msg) http.Error(w, msg, http.StatusInternalServerError) } msg := fmt.Sprintf(`{"status": %d, "description": "%s %s"}`, http.StatusOK, vars["service"], vars["action"]) log.Trace(msg) w.WriteHeader(http.StatusOK) fmt.Fprintf(w, msg) default: msg := fmt.Sprintf(`{"status": %d, "description": "Invalid Action %s for %s"}`, http.StatusBadRequest, vars["action"], vars["service"]) log.Error(msg) http.Error(w, msg, http.StatusBadRequest) } default: msg := fmt.Sprintf(`{"status": %d, "description": "Method not allowed %s"}`, http.StatusMethodNotAllowed, request.Method) log.Error(msg) http.Error(w, msg, http.StatusMethodNotAllowed) } }
func (r *RDPG) DropDatabase(dbname string) (err error) { nodes := r.Nodes() for i := len(nodes) - 1; i >= 0; i-- { node := nodes[i] node.Database = "postgres" db, err := node.Connect() if err != nil { log.Error(fmt.Sprintf("RDPG#DropDatabase(%s) %s ! %s", dbname, node.Host, err)) return err } // sq := fmt.Sprintf(SELECT slot_name FROM pg_replication_slots WHERE database='%s',dbname); // pg_recvlogical --drop-slot // TODO: How do we drop a database in bdr properly? sq := fmt.Sprintf(`DROP DATABASE IF EXISTS %s`, dbname) log.Trace(fmt.Sprintf(`RDPG#DropDatabase(%s) %s DROP > %s`, dbname, node.Host, sq)) _, err = db.Exec(sq) if err != nil { log.Error(fmt.Sprintf("RDPG#DropDatabase(%s) DROP %s ! %s", dbname, node.Host, err)) } db.Close() } return nil }
func (r *RDPG) connect() (db *sqlx.DB, err error) { db, err = sqlx.Connect("postgres", r.URI) if err != nil { log.Error(fmt.Sprintf("rdpg.Node#Connect() %s ! %s", r.URI, err)) } return db, err }
// TODO: RDPG Struct => RDPG Struct, allowing for multiple instances of RDPG func NewRDPG(uri string) *RDPG { if uri == "" || uri[0:13] != "postgresql://" { log.Error(fmt.Sprintf("rdpg.NewRDPG() uri malformed ! %s", uri)) return nil } return &RDPG{URI: uri} }
func RemoveBinding(bindingId string) (binding *Binding, err error) { binding, err = FindBinding(bindingId) if err != nil { log.Error(fmt.Sprintf(`cfsb.CreateBinding(%s) ! %s`, bindingId, err)) return } r := rdpg.New() sq := `UPDATE cfsb.bindings SET ineffective_at = CURRENT_TIMESTAMP WHERE binding_id = $1;` log.Trace(fmt.Sprintf(`cfsb.RemoveBinding(%s) > %s`, bindingId, sq)) r.OpenDB("rdpg") _, err = r.DB.Query(sq, bindingId) if err != nil { log.Error(fmt.Sprintf(`cfsb.CreateBinding(%s) ! %s`, bindingId, err)) } r.DB.Close() return }
func (n *Node) Connect() (db *sqlx.DB, err error) { uri := n.URI() db, err = sqlx.Connect(`postgres`, uri) // n.LocalDSN) if err != nil { log.Error(fmt.Sprintf(`rdpg.Node#Connect() %s ! %s`, uri, err)) return db, err } return db, nil }
func (i *Instance) Provision() (err error) { i.Pass = strings.ToLower(strings.Replace(rdpg.NewUUID().String(), "-", "", -1)) r := rdpg.New() // TODO: Alter this logic based on "plan" err = r.CreateUser(i.User, i.Pass) if err != nil { log.Error(fmt.Sprintf("Instance#Provision(%s) CreateUser(%s) ! %s", i.InstanceId, i.User, err)) return err } err = r.CreateDatabase(i.Database, i.User) if err != nil { log.Error(fmt.Sprintf("Instance#Provision(%s) CreateDatabase(%s,%s) ! %s", i.InstanceId, i.Database, i.User, err)) return err } err = r.CreateReplicationGroup(i.Database) if err != nil { log.Error(fmt.Sprintf("Instance#Provision(%s) CreateReplicationGroup(%s) ! %s", i.InstanceId, i.Database, err)) return err } r.OpenDB("rdpg") sq := `INSERT INTO cfsb.instances (instance_id, service_id, plan_id, organization_id, space_id, dbname, uname, pass) VALUES ($1,$2,$3,$4,$5,$6,$7,$8); ` _, err = r.DB.Query(sq, i.InstanceId, i.ServiceId, i.PlanId, i.OrganizationId, i.SpaceId, i.Database, i.User, i.Pass) if err != nil { log.Error(fmt.Sprintf(`Instance#Provision(%s) ! %s`, i.InstanceId, err)) } nodes := r.Nodes() for _, node := range nodes { err := node.AdminAPI("PUT", "services/pgbouncer/configure") if err != nil { log.Error(fmt.Sprintf(`Instance#Provision(%s) %s ! %s`, i.InstanceId, node.Host, err)) } } r.DB.Close() return nil }
func (i *Instance) Remove() (err error) { r := rdpg.New() r.OpenDB("rdpg") _, err = r.DB.Exec(`UPDATE cfsb.instances SET ineffective_at = CURRENT_TIMESTAMP WHERE id=$1`, i.Id) if err != nil { log.Error(fmt.Sprintf("Instance#Remove(%s) ! %s", i.InstanceId, err)) } time.Sleep(1) // Wait for the update to propigate to the other nodes. for _, node := range r.Nodes() { err := node.AdminAPI("PUT", "services/pgbouncer/configure") if err != nil { log.Error(fmt.Sprintf(`Instance#Provision(%s) %s ! %s`, i.InstanceId, node.Host, err)) } } r.DB.Close() return }
func (n *Node) CreateUser(name, password string) (err error) { if n.User != `postgres` { return errors.New(fmt.Sprintf(`Node user is not postgres, can not create a user with '%s'`, n.User)) } uri := n.URI() db, err := sqlx.Connect(`postgres`, uri) if err != nil { log.Error(fmt.Sprintf(`Node#CreateUser(%s) %s ! %s`, name, uri, err)) return err } defer db.Close() err = db.Get(&name, `SELECT rolname FROM pg_roles WHERE rolname=? LIMIT 1;`, name) if err != nil { log.Error(fmt.Sprintf(`Node#CreateUser(%s) %s ! %s`, name, n.Host, err)) return err } if name != "" { log.Debug(fmt.Sprintf(`User %s already exists, skipping.`, name)) return nil } sq := fmt.Sprintf(`CREATE USER %s WITH SUPERUSER CREATEDB CREATEROLE INHERIT;`, name) result, err := db.Exec(sq) rows, _ := result.RowsAffected() if rows > 0 { log.Debug(fmt.Sprintf(`Node#CreateUser(%s) %s User Created`, n.Host, name)) } if err != nil { log.Error(fmt.Sprintf(`Node#CreateUser(%s) %s ! %s`, name, n.Host, err)) return err } sq = fmt.Sprintf(`ALTER USER %s ENCRYPTED PASSWORD %s;`, name, password) _, err = db.Exec(sq) if err != nil { log.Error(fmt.Sprintf(`Node#CreateUser(%s) %s ! %s`, name, n.Host, err)) return err } return nil }
func CreateBinding(instanceId, bindingId string) (binding *Binding, err error) { instance, err := FindInstance(instanceId) if err != nil { log.Error(fmt.Sprintf(`cfsb.CreateBinding(%s,%s) ! %s`, instanceId, bindingId, err)) return } binding = &Binding{BindingId: bindingId, InstanceId: instanceId} dns := instance.ExternalDNS() s := strings.Split(dns, ":") binding.Creds = Credentials{ URI: instance.URI(), DSN: instance.DSN(), JDBCURI: "jdbc:" + instance.URI(), Host: s[0], Port: s[1], UserName: instance.User, Password: instance.Pass, Database: instance.Database, } r := rdpg.New() r.OpenDB("rdpg") sq := `INSERT INTO cfsb.bindings (instance_id,binding_id) VALUES ($1,$2);` _, err = r.DB.Query(sq, binding.InstanceId, binding.BindingId) if err != nil { log.Error(fmt.Sprintf(`cfsb.CreateBinding(%s) ! %s`, bindingId, err)) } sq = `INSERT INTO cfsb.credentials (instance_id,binding_id,host,port,uname,pass,dbname) VALUES ($1,$2,$3,$4,$5,$6,$7);` _, err = r.DB.Query(sq, binding.InstanceId, binding.BindingId, binding.Creds.Host, binding.Creds.Port, binding.Creds.UserName, binding.Creds.Password, binding.Creds.Database) if err != nil { log.Error(fmt.Sprintf(`cfsb.CreateBinding(%s) ! %s`, bindingId, err)) } r.DB.Close() return }
func (n *Node) CreateExtensions(exts []string) (err error) { db, err := n.Connect() if err != nil { log.Error(fmt.Sprintf("Node#CreateExtensions() %s ! %s", n.URI(), err)) return } defer db.Close() _, err = db.Query(`CREATE EXTENSION IF NOT EXISTS btree_gist`) if err != nil { log.Error(fmt.Sprintf("Node#CreateExtensions() %s ! %s", n.Host, err)) return } _, err = db.Query(`CREATE EXTENSION IF NOT EXISTS bdr`) if err != nil { log.Error(fmt.Sprintf("Node#CreateExtensions() %s ! %s", n.Host, err)) return } return }
func FindPlan(planId string) (plan *Plan, err error) { r := rdpg.New() r.OpenDB("rdpg") plan = &Plan{} sq := `SELECT id,name,description FROM cfsb.plans WHERE id=$1 LIMIT 1;` err = r.DB.Get(&plan, sq, planId) if err != nil { log.Error(fmt.Sprintf("cfsb.FindPlan(%s) %s", planId, err)) } r.DB.Close() return plan, err }
func (r *RDPG) DropUser(name string) (err error) { nodes := r.Nodes() for i := len(nodes) - 1; i >= 0; i-- { node := nodes[i] node.Database = "postgres" db, err := node.Connect() if err != nil { log.Error(fmt.Sprintf("RDPG#DropUser(%s) %s ! %s", name, node.Host, err)) return err } sq := fmt.Sprintf(`DROP USER %s`, name) log.Trace(fmt.Sprintf(`RDPG#DropUser(%s) %s > %s`, name, node.Host, sq)) _, err = db.Exec(sq) if err != nil { log.Error(fmt.Sprintf("RDPG#DropUser(%s) %s ! %s", name, node.Host, err)) } db.Close() } return nil }
func (r *RDPG) Nodes() (nodes []Node) { db, err := r.connect() if err != nil { log.Error(fmt.Sprintf("RDPG#Nodes() ! %s", err)) } // TODO: Populate list of rdpg nodes for given URL, //`SELECT node_local_dsn FROM bdr.bdr_nodes INTO rdpg.nodes (node_local_dsn);` type dsn struct { DSN string `db:"node_local_dsn"` } dsns := []dsn{} err = db.Select(&dsns, SQL["bdr_nodes_dsn"]) if err != nil { log.Error(fmt.Sprintf("RDPG#Nodes() %s ! %s", SQL["bdr_nodes"], err)) } for _, t := range dsns { node := Node{} s := strings.Split(t.DSN, " ") node.LocalDSN = t.DSN node.Host = strings.Split(s[0], "=")[1] node.Port = strings.Split(s[1], "=")[1] node.User = strings.Split(s[2], "=")[1] node.Database = `postgres` // strings.Split(s[3], "=")[1] nodes = append(nodes, node) } // TODO: Get this information into the database and then out of the rdpg.nodes //rows, err := db.Query("SELECT host,port,user,'postgres' FROM rdpg.nodes;") //if err != nil { // log.Error(fmt.Sprintf("Nodes() %s", err)) //} else { // sqlx.StructScan(rows, nodes) //} db.Close() return nodes }
func (r *RDPG) DisableDatabase(dbname string) (err error) { nodes := r.Nodes() for i := len(nodes) - 1; i >= 0; i-- { node := nodes[i] node.Database = "postgres" db, err := node.Connect() if err != nil { log.Error(fmt.Sprintf("RDPG#DisableDatabase(%s) %s ! %s", dbname, node.Host, err)) return err } sq := fmt.Sprintf(`SELECT rdpg.bdr_disable_database('%s');`, dbname) log.Trace(fmt.Sprintf(`RDPG#DisableDatabase(%s) DISABLE %s > %s`, dbname, node.Host, sq)) _, err = db.Exec(sq) if err != nil { log.Error(fmt.Sprintf("RDPG#DisableDatabase(%s) DISABLE %s ! %s", dbname, node.Host, err)) } db.Close() } return nil }
/* (CB) PUT /v2/service_instances/:instance_id/service_bindings/:binding_id (RB) DELETE /v2/service_instances/:instance_id/service_bindings/:binding_id */ func BindingHandler(w http.ResponseWriter, request *http.Request) { vars := mux.Vars(request) body, err := ioutil.ReadAll(request.Body) if err != nil { w.WriteHeader(http.StatusInternalServerError) fmt.Fprintf(w, `{"status": %d,"description": %s}`, http.StatusInternalServerError, err) return } log.Trace(fmt.Sprintf("%s /v2/service_instances/:instance_id/service_bindings/:binding_id :: %+v :: %s", request.Method, vars, body)) switch request.Method { case "PUT": binding, err := CreateBinding(vars["instance_id"], vars["binding_id"]) if err != nil { log.Error(fmt.Sprintf("%s /v2/service_instances/:instance_id/service_bindings/:binding_id %s", request.Method, err)) w.WriteHeader(http.StatusInternalServerError) fmt.Fprintf(w, `{"status": %d,"description": %s}`, http.StatusInternalServerError, err) return } j, err := json.Marshal(binding) if err != nil { log.Error(fmt.Sprintf("%s /v2/service_instances/:instance_id/service_bindings/:binding_id %s", request.Method, err)) w.WriteHeader(http.StatusInternalServerError) fmt.Fprintf(w, `{"status": %d,"description": %s}`, http.StatusInternalServerError, err) } else { w.WriteHeader(http.StatusOK) w.Write(j) return } case "DELETE": // Question, do we need to do anything else? w.WriteHeader(http.StatusOK) fmt.Fprintf(w, `{"status": %d,"description": "Binding Removed"}`, http.StatusOK) default: w.WriteHeader(http.StatusMethodNotAllowed) fmt.Fprintf(w, `{"status": %d,"description": "Allowed Methods: PUT, DELETE"}`, http.StatusMethodNotAllowed) return } }
func FindInstance(instanceId string) (i *Instance, err error) { r := rdpg.New() in := Instance{} sq := `SELECT id, instance_id, service_id, plan_id, organization_id, space_id, dbname, uname, pass FROM cfsb.instances WHERE instance_id=lower($1) LIMIT 1;` r.OpenDB("rdpg") err = r.DB.Get(&in, sq, instanceId) if err != nil { // TODO: Change messaging if err is sql.NoRows then say couldn't find instance with instanceId log.Error(fmt.Sprintf("cfsb.FindInstance(%s) ! %s", instanceId, err)) } r.DB.Close() i = &in return }
func Instances() (si []Instance, err error) { r := rdpg.New() r.OpenDB("rdpg") si = []Instance{} // TODO: Move this into a versioned SQL Function. sq := `SELECT instance_id, service_id, plan_id, organization_id, space_id, dbname, uname, 'md5'||md5(cfsb.instances.pass||uname) as pass FROM cfsb.instances WHERE ineffective_at IS NULL; ` err = r.DB.Select(&si, sq) if err != nil { // TODO: Change messaging if err is sql.NoRows then say couldn't find instance with instanceId log.Error(fmt.Sprintf("cfsb.Instances() ! %s", err)) } r.DB.Close() return }
func init() { rdpgURI = os.Getenv("RDPG_ADMIN_PG_URI") if rdpgURI == "" || rdpgURI[0:13] != "postgresql://" { log.Error("ERROR: RDPG_ADMIN_PG_URI is not set.") proc, _ := os.FindProcess(os.Getpid()) proc.Signal(syscall.SIGTERM) } db, err := sqlx.Connect("postgres", rdpgURI) if err != nil { log.Error(fmt.Sprintf("Failed connecting to %s err: %s", rdpgURI, err)) proc, _ := os.FindProcess(os.Getpid()) proc.Signal(syscall.SIGTERM) } err = db.Ping() if err != nil { db.Close() log.Error(fmt.Sprintf("Unable to Ping %s err: %s", rdpgURI, err)) proc, _ := os.FindProcess(os.Getpid()) proc.Signal(syscall.SIGTERM) } db.Close() }
func (n *Node) AdminAPI(method, path string) (err error) { url := fmt.Sprintf("http://%s:%s/%s", n.Host, os.Getenv("RDPG_ADMIN_PORT"), path) req, err := http.NewRequest(method, url, bytes.NewBuffer([]byte(`{}`))) // req.Header.Set("Content-Type", "application/json") req.SetBasicAuth(os.Getenv("RDPG_ADMIN_USER"), os.Getenv("RDPG_ADMIN_PASS")) client := &http.Client{} log.Trace(fmt.Sprintf(`Node#AdminAPI(%s,%s) %s`, method, path, url)) resp, err := client.Do(req) if err != nil { log.Error(fmt.Sprintf(`Node#AdminAPI(%s,%s) ! %s`, method, url, err)) } resp.Body.Close() return }
func FindBinding(bindingId string) (binding *Binding, err error) { r := rdpg.New() b := Binding{} sq := `SELECT id,instance_id, binding_id FROM cfsb.bindings WHERE binding_id=lower($1) LIMIT 1;` log.Trace(fmt.Sprintf(`cfsb.FindBinding(%s) > %s`, bindingId, sq)) r.OpenDB("rdpg") err = r.DB.Get(&b, sq, bindingId) if err != nil { // TODO: Change messaging if err is sql.NoRows then say couldn't find binding with bindingId log.Error(fmt.Sprintf("cfsb.FindBinding(%s) ! %s", bindingId, err)) } r.DB.Close() binding = &b return }
func Check(check string) (status int) { r := rdpg.New() err := r.OpenDB("rdpg") if err != nil { log.Error(fmt.Sprintf("Error opening ! %s", r.URI)) return http.StatusInternalServerError } switch check { case "ha_pb_pg": var numNodes int r.DB.Get(&numNodes, "SELECT count(node_name) FROM bdr.bdr_nodes;") if numNodes < 3 { return http.StatusInternalServerError } default: return http.StatusInternalServerError } return http.StatusOK }
func (r *RDPG) CreateReplicationGroup(dbname string) (err error) { nodes := r.Nodes() // TODO: Drop Database on all nodes if err != nil for any operation below for index, node := range nodes { node.Database = dbname db, err := node.Connect() if err != nil { break } sq := "" name := fmt.Sprintf("%s", node.Host) if index == 0 { sq = fmt.Sprintf(`SELECT bdr.bdr_group_create( local_node_name := '%s', node_external_dsn := 'host=%s port=%s user=%s dbname=%s' ); `, name, node.Host, node.Port, node.User, dbname) } else { sq = fmt.Sprintf(`SELECT bdr.bdr_group_join( local_node_name := '%s', node_external_dsn := 'host=%s port=%s user=%s dbname=%s', join_using_dsn := 'host=%s port=%s user=%s dbname=%s' ); `, name, node.Host, node.Port, node.User, node.Database, nodes[0].Host, nodes[0].Port, nodes[0].User, dbname, ) } log.Trace(fmt.Sprintf(`RDPG#CreateReplicationGroup(%s) %s > %s`, dbname, node.Host, sq)) _, err = db.Exec(sq) if err == nil { sq = `SELECT bdr.bdr_node_join_wait_for_ready();` log.Trace(fmt.Sprintf(`RDPG#CreateReplicationGroup(%s) %s > %s`, dbname, node.Host, sq)) _, err = db.Exec(sq) } db.Close() } if err != nil { // Cleanup in BDR currently requires droping the database and trying again... log.Error(fmt.Sprintf("CreateReplicationGroup(%s) ! %s", dbname, err)) } return err }
func (r *RDPG) BackupDatabase(dbname string) (err error) { log.Error(fmt.Sprintf("RDPG#BackupDatabase(%s) TODO: IMPLEMENT", dbname)) return nil }