blob: 262546a81cce540a79e532b48a4513cfac25cc2c [file] [log] [blame] [edit]
// Copyright 2017 Google Inc.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
package apidAnalytics
import (
"database/sql"
"sync"
)
// Cache for scope uuid to org, env and tenantId information
var tenantCache map[string]tenant
// RW lock for tenant map cache since the cache can be
// read while its being written to and vice versa
var tenantCachelock = sync.RWMutex{}
// Cache for all org/env for this cluster
var orgEnvCache map[string]bool
// RW lock for orgEnvCache map cache since the cache can be
// read while its being written to and vice versa
var orgEnvCacheLock = sync.RWMutex{}
// Load data scope information into an in-memory cache so that
// for each record a DB lookup is not required
func createTenantCache() {
// Lock before writing to the map as it has multiple readers
tenantCachelock.Lock()
defer tenantCachelock.Unlock()
tenantCache = make(map[string]tenant)
var org, env, id string
db := getDB()
rows, error := db.Query("SELECT env, org, id FROM edgex_data_scope")
if error != nil {
log.Warnf("Could not get datascope from DB due to : %s", error.Error())
} else {
defer rows.Close()
// Lock before writing to the map as it has multiple readers
for rows.Next() {
rows.Scan(&env, &org, &id)
tenantCache[id] = tenant{Org: org, Env: env}
}
}
log.Debugf("Count of data scopes in the cache: %d", len(tenantCache))
}
// Load data scope information into an in-memory cache so that
// for each record a DB lookup is not required
func createOrgEnvCache() {
// Lock before writing to the map as it has multiple readers
orgEnvCacheLock.Lock()
defer orgEnvCacheLock.Unlock()
orgEnvCache = make(map[string]bool)
var org, env string
db := getDB()
rows, error := db.Query("SELECT env, org FROM edgex_data_scope")
if error != nil {
log.Warnf("Could not get datascope from DB due to : %s", error.Error())
} else {
defer rows.Close()
// Lock before writing to the map as it has multiple readers
for rows.Next() {
rows.Scan(&env, &org)
orgEnv := getKeyForOrgEnvCache(org, env)
orgEnvCache[orgEnv] = true
}
}
log.Debugf("Count of org~env in the cache: %d", len(orgEnvCache))
}
// Returns Tenant Info given a scope uuid from the cache or by querying
// the DB directly based on useCaching config
func getTenantForScope(scopeuuid string) (tenant, dbError) {
if config.GetBool(useCaching) {
// acquire a read lock as this cache has 1 writer as well
tenantCachelock.RLock()
ten, exists := tenantCache[scopeuuid]
tenantCachelock.RUnlock()
dbErr := dbError{}
if !exists {
log.Debugf("No tenant found for scopeuuid = %s "+
"in cache, loading info from DB", scopeuuid)
// Update cache
t, err := getTenantFromDB(scopeuuid)
if err.ErrorCode != "" {
dbErr = err
ten = t
} else {
// update cache
tenantCachelock.Lock()
defer tenantCachelock.Unlock()
tenantCache[scopeuuid] = t
ten = t
}
}
return ten, dbErr
} else {
return getTenantFromDB(scopeuuid)
}
}
// Returns tenant info by querying DB directly
func getTenantFromDB(scopeuuid string) (tenant, dbError) {
var org, env string
db := getDB()
error := db.QueryRow("SELECT env, org FROM edgex_data_scope"+
" where id = ?", scopeuuid).Scan(&env, &org)
switch {
case error == sql.ErrNoRows:
reason := "No tenant found for this scopeuuid: " + scopeuuid
errorCode := "UNKNOWN_SCOPE"
return tenant{}, dbError{
ErrorCode: errorCode,
Reason: reason}
case error != nil:
reason := error.Error()
errorCode := "INTERNAL_SEARCH_ERROR"
return tenant{}, dbError{
ErrorCode: errorCode,
Reason: reason}
}
return tenant{
Org: org,
Env: env}, dbError{}
}
/*
Checks if given org/env exists is a valid scope for this apid cluster
It also stores the scope i.e. tenant_id in the tenant object using pointer.
tenant_id in combination with apiKey is used to find kms related information
*/
func validateTenant(tenant tenant) (bool, dbError) {
if config.GetBool(useCaching) {
// acquire a read lock as this cache has 1 writer as well
orgEnvCacheLock.RLock()
orgEnv := getKeyForOrgEnvCache(tenant.Org, tenant.Env)
_, exists := orgEnvCache[orgEnv]
orgEnvCacheLock.RUnlock()
dbErr := dbError{}
if !exists {
log.Debugf("OrgEnv = %s not found "+
"in cache, loading info from DB", orgEnv)
// Update cache
valid, dbErr := validateTenantFromDB(tenant)
if valid {
// update cache
orgEnvCacheLock.Lock()
defer orgEnvCacheLock.Unlock()
orgEnvCache[orgEnv] = true
}
return valid, dbErr
} else {
return true, dbErr
}
} else {
return validateTenantFromDB(tenant)
}
}
func validateTenantFromDB(tenant tenant) (bool, dbError) {
db := getDB()
rows, err := db.Query("SELECT 1 FROM edgex_data_scope"+
" where org = ? and env = ?", tenant.Org, tenant.Env)
defer rows.Close()
if !rows.Next() {
if err == nil {
reason := "No tenant found for this org: " + tenant.Org + " and env:" + tenant.Env
errorCode := "UNKNOWN_SCOPE"
return false, dbError{
ErrorCode: errorCode,
Reason: reason}
} else {
reason := err.Error()
errorCode := "INTERNAL_SEARCH_ERROR"
return false, dbError{
ErrorCode: errorCode,
Reason: reason}
}
}
return true, dbError{}
}
func getKeyForOrgEnvCache(org, env string) string {
return org + "~" + env
}