mirror of
https://github.com/goharbor/harbor.git
synced 2024-09-26 04:23:22 +02: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"`
|
Value string `json:"value,omitempty"`
|
||||||
}
|
}
|
||||||
|
|
||||||
// NewConfigureValue ...
|
// NewCfgValue ... Create checked config value
|
||||||
func NewConfigureValue(name, value string) *ConfigureValue {
|
func NewCfgValue(name, value string) (*ConfigureValue, error) {
|
||||||
result := &ConfigureValue{}
|
result := &ConfigureValue{}
|
||||||
err := result.Set(name, value)
|
err := result.Set(name, value)
|
||||||
if err != nil {
|
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
|
result.Name = name // Keep name to trace error
|
||||||
}
|
}
|
||||||
return result
|
return result, err
|
||||||
}
|
}
|
||||||
|
|
||||||
// GetString - Get the string value of current configure
|
// GetString - Get the string value of current configure
|
||||||
@ -74,7 +73,7 @@ func (c *ConfigureValue) GetInt() int {
|
|||||||
return intValue
|
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
|
return 0
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -90,7 +89,7 @@ func (c *ConfigureValue) GetInt64() int64 {
|
|||||||
return int64Value
|
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
|
return 0
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -106,7 +105,7 @@ func (c *ConfigureValue) GetBool() bool {
|
|||||||
return boolValue
|
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
|
return false
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -116,7 +115,7 @@ func (c *ConfigureValue) GetStringToStringMap() map[string]string {
|
|||||||
if item, ok := Instance().GetByName(c.Name); ok {
|
if item, ok := Instance().GetByName(c.Name); ok {
|
||||||
val, err := item.ItemType.get(c.Value)
|
val, err := item.ItemType.get(c.Value)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
log.Errorf("The GetBool failed, error: %+v", err)
|
log.Errorf("The GetStringToStringMap failed, error: %+v", err)
|
||||||
return result
|
return result
|
||||||
}
|
}
|
||||||
if mapValue, suc := val.(map[string]string); suc {
|
if mapValue, suc := val.(map[string]string); suc {
|
||||||
|
@ -15,6 +15,7 @@
|
|||||||
package metadata
|
package metadata
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"fmt"
|
||||||
"github.com/stretchr/testify/assert"
|
"github.com/stretchr/testify/assert"
|
||||||
"testing"
|
"testing"
|
||||||
)
|
)
|
||||||
@ -27,25 +28,36 @@ var testingMetaDataArray = []Item{
|
|||||||
{Name: "sample_map_setting", ItemType: &MapType{}, Scope: "user", Group: "ldapbasic"},
|
{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) {
|
func TestConfigureValue_GetBool(t *testing.T) {
|
||||||
assert.Equal(t, NewConfigureValue("ldap_verify_cert", "true").GetBool(), true)
|
assert.Equal(t, createCfgValue("ldap_verify_cert", "true").GetBool(), true)
|
||||||
assert.Equal(t, NewConfigureValue("unknown", "false").GetBool(), false)
|
assert.Equal(t, createCfgValue("unknown", "false").GetBool(), false)
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestConfigureValue_GetString(t *testing.T) {
|
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) {
|
func TestConfigureValue_GetStringToStringMap(t *testing.T) {
|
||||||
Instance().initFromArray(testingMetaDataArray)
|
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()
|
Instance().init()
|
||||||
}
|
}
|
||||||
func TestConfigureValue_GetInt(t *testing.T) {
|
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) {
|
func TestConfigureValue_GetInt64(t *testing.T) {
|
||||||
Instance().initFromArray(testingMetaDataArray)
|
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