mirror of
https://github.com/goharbor/harbor.git
synced 2024-12-24 17:47:46 +01:00
Merge pull request #6537 from stonezdj/ref_admin_driver
Refactor config settings stage2
This commit is contained in:
commit
93c0a18b06
181
src/common/config/manager.go
Normal file
181
src/common/config/manager.go
Normal 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)
|
||||
}
|
113
src/common/config/manager_test.go
Normal file
113
src/common/config/manager_test.go
Normal 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())
|
||||
}
|
@ -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 {
|
||||
|
@ -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))
|
||||
}
|
||||
|
81
src/common/config/store/driver/db.go
Normal file
81
src/common/config/store/driver/db.go
Normal 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)
|
||||
}
|
59
src/common/config/store/driver/db_test.go
Normal file
59
src/common/config/store/driver/db_test.go
Normal 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)
|
||||
|
||||
}
|
10
src/common/config/store/driver/driver.go
Normal file
10
src/common/config/store/driver/driver.go
Normal 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
|
||||
}
|
29
src/common/config/store/driver/rest.go
Normal file
29
src/common/config/store/driver/rest.go
Normal 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)
|
||||
}
|
75
src/common/config/store/driver/rest_test.go
Normal file
75
src/common/config/store/driver/rest_test.go
Normal 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"])
|
||||
|
||||
}
|
102
src/common/config/store/store.go
Normal file
102
src/common/config/store/store.go
Normal 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)
|
||||
}
|
52
src/common/config/store/store_test.go
Normal file
52
src/common/config/store/store_test.go
Normal 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())
|
||||
|
||||
}
|
Loading…
Reference in New Issue
Block a user