/* * Copyright 2019 Intel Corporation, 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 namegenerator import ( "encoding/json" "log" "strings" "sync" "github.com/onap/multicloud-k8s/src/k8splugin/internal/db" "github.com/docker/engine/pkg/namesgenerator" pkgerrors "github.com/pkg/errors" ) const ( storeName = "instanceNames" tag = "names" ) var ( nameCache = &cache{} cacheKeyGlobal = cacheKey{"k8sPluginCacheKey"} ) type cache struct { cache map[string]bool mux sync.Mutex } type cacheKey struct { Key string `json:"key"` } func (c cacheKey) String() string { out, err := json.Marshal(c) if err != nil { return "" } return string(out) } func (c *cache) init() { // We have either restarted or this is the first time // that a name is being requested since the service came // up. if c.cache == nil { c.cache = make(map[string]bool) err := c.readCacheFromDB() if err != nil { log.Println("Error Reading from DB: ", err.Error()) return } } } func (c *cache) isAlreadyUsed(name string) bool { if _, ok := c.cache[name]; ok { return true } return false } func (c *cache) readCacheFromDB() error { // Read the latest from cache data, err := db.DBconn.Read(storeName, cacheKeyGlobal, tag) if err != nil { log.Println("Error reading name cache from Database: ", err) return pkgerrors.Wrap(err, "Reading cache from DB") } err = db.DBconn.Unmarshal(data, &c.cache) if err != nil { log.Println("Error unmarshaling data into cache: ", err) return pkgerrors.Wrap(err, "Unmarshaling cache from DB") } return nil } // writeCacheToDB will update the DB with the updated cache func (c *cache) writeCacheToDB() { //Update the database as well err := db.DBconn.Update(storeName, cacheKeyGlobal, tag, c.cache) if err != nil { // TODO: Replace with DBconn variable if strings.Contains(err.Error(), "Error finding master table") { err = db.DBconn.Create(storeName, cacheKeyGlobal, tag, c.cache) if err != nil { log.Println("Error creating the entry in DB. Will try later...") return } } else { log.Println("Error updating DB: ", err.Error()) return } } } func (c *cache) generateName() string { c.mux.Lock() defer c.mux.Unlock() c.init() for { //Call moby package here to generate name name := namesgenerator.GetRandomName(0) if c.isAlreadyUsed(name) { // Generate another name log.Printf("Name %s already used", name) continue } c.cache[name] = true // Update the cache and db c.writeCacheToDB() return name } } func (c *cache) releaseName(name string) { c.mux.Lock() defer c.mux.Unlock() c.init() if c.isAlreadyUsed(name) { c.cache[name] = false // Update the cache and db c.writeCacheToDB() } } // Generate returns an autogenerated name func Generate() string { return nameCache.generateName() } // Release name from cache func Release(name string) { nameCache.releaseName(name) }