automation-tests/common/pkg/configmaps/configmapsdb.go

211 lines
5.4 KiB
Go

package configmaps
import (
"encoding/json"
"io/ioutil"
"os"
"strings"
"time"
"github.com/pkg/errors"
)
type db struct {
// ConfigMaps maps a configmap id to configmap metadata
ConfigMaps map[string]ConfigMap `json:"configmaps"`
// NameToID maps a configmap name to a configmap id
NameToID map[string]string `json:"nameToID"`
// IDToName maps a configmap id to a configmap name
IDToName map[string]string `json:"idToName"`
// lastModified is the time when the database was last modified on the file system
lastModified time.Time
}
// loadDB loads database data into the in-memory cache if it has been modified
func (s *ConfigMapManager) loadDB() error {
// check if the db file exists
fileInfo, err := os.Stat(s.configMapDBPath)
if err != nil {
if !os.IsExist(err) {
// If the file doesn't exist, then there's no reason to update the db cache,
// the db cache will show no entries anyway.
// The file will be created later on a store()
return nil
}
return err
}
// We check if the file has been modified after the last time it was loaded into the cache.
// If the file has been modified, then we know that our cache is not up-to-date, so we load
// the db into the cache.
if s.db.lastModified.Equal(fileInfo.ModTime()) {
return nil
}
file, err := os.Open(s.configMapDBPath)
if err != nil {
return err
}
defer file.Close()
if err != nil {
return err
}
byteValue, err := ioutil.ReadAll(file)
if err != nil {
return err
}
unmarshalled := new(db)
if err := json.Unmarshal(byteValue, unmarshalled); err != nil {
return err
}
s.db = unmarshalled
s.db.lastModified = fileInfo.ModTime()
return nil
}
// getNameAndID takes a configmap's name, ID, or partial ID, and returns both its name and full ID.
func (s *ConfigMapManager) getNameAndID(nameOrID string) (name, id string, err error) {
name, id, err = s.getExactNameAndID(nameOrID)
if err == nil {
return name, id, nil
} else if errors.Cause(err) != ErrNoSuchConfigMap {
return "", "", err
}
// ID prefix may have been given, iterate through all IDs.
// ID and partial ID has a max length of 25, so we return if its greater than that.
if len(nameOrID) > configMapIDLength {
return "", "", errors.Wrapf(ErrNoSuchConfigMap, "no configmap with name or id %q", nameOrID)
}
exists := false
var foundID, foundName string
for id, name := range s.db.IDToName {
if strings.HasPrefix(id, nameOrID) {
if exists {
return "", "", errors.Wrapf(errAmbiguous, "more than one result configmap with prefix %s", nameOrID)
}
exists = true
foundID = id
foundName = name
}
}
if exists {
return foundName, foundID, nil
}
return "", "", errors.Wrapf(ErrNoSuchConfigMap, "no configmap with name or id %q", nameOrID)
}
// getExactNameAndID takes a configmap's name or ID and returns both its name and full ID.
func (s *ConfigMapManager) getExactNameAndID(nameOrID string) (name, id string, err error) {
err = s.loadDB()
if err != nil {
return "", "", err
}
if name, ok := s.db.IDToName[nameOrID]; ok {
id := nameOrID
return name, id, nil
}
if id, ok := s.db.NameToID[nameOrID]; ok {
name := nameOrID
return name, id, nil
}
return "", "", errors.Wrapf(ErrNoSuchConfigMap, "no configmap with name or id %q", nameOrID)
}
// exactConfigMapExists checks if the configmap exists, given a name or ID
// Does not match partial name or IDs
func (s *ConfigMapManager) exactConfigMapExists(nameOrID string) (bool, error) {
_, _, err := s.getExactNameAndID(nameOrID)
if err != nil {
if errors.Cause(err) == ErrNoSuchConfigMap {
return false, nil
}
return false, err
}
return true, nil
}
// lookupAll gets all configmaps stored.
func (s *ConfigMapManager) lookupAll() (map[string]ConfigMap, error) {
err := s.loadDB()
if err != nil {
return nil, err
}
return s.db.ConfigMaps, nil
}
// lookupConfigMap returns a configmap with the given name, ID, or partial ID.
func (s *ConfigMapManager) lookupConfigMap(nameOrID string) (*ConfigMap, error) {
err := s.loadDB()
if err != nil {
return nil, err
}
_, id, err := s.getNameAndID(nameOrID)
if err != nil {
return nil, err
}
allConfigMaps, err := s.lookupAll()
if err != nil {
return nil, err
}
if configmap, ok := allConfigMaps[id]; ok {
return &configmap, nil
}
return nil, errors.Wrapf(ErrNoSuchConfigMap, "no configmap with name or id %q", nameOrID)
}
// Store creates a new configmap in the configmaps database.
// It deals with only storing metadata, not data payload.
func (s *ConfigMapManager) store(entry *ConfigMap) error {
err := s.loadDB()
if err != nil {
return err
}
s.db.ConfigMaps[entry.ID] = *entry
s.db.NameToID[entry.Name] = entry.ID
s.db.IDToName[entry.ID] = entry.Name
marshalled, err := json.MarshalIndent(s.db, "", " ")
if err != nil {
return err
}
err = ioutil.WriteFile(s.configMapDBPath, marshalled, 0o600)
if err != nil {
return err
}
return nil
}
// delete deletes a configmap from the configmaps database, given a name, ID, or partial ID.
// It deals with only deleting metadata, not data payload.
func (s *ConfigMapManager) delete(nameOrID string) error {
name, id, err := s.getNameAndID(nameOrID)
if err != nil {
return err
}
err = s.loadDB()
if err != nil {
return err
}
delete(s.db.ConfigMaps, id)
delete(s.db.NameToID, name)
delete(s.db.IDToName, id)
marshalled, err := json.MarshalIndent(s.db, "", " ")
if err != nil {
return err
}
err = ioutil.WriteFile(s.configMapDBPath, marshalled, 0o600)
if err != nil {
return err
}
return nil
}