func newKeyspace(p *namespace, name string) (datastore.Keyspace, errors.Error) { cbNamespace := p.getPool() cbbucket, err := cbNamespace.GetBucket(name) if err != nil { logging.Infof(" keyspace %s not found %v", name, err) // go-couchbase caches the buckets // to be sure no such bucket exists right now // we trigger a refresh p.refresh(true) cbNamespace = p.getPool() // and then check one more time logging.Infof(" Retrying bucket %s", name) cbbucket, err = cbNamespace.GetBucket(name) if err != nil { // really no such bucket exists return nil, errors.NewCbKeyspaceNotFoundError(err, "keyspace "+name) } } if strings.EqualFold(cbbucket.Type, "memcached") { return nil, errors.NewCbBucketTypeNotSupportedError(nil, cbbucket.Type) } rv := &keyspace{ namespace: p, name: name, cbbucket: cbbucket, } // Initialize index providers rv.viewIndexer = newViewIndexer(rv) logging.Infof("Created New Bucket %s", name) //discover existing indexes if ierr := rv.loadIndexes(); ierr != nil { logging.Warnf("Error loading indexes for keyspace %s, Error %v", name, ierr) } var qerr errors.Error rv.gsiIndexer, qerr = gsi.NewGSIIndexer(p.site.URL(), p.Name(), name) if qerr != nil { logging.Warnf("Error loading GSI indexes for keyspace %s. Error %v", name, qerr) } // Create a bucket updater that will keep the couchbase bucket fresh. cbbucket.RunBucketUpdater(p.KeyspaceDeleteCallback) return rv, nil }
// Called by go-couchbase if a configured keyspace is deleted func (p *namespace) KeyspaceDeleteCallback(name string, err error) { p.lock.Lock() defer p.lock.Unlock() ks, ok := p.keyspaceCache[name] if ok { logging.Infof("Keyspace %v being deleted", name) ks.(*keyspace).deleted = true delete(p.keyspaceCache, name) } else { logging.Warnf("Keyspace %v not configured on this server", name) } }
func loadViewIndexes(v *viewIndexer) ([]*datastore.Index, error) { b := v.keyspace rows, err := b.cbbucket.GetDDocsWithRetry() if err != nil { return nil, err } inames := make([]string, 0, len(rows.Rows)) nonUsableIndexes := make([]string, 0) for _, row := range rows.Rows { cdoc := row.DDoc id := cdoc.Meta["id"].(string) if strings.HasPrefix(id, "_design/ddl_") { iname := strings.TrimPrefix(id, "_design/ddl_") inames = append(inames, iname) } else if strings.HasPrefix(id, "_design/dev_") { // append this to the list of non-usuable indexes iname := strings.TrimPrefix(id, "_design/dev_") for _, name := range v.nonUsableIndexes { if iname == name { continue } } nonUsableIndexes = append(nonUsableIndexes, iname) } else if strings.HasPrefix(id, "_design/") { iname := strings.TrimPrefix(id, "_design/") for _, name := range v.nonUsableIndexes { if iname == name { continue } } nonUsableIndexes = append(nonUsableIndexes, iname) } } indexes := make([]*datastore.Index, 0, len(inames)) for _, iname := range inames { ddname := "ddl_" + iname jdoc, err := getDesignDoc(b, ddname) if err != nil { return nil, err } jview, ok := jdoc.Views[iname] if !ok { nonUsableIndexes = append(nonUsableIndexes, iname) logging.Errorf("Missing view for index %v ", iname) continue } exprlist := make([]expression.Expression, 0, len(jdoc.IndexOn)) for _, ser := range jdoc.IndexOn { if jdoc.PrimaryIndex == true { doc := expression.NewIdentifier(b.Name()) meta := expression.NewMeta(doc) mdid := expression.NewField(meta, expression.NewFieldName("id", false)) exprlist = append(exprlist, mdid) } else { expr, err := parser.Parse(ser) if err != nil { nonUsableIndexes = append(nonUsableIndexes, iname) logging.Errorf("Cannot unmarshal expression for index %v", iname) continue } exprlist = append(exprlist, expr) } } if len(exprlist) != len(jdoc.IndexOn) { continue } var conditionExpr expression.Expression if jdoc.Condition != "" { conditionExpr, err = parser.Parse(jdoc.Condition) if err != nil { logging.Errorf("Unable to parse condition expression. Err %v", err) continue } } ddoc := designdoc{ name: ddname, viewname: iname, mapfn: jview.Map, reducefn: jview.Reduce, } if ddoc.checksum() != jdoc.IndexChecksum { nonUsableIndexes = append(nonUsableIndexes, iname) logging.Warnf("Warning - checksum failed on index %v", iname) continue } var index datastore.Index if jdoc.PrimaryIndex == true { index = &primaryIndex{ viewIndex{ name: iname, keyspace: b, view: v, using: datastore.VIEW, ddoc: &ddoc, on: exprlist, where: conditionExpr, isPrimary: jdoc.PrimaryIndex, }, } } else { index = &viewIndex{ name: iname, keyspace: b, view: v, using: datastore.VIEW, ddoc: &ddoc, on: exprlist, where: conditionExpr, isPrimary: jdoc.PrimaryIndex, } } indexes = append(indexes, &index) } v.nonUsableIndexes = nonUsableIndexes if len(indexes) == 0 { return nil, nil } return indexes, nil }
// NewSite creates a new Couchbase site for the given url. func NewDatastore(u string) (s datastore.Datastore, e errors.Error) { var client cb.Client var cbAuthInit bool // try and initialize cbauth c, err := initCbAuth(u) if err != nil { logging.Errorf(" Unable to initialize cbauth. Error %v", err) url, err := url.Parse(u) if err != nil { return nil, errors.NewCbUrlParseError(err, "url "+u) } if url.User != nil { password, _ := url.User.Password() if password == "" { logging.Errorf("No password found in url %s", u) } // intialize cb_auth variables manually logging.Infof(" Trying to init cbauth with credentials %s %s", url.Host, url.User.Username()) set, err := cbauth.InternalRetryDefaultInit(url.Host, url.User.Username(), password) if set == false || err != nil { logging.Errorf(" Unable to initialize cbauth variables. Error %v", err) } else { c, err = initCbAuth("http://" + url.Host) if err != nil { logging.Errorf("Unable to initliaze cbauth. Error %v", err) } else { client = *c cbAuthInit = true } } } } else { client = *c cbAuthInit = true } if cbAuthInit == false { // connect without auth logging.Warnf("Unable to intialize cbAuth, access to couchbase buckets may be restricted") cb.HTTPClient = &http.Client{} client, err = cb.Connect(u) if err != nil { return nil, errors.NewCbConnectionError(err, "url "+u) } } site := &site{ client: client, namespaceCache: make(map[string]*namespace), CbAuthInit: cbAuthInit, } // initialize the default pool. // TODO can couchbase server contain more than one pool ? defaultPool, Err := loadNamespace(site, "default") if Err != nil { logging.Errorf("Cannot connect to default pool") return nil, Err } site.namespaceCache["default"] = defaultPool logging.Infof("New site created with url %s", u) return site, nil }
func (s *site) Authorize(privileges datastore.Privileges, credentials datastore.Credentials) errors.Error { var authResult bool var err error if s.CbAuthInit == false { // cbauth is not initialized. Access to SASL protected buckets will be // denied by the couchbase server logging.Warnf("CbAuth not intialized") return nil } // if the authentication fails for any of the requested privileges return an error for keyspace, privilege := range privileges { if strings.Contains(keyspace, ":") { q := strings.Split(keyspace, ":") pool := q[0] keyspace = q[1] if strings.EqualFold(pool, "#system") { // trying auth on system keyspace return nil } } logging.Debugf("Authenticating for keyspace %s", keyspace) if len(credentials) == 0 { authResult, err = doAuth(keyspace, "", keyspace, privilege) if authResult == false || err != nil { logging.Infof("Auth failed for keyspace %s", keyspace) return errors.NewDatastoreAuthorizationError(err, "Keyspace "+keyspace) } } else { //look for either the bucket name or the admin credentials for username, password := range credentials { var un string userCreds := strings.Split(username, ":") if len(userCreds) == 1 { un = userCreds[0] } else { un = userCreds[1] } logging.Debugf(" Credentials %v %v", un, userCreds) if strings.EqualFold(un, "Administrator") || strings.EqualFold(userCreds[0], "admin") { authResult, err = doAuth(un, password, keyspace, privilege) } else if un != "" && password != "" { authResult, err = doAuth(un, password, keyspace, privilege) } else { //try with empty password authResult, err = doAuth(keyspace, "", keyspace, privilege) } if err != nil { return errors.NewDatastoreAuthorizationError(err, "Keyspace "+keyspace) } // Auth succeeded if authResult == true { break } continue } } } if authResult == false { return errors.NewDatastoreAuthorizationError(err, "") } return nil }