Merge pull request #6537 from stonezdj/ref_admin_driver

Refactor config settings stage2
This commit is contained in:
Daniel Jiang 2018-12-21 15:12:56 +08:00 committed by GitHub
commit 93c0a18b06
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
11 changed files with 727 additions and 14 deletions

View File

@ -0,0 +1,181 @@
// Copyright Project Harbor Authors
//
// 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 config
import (
"fmt"
"github.com/goharbor/harbor/src/common"
"github.com/goharbor/harbor/src/common/config/metadata"
"github.com/goharbor/harbor/src/common/config/store"
"github.com/goharbor/harbor/src/common/config/store/driver"
"github.com/goharbor/harbor/src/common/http/modifier/auth"
"github.com/goharbor/harbor/src/common/models"
"github.com/goharbor/harbor/src/common/utils/log"
"os"
)
// CfgManager ... Configure Manager
type CfgManager struct {
store *store.ConfigStore
}
// NewDBCfgManager - create DB config manager
func NewDBCfgManager() *CfgManager {
manager := &CfgManager{store: store.NewConfigStore(&driver.Database{})}
// load default value
manager.loadDefault()
// load system config from env
manager.loadSystemConfigFromEnv()
return manager
}
// NewRESTCfgManager - create REST config manager
func NewRESTCfgManager(configURL, secret string) *CfgManager {
secAuth := auth.NewSecretAuthorizer(secret)
manager := &CfgManager{store: store.NewConfigStore(driver.NewRESTDriver(configURL, secAuth))}
return manager
}
// InmemoryDriver driver for unit testing
type InmemoryDriver struct {
cfgMap map[string]interface{}
}
// Load ...
func (d *InmemoryDriver) Load() (map[string]interface{}, error) {
return d.cfgMap, nil
}
// Save ...
func (d *InmemoryDriver) Save(cfg map[string]interface{}) error {
for k, v := range cfg {
d.cfgMap[k] = v
}
return nil
}
// NewInMemoryManager create a manager for unit testing, doesn't involve database or REST
func NewInMemoryManager() *CfgManager {
return &CfgManager{store: store.NewConfigStore(&InmemoryDriver{cfgMap: map[string]interface{}{}})}
}
// loadDefault ...
func (c *CfgManager) loadDefault() {
// Init Default Value
itemArray := metadata.Instance().GetAll()
for _, item := range itemArray {
// Every string type have default value, other types should have a default value
if _, ok := item.ItemType.(*metadata.StringType); ok || len(item.DefaultValue) > 0 {
cfgValue, err := metadata.NewCfgValue(item.Name, item.DefaultValue)
if err != nil {
log.Errorf("loadDefault failed, config item, key: %v, err: %v", item.Name, err)
continue
}
c.store.Set(item.Name, *cfgValue)
}
}
}
// loadSystemConfigFromEnv ...
func (c *CfgManager) loadSystemConfigFromEnv() {
itemArray := metadata.Instance().GetAll()
// Init System Value
for _, item := range itemArray {
if item.Scope == metadata.SystemScope && len(item.EnvKey) > 0 {
if envValue, ok := os.LookupEnv(item.EnvKey); ok {
configValue, err := metadata.NewCfgValue(item.Name, envValue)
if err != nil {
log.Errorf("loadSystemConfigFromEnv failed, config item, key: %v, err: %v", item.Name, err)
continue
}
c.store.Set(item.Name, *configValue)
}
}
}
}
// GetAll ... Get all settings
func (c *CfgManager) GetAll() []metadata.ConfigureValue {
results := make([]metadata.ConfigureValue, 0)
if err := c.store.Load(); err != nil {
log.Errorf("GetAll failed, error %v", err)
return results
}
metaDataList := metadata.Instance().GetAll()
for _, item := range metaDataList {
if cfgValue, err := c.store.Get(item.Name); err == nil {
results = append(results, *cfgValue)
}
}
return results
}
// Load - Load configuration from storage, like database or redis
func (c *CfgManager) Load() error {
return c.store.Load()
}
// Save - Save all current configuration to storage
func (c *CfgManager) Save() error {
return c.store.Save()
}
// Get ...
func (c *CfgManager) Get(key string) *metadata.ConfigureValue {
configValue, err := c.store.Get(key)
if err != nil {
log.Errorf("failed to get key %v, error: %v", key, err)
configValue = &metadata.ConfigureValue{}
}
return configValue
}
// Set ...
func (c *CfgManager) Set(key string, value interface{}) {
configValue, err := metadata.NewCfgValue(key, fmt.Sprintf("%v", value))
if err != nil {
log.Errorf("error when setting key: %v, error %v", key, err)
return
}
c.store.Set(key, *configValue)
}
// GetDatabaseCfg - Get database configurations
/*
In database related testing, call it in the TestMain to initialize database schema and set testing configures
cfgMgr := config.NewDBCfgManager()
dao.InitDatabase(cfgMgr.GetDatabaseCfg())
cfgMgr.Load()
cfgMrg.UpdateConfig(testingConfigs)
*/
func (c *CfgManager) GetDatabaseCfg() *models.Database {
return &models.Database{
Type: c.Get(common.DatabaseType).GetString(),
PostGreSQL: &models.PostGreSQL{
Host: c.Get(common.PostGreSQLHOST).GetString(),
Port: c.Get(common.PostGreSQLPort).GetInt(),
Username: c.Get(common.PostGreSQLUsername).GetString(),
Password: c.Get(common.PostGreSQLPassword).GetString(),
Database: c.Get(common.PostGreSQLDatabase).GetString(),
SSLMode: c.Get(common.PostGreSQLSSLMode).GetString(),
},
}
}
// UpdateConfig - Update config store with a specified configuration and also save updated configure
func (c *CfgManager) UpdateConfig(cfgs map[string]interface{}) error {
return c.store.Update(cfgs)
}

View File

@ -0,0 +1,113 @@
package config
import (
"fmt"
"github.com/goharbor/harbor/src/common/dao"
"github.com/stretchr/testify/assert"
"os"
"testing"
)
var TestDBConfig = map[string]interface{}{
"postgresql_host": "localhost",
"postgresql_database": "registry",
"postgresql_password": "root123",
"postgresql_username": "postgres",
"postgresql_sslmode": "disable",
"email_host": "127.0.0.1",
"clair_url": "http://clair:6060",
}
var configManager *CfgManager
func TestMain(m *testing.M) {
configManager = NewDBCfgManager()
dao.InitDatabase(configManager.GetDatabaseCfg())
configManager.UpdateConfig(TestDBConfig)
os.Exit(m.Run())
}
func TestLoadFromDatabase(t *testing.T) {
dao.InitDatabase(configManager.GetDatabaseCfg())
configManager.Load()
configManager.UpdateConfig(TestDBConfig)
assert.Equal(t, "127.0.0.1", configManager.Get("email_host").GetString())
assert.Equal(t, "http://clair:6060", configManager.Get("clair_url").GetString())
}
func TestSaveToDatabase(t *testing.T) {
dao.InitDatabase(configManager.GetDatabaseCfg())
fmt.Printf("database config %#v\n", configManager.GetDatabaseCfg())
configManager.Load()
configManager.Set("read_only", "true")
configManager.UpdateConfig(TestDBConfig)
configManager.Save()
configManager.Load()
assert.Equal(t, true, configManager.Get("read_only").GetBool())
}
func TestUpdateCfg(t *testing.T) {
testConfig := map[string]interface{}{
"ldap_url": "ldaps://ldap.vmware.com",
"ldap_search_dn": "cn=admin,dc=example,dc=com",
"ldap_timeout": 10,
"ldap_search_password": "admin",
"ldap_base_dn": "dc=example,dc=com",
}
dao.InitDatabase(configManager.GetDatabaseCfg())
configManager.Load()
configManager.UpdateConfig(testConfig)
assert.Equal(t, "ldaps://ldap.vmware.com", configManager.Get("ldap_url").GetString())
assert.Equal(t, 10, configManager.Get("ldap_timeout").GetInt())
assert.Equal(t, "admin", configManager.Get("ldap_search_password").GetPassword())
assert.Equal(t, "cn=admin,dc=example,dc=com", configManager.Get("ldap_search_dn").GetString())
assert.Equal(t, "dc=example,dc=com", configManager.Get("ldap_base_dn").GetString())
}
func TestCfgManager_loadDefaultValues(t *testing.T) {
configManager.loadDefault()
if configManager.Get("ldap_timeout").GetInt() != 5 {
t.Errorf("Failed to load ldap_timeout")
}
}
func TestCfgManger_loadSystemValues(t *testing.T) {
// os.Setenv("CLAIR_DB", "mysql")
configManager.loadDefault()
configManager.loadSystemConfigFromEnv()
configManager.UpdateConfig(map[string]interface{}{
"clair_db": "mysql",
})
if configManager.Get("clair_db").GetString() != "mysql" {
t.Errorf("Failed to set system value clair_db, expected %v, actual %v", "mysql", configManager.Get("clair_db").GetString())
}
}
func TestCfgManager_GetDatabaseCfg(t *testing.T) {
configManager.UpdateConfig(map[string]interface{}{
"postgresql_host": "localhost",
"postgresql_database": "registry",
"postgresql_password": "root123",
"postgresql_username": "postgres",
"postgresql_sslmode": "disable",
})
dbCfg := configManager.GetDatabaseCfg()
assert.Equal(t, "localhost", dbCfg.PostGreSQL.Host)
assert.Equal(t, "registry", dbCfg.PostGreSQL.Database)
assert.Equal(t, "root123", dbCfg.PostGreSQL.Password)
assert.Equal(t, "postgres", dbCfg.PostGreSQL.Username)
assert.Equal(t, "disable", dbCfg.PostGreSQL.SSLMode)
}
func TestNewInMemoryManager(t *testing.T) {
inMemoryManager := NewInMemoryManager()
inMemoryManager.UpdateConfig(map[string]interface{}{
"ldap_url": "ldaps://ldap.vmware.com",
"ldap_timeout": 5,
"ldap_verify_cert": true,
})
assert.Equal(t, "ldaps://ldap.vmware.com", inMemoryManager.Get("ldap_url").GetString())
assert.Equal(t, 5, inMemoryManager.Get("ldap_timeout").GetInt())
assert.Equal(t, true, inMemoryManager.Get("ldap_verify_cert").GetBool())
}

View File

@ -37,15 +37,14 @@ type ConfigureValue struct {
Value string `json:"value,omitempty"`
}
// NewConfigureValue ...
func NewConfigureValue(name, value string) *ConfigureValue {
// NewCfgValue ... Create checked config value
func NewCfgValue(name, value string) (*ConfigureValue, error) {
result := &ConfigureValue{}
err := result.Set(name, value)
if err != nil {
log.Errorf("Failed to set name:%v, value:%v, error %v", name, value, err)
result.Name = name // Keep name to trace error
}
return result
return result, err
}
// GetString - Get the string value of current configure
@ -74,7 +73,7 @@ func (c *ConfigureValue) GetInt() int {
return intValue
}
}
log.Errorf("The current value's metadata is not defined, %+v", c)
log.Errorf("GetInt failed, the current value's metadata is not defined, %+v", c)
return 0
}
@ -90,7 +89,7 @@ func (c *ConfigureValue) GetInt64() int64 {
return int64Value
}
}
log.Errorf("The current value's metadata is not defined, %+v", c)
log.Errorf("GetInt64 failed, the current value's metadata is not defined, %+v", c)
return 0
}
@ -106,7 +105,7 @@ func (c *ConfigureValue) GetBool() bool {
return boolValue
}
}
log.Errorf("The current value's metadata is not defined, %+v", c)
log.Errorf("GetBool failed, the current value's metadata is not defined, %+v", c)
return false
}
@ -116,7 +115,7 @@ func (c *ConfigureValue) GetStringToStringMap() map[string]string {
if item, ok := Instance().GetByName(c.Name); ok {
val, err := item.ItemType.get(c.Value)
if err != nil {
log.Errorf("The GetBool failed, error: %+v", err)
log.Errorf("The GetStringToStringMap failed, error: %+v", err)
return result
}
if mapValue, suc := val.(map[string]string); suc {

View File

@ -15,6 +15,7 @@
package metadata
import (
"fmt"
"github.com/stretchr/testify/assert"
"testing"
)
@ -27,25 +28,36 @@ var testingMetaDataArray = []Item{
{Name: "sample_map_setting", ItemType: &MapType{}, Scope: "user", Group: "ldapbasic"},
}
// createCfgValue ... Create a ConfigureValue object, only used in test
func createCfgValue(name, value string) *ConfigureValue {
result := &ConfigureValue{}
err := result.Set(name, value)
if err != nil {
fmt.Printf("failed to create ConfigureValue name:%v, value:%v, error %v\n", name, value, err)
result.Name = name // Keep name to trace error
}
return result
}
func TestConfigureValue_GetBool(t *testing.T) {
assert.Equal(t, NewConfigureValue("ldap_verify_cert", "true").GetBool(), true)
assert.Equal(t, NewConfigureValue("unknown", "false").GetBool(), false)
assert.Equal(t, createCfgValue("ldap_verify_cert", "true").GetBool(), true)
assert.Equal(t, createCfgValue("unknown", "false").GetBool(), false)
}
func TestConfigureValue_GetString(t *testing.T) {
assert.Equal(t, NewConfigureValue("ldap_url", "ldaps://ldap.vmware.com").GetString(), "ldaps://ldap.vmware.com")
assert.Equal(t, createCfgValue("ldap_url", "ldaps://ldap.vmware.com").GetString(), "ldaps://ldap.vmware.com")
}
func TestConfigureValue_GetStringToStringMap(t *testing.T) {
Instance().initFromArray(testingMetaDataArray)
assert.Equal(t, NewConfigureValue("sample_map_setting", `{"sample":"abc"}`).GetStringToStringMap(), map[string]string{"sample": "abc"})
assert.Equal(t, createCfgValue("sample_map_setting", `{"sample":"abc"}`).GetStringToStringMap(), map[string]string{"sample": "abc"})
Instance().init()
}
func TestConfigureValue_GetInt(t *testing.T) {
assert.Equal(t, NewConfigureValue("ldap_timeout", "5").GetInt(), 5)
assert.Equal(t, createCfgValue("ldap_timeout", "5").GetInt(), 5)
}
func TestConfigureValue_GetInt64(t *testing.T) {
Instance().initFromArray(testingMetaDataArray)
assert.Equal(t, NewConfigureValue("ulimit", "99999").GetInt64(), int64(99999))
assert.Equal(t, createCfgValue("ulimit", "99999").GetInt64(), int64(99999))
}

View File

@ -0,0 +1,81 @@
// Copyright Project Harbor Authors
//
// 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 driver
import (
"fmt"
"github.com/goharbor/harbor/src/common/config/encrypt"
"github.com/goharbor/harbor/src/common/config/metadata"
"github.com/goharbor/harbor/src/common/dao"
"github.com/goharbor/harbor/src/common/models"
"github.com/goharbor/harbor/src/common/utils/log"
)
// Database - Used to load/save configuration in database
type Database struct {
}
// Load - load config from database, only user setting will be load from database.
func (d *Database) Load() (map[string]interface{}, error) {
resultMap := map[string]interface{}{}
configEntries, err := dao.GetConfigEntries()
if err != nil {
return resultMap, err
}
for _, item := range configEntries {
itemMetadata, ok := metadata.Instance().GetByName(item.Key)
if !ok {
log.Warningf("failed to get metadata, key:%v, error:%v, skip to load item", item.Key, err)
continue
}
if itemMetadata.Scope == metadata.SystemScope {
continue
}
if _, ok := itemMetadata.ItemType.(*metadata.PasswordType); ok {
if decryptPassword, err := encrypt.Instance().Decrypt(item.Value); err == nil {
item.Value = decryptPassword
} else {
log.Errorf("decrypt password failed, error %v", err)
}
}
resultMap[itemMetadata.Name] = item.Value
}
return resultMap, nil
}
// Save - Only save user config items in the cfgs map
func (d *Database) Save(cfgs map[string]interface{}) error {
var configEntries []models.ConfigEntry
for key, value := range cfgs {
if item, ok := metadata.Instance().GetByName(key); ok {
if item.Scope == metadata.SystemScope {
log.Errorf("system setting can not updated, key %v", key)
continue
}
strValue := fmt.Sprintf("%v", value)
entry := &models.ConfigEntry{Key: key, Value: strValue}
if _, ok := item.ItemType.(*metadata.PasswordType); ok {
if encryptPassword, err := encrypt.Instance().Encrypt(strValue); err == nil {
entry.Value = encryptPassword
}
}
configEntries = append(configEntries, *entry)
} else {
log.Errorf("failed to get metadata, skip to save key:%v", key)
}
}
return dao.SaveConfigEntries(configEntries)
}

View File

@ -0,0 +1,59 @@
// Copyright Project Harbor Authors
//
// 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 driver
import (
"github.com/goharbor/harbor/src/common/dao"
"github.com/stretchr/testify/assert"
"os"
"testing"
)
func TestMain(m *testing.M) {
dao.PrepareTestForPostgresSQL()
os.Exit(m.Run())
}
func TestDatabase_Load(t *testing.T) {
driver := Database{}
cfgMap, err := driver.Load()
if err != nil {
t.Errorf("failed to load, error %v", err)
}
assert.True(t, len(cfgMap) > 10)
if _, ok := cfgMap["ldap_url"]; !ok {
t.Error("Can not find ldap_url")
}
}
func TestDatabase_Save(t *testing.T) {
ldapURL := "ldap://ldap.vmware.com"
driver := Database{}
prevCfg, err := driver.Load()
if err != nil {
t.Errorf("failed to load config %v", err)
}
cfgMap := map[string]interface{}{"ldap_url": ldapURL}
driver.Save(cfgMap)
updatedMap, err := driver.Load()
if err != nil {
t.Errorf("failed to load config %v", err)
}
assert.Equal(t, updatedMap["ldap_url"], ldapURL)
driver.Save(prevCfg)
}

View File

@ -0,0 +1,10 @@
// Package driver provide the implementation of config driver used in CfgManager
package driver
// Driver the interface to save/load config
type Driver interface {
// Load - load config item from config driver
Load() (map[string]interface{}, error)
// Save - save config item into config driver
Save(cfg map[string]interface{}) error
}

View File

@ -0,0 +1,29 @@
package driver
import (
"github.com/goharbor/harbor/src/common/http"
"github.com/goharbor/harbor/src/common/http/modifier"
)
// RESTDriver - config store driver based on REST API
type RESTDriver struct {
coreURL string
client *http.Client
}
// NewRESTDriver - Create RESTDriver
func NewRESTDriver(coreURL string, modifiers ...modifier.Modifier) *RESTDriver {
return &RESTDriver{coreURL: coreURL, client: http.NewClient(nil, modifiers...)}
}
// Load - load config data from REST server
func (h *RESTDriver) Load() (map[string]interface{}, error) {
cfgMap := map[string]interface{}{}
err := h.client.Get(h.coreURL, &cfgMap)
return cfgMap, err
}
// Save - Save config data to REST server by PUT method
func (h *RESTDriver) Save(cfgMap map[string]interface{}) error {
return h.client.Put(h.coreURL, cfgMap)
}

View File

@ -0,0 +1,75 @@
package driver
import (
"encoding/json"
"github.com/goharbor/harbor/src/common/utils/log"
"github.com/stretchr/testify/assert"
"io/ioutil"
"net/http"
"net/http/httptest"
"testing"
)
func ConfigGetHandler(w http.ResponseWriter, r *http.Request) {
cfgs := map[string]interface{}{
"ldap_url": "ldaps://ldap.vmware.com",
"ldap_scope": 5,
"ldap_verify_cert": true,
}
b, err := json.Marshal(cfgs)
if err != nil {
return
}
w.Write(b)
}
func TestHTTPDriver_Load(t *testing.T) {
server := httptest.NewServer(http.HandlerFunc(ConfigGetHandler))
defer server.Close()
httpDriver := NewRESTDriver(server.URL)
configMap, err := httpDriver.Load()
if err != nil {
t.Errorf("Error when testing http driver %v", err)
}
assert.Equal(t, "ldaps://ldap.vmware.com", configMap["ldap_url"])
// json.Marshal() always convert number to float64, configvalue can handle it by convert it to string
assert.Equal(t, float64(5), configMap["ldap_scope"])
assert.Equal(t, true, configMap["ldap_verify_cert"])
}
var configMapForTest = map[string]interface{}{}
func ConfigPutHandler(w http.ResponseWriter, r *http.Request) {
cfgs := map[string]interface{}{}
content, err := ioutil.ReadAll(r.Body)
if err != nil {
log.Fatal(err)
}
err = json.Unmarshal(content, &cfgs)
if err != nil {
log.Fatal(err)
}
for k, v := range cfgs {
configMapForTest[k] = v
}
}
func TestHTTPDriver_Save(t *testing.T) {
server := httptest.NewServer(http.HandlerFunc(ConfigPutHandler))
defer server.Close()
httpDriver := NewRESTDriver(server.URL)
configMap := map[string]interface{}{
"ldap_url": "ldap://www.example.com",
"ldap_timeout": 10,
"ldap_verify_cert": false,
}
err := httpDriver.Save(configMap)
if err != nil {
t.Fatal(err)
}
assert.Equal(t, "ldap://www.example.com", configMapForTest["ldap_url"])
assert.Equal(t, float64(10), configMapForTest["ldap_timeout"])
assert.Equal(t, false, configMapForTest["ldap_verify_cert"])
}

View File

@ -0,0 +1,102 @@
// Package store is only used in the internal implement of manager, not a public api.
package store
import (
"errors"
"fmt"
"github.com/goharbor/harbor/src/common/config/metadata"
"github.com/goharbor/harbor/src/common/config/store/driver"
"github.com/goharbor/harbor/src/common/utils/log"
"sync"
)
// ConfigStore - the config data store
type ConfigStore struct {
cfgDriver driver.Driver
cfgValues sync.Map
}
// NewConfigStore create config store
func NewConfigStore(cfgDriver driver.Driver) *ConfigStore {
return &ConfigStore{cfgDriver: cfgDriver}
}
// Get - Get config data from current store
func (c *ConfigStore) Get(key string) (*metadata.ConfigureValue, error) {
if value, ok := c.cfgValues.Load(key); ok {
if result, ok := value.(metadata.ConfigureValue); ok {
return &result, nil
}
return nil, errors.New("data in config store is not a ConfigureValue type")
}
return nil, metadata.ErrValueNotSet
}
// Set - Set configure value in store, not saved to config driver
func (c *ConfigStore) Set(key string, value metadata.ConfigureValue) error {
c.cfgValues.Store(key, value)
return nil
}
// Load - Load data from driver, all user config in the store will be refreshed
func (c *ConfigStore) Load() error {
if c.cfgDriver == nil {
return errors.New("failed to load store, cfgDriver is nil")
}
cfgs, err := c.cfgDriver.Load()
if err != nil {
return err
}
for key, value := range cfgs {
cfgValue := metadata.ConfigureValue{}
strValue := fmt.Sprintf("%v", value)
err = cfgValue.Set(key, strValue)
if err != nil {
log.Errorf("error when loading data item, key %v, value %v, error %v", key, value, err)
continue
}
c.cfgValues.Store(key, cfgValue)
}
return nil
}
// Save - Save all data in current store
func (c *ConfigStore) Save() error {
cfgMap := map[string]interface{}{}
c.cfgValues.Range(func(key, value interface{}) bool {
keyStr := fmt.Sprintf("%v", key)
if configValue, ok := value.(metadata.ConfigureValue); ok {
valueStr := configValue.Value
if _, ok := metadata.Instance().GetByName(keyStr); ok {
cfgMap[keyStr] = valueStr
} else {
log.Errorf("failed to get metadata for key %v", keyStr)
}
}
return true
})
if c.cfgDriver == nil {
return errors.New("failed to save store, cfgDriver is nil")
}
return c.cfgDriver.Save(cfgMap)
}
// Update - Only update specified settings in cfgMap in store and driver
func (c *ConfigStore) Update(cfgMap map[string]interface{}) error {
// Update to store
for key, value := range cfgMap {
configValue, err := metadata.NewCfgValue(key, fmt.Sprintf("%v", value))
if err != nil {
log.Warningf("error %v, skip to update configure item, key:%v ", err, key)
delete(cfgMap, key)
continue
}
c.Set(key, *configValue)
}
// Update to driver
return c.cfgDriver.Save(cfgMap)
}

View File

@ -0,0 +1,52 @@
package store
import (
"github.com/goharbor/harbor/src/common/config/metadata"
"github.com/goharbor/harbor/src/common/config/store/driver"
"github.com/goharbor/harbor/src/common/dao"
"github.com/goharbor/harbor/src/common/utils/log"
"github.com/stretchr/testify/assert"
"os"
"testing"
)
func TestMain(m *testing.M) {
dao.PrepareTestForPostgresSQL()
cfgStore := NewConfigStore(&driver.Database{})
cfgStore.Set("ldap_url", metadata.ConfigureValue{Name: "ldap_url", Value: "ldap://ldap.vmware.com"})
err := cfgStore.Save()
if err != nil {
log.Fatal(err)
}
os.Exit(m.Run())
}
func TestConfigStore_Save(t *testing.T) {
cfgStore := NewConfigStore(&driver.Database{})
err := cfgStore.Save()
cfgStore.Set("ldap_verify_cert", metadata.ConfigureValue{Name: "ldap_verify_cert", Value: "true"})
if err != nil {
t.Fatal(err)
}
cfgValue, err := cfgStore.Get("ldap_verify_cert")
if err != nil {
t.Fatal(err)
}
assert.Equal(t, true, cfgValue.GetBool())
}
func TestConfigStore_Load(t *testing.T) {
cfgStore := NewConfigStore(&driver.Database{})
err := cfgStore.Load()
if err != nil {
t.Fatal(err)
}
cfgValue, err := cfgStore.Get("ldap_url")
if err != nil {
t.Fatal(err)
}
assert.Equal(t, "ldap://ldap.vmware.com", cfgValue.GetString())
}