From f113f4a54f2ab841df64e23a43dd713c5ee5e772 Mon Sep 17 00:00:00 2001 From: Wenkai Yin Date: Fri, 20 Jan 2017 13:30:49 +0800 Subject: [PATCH] update --- src/adminserver/api/base_test.go | 32 ++++ src/adminserver/api/cfg.go | 25 +-- src/adminserver/api/cfg_test.go | 25 +++ src/adminserver/systemcfg/store/driver.go | 13 +- .../systemcfg/store/json/driver_json.go | 34 +++- .../systemcfg/store/json/driver_json_test.go | 22 +-- src/adminserver/systemcfg/systemcfg.go | 115 ++++++------ src/adminserver/systemcfg/systemcfg_test.go | 79 ++++++--- src/common/api/base.go | 10 -- src/common/api/base_test.go | 20 --- src/common/config/config.go | 72 +++++--- src/common/dao/config_test.go | 164 ------------------ src/common/models/config.go | 18 +- src/common/utils/email/mail.go | 5 +- src/common/utils/test/adminserver.go | 44 ++++- src/jobservice/config/config.go | 70 +++----- src/ui/api/config.go | 152 ++++++++++------ src/ui/api/repository.go | 6 +- src/ui/api/target.go | 4 +- src/ui/auth/ldap/ldap.go | 2 +- src/ui/config/config.go | 132 +++++++------- src/ui/config/config_test.go | 2 +- 22 files changed, 512 insertions(+), 534 deletions(-) create mode 100644 src/adminserver/api/base_test.go create mode 100644 src/adminserver/api/cfg_test.go diff --git a/src/adminserver/api/base_test.go b/src/adminserver/api/base_test.go new file mode 100644 index 000000000..3ba79b912 --- /dev/null +++ b/src/adminserver/api/base_test.go @@ -0,0 +1,32 @@ +/* + Copyright (c) 2016 VMware, Inc. All Rights Reserved. + 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 api + +import ( + "net/http" + "net/http/httptest" + "testing" +) + +func TestHandleInternalServerError(t *testing.T) { + w := httptest.NewRecorder() + handleInternalServerError(w) + + if w.Code != http.StatusInternalServerError { + t.Errorf("unexpected status code: %d != %d", w.Code, http.StatusInternalServerError) + } + +} diff --git a/src/adminserver/api/cfg.go b/src/adminserver/api/cfg.go index e2a41412b..79b0b4dcf 100644 --- a/src/adminserver/api/cfg.go +++ b/src/adminserver/api/cfg.go @@ -20,11 +20,8 @@ import ( "io/ioutil" "net/http" "os" - "strconv" cfg "github.com/vmware/harbor/src/adminserver/systemcfg" - comcfg "github.com/vmware/harbor/src/common/config" - "github.com/vmware/harbor/src/common/models" "github.com/vmware/harbor/src/common/utils/log" ) @@ -93,31 +90,20 @@ func UpdateCfgs(w http.ResponseWriter, r *http.Request) { return } - m := &map[string]string{} - if err = json.Unmarshal(b, m); err != nil { + m := map[string]interface{}{} + if err = json.Unmarshal(b, &m); err != nil { handleBadRequestError(w, err.Error()) return } - system, err := cfg.GetSystemCfg() - if err != nil { - handleInternalServerError(w) - return - } - - if err := populate(system, *m); err != nil { - log.Errorf("failed to populate system configurations: %v", err) - handleInternalServerError(w) - return - } - - if err = cfg.UpdateSystemCfg(system); err != nil { + if err = cfg.UpdateSystemCfg(m); err != nil { log.Errorf("failed to update system configurations: %v", err) handleInternalServerError(w) return } } +/* // populate attrs of cfg according to m func populate(cfg *models.SystemCfg, m map[string]string) error { if mode, ok := m[comcfg.AUTHMode]; ok { @@ -133,7 +119,7 @@ func populate(cfg *models.SystemCfg, m map[string]string) error { cfg.Authentication.LDAP.SearchDN = dn } if pwd, ok := m[comcfg.LDAPSearchPwd]; ok { - cfg.Authentication.LDAP.SearchPwd = pwd + cfg.Authentication.LDAP.SearchPassword = pwd } if dn, ok := m[comcfg.LDAPBaseDN]; ok { cfg.Authentication.LDAP.BaseDN = dn @@ -191,3 +177,4 @@ func populate(cfg *models.SystemCfg, m map[string]string) error { return nil } +*/ diff --git a/src/adminserver/api/cfg_test.go b/src/adminserver/api/cfg_test.go new file mode 100644 index 000000000..320aa5e50 --- /dev/null +++ b/src/adminserver/api/cfg_test.go @@ -0,0 +1,25 @@ +/* + Copyright (c) 2016 VMware, Inc. All Rights Reserved. + 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 api + +import ( + "net/http/httptest" +) + +func test() { + httptest.NewRecorder() + +} diff --git a/src/adminserver/systemcfg/store/driver.go b/src/adminserver/systemcfg/store/driver.go index c0a9a2832..3a36d572c 100644 --- a/src/adminserver/systemcfg/store/driver.go +++ b/src/adminserver/systemcfg/store/driver.go @@ -15,16 +15,13 @@ package store -import ( - "github.com/vmware/harbor/src/common/models" -) - // Driver defines methods that a configuration store driver must implement type Driver interface { // Name returns a human-readable name of the driver Name() string - // Read reads the configurations from store - Read() (*models.SystemCfg, error) - // Write writes the configurations to store - Write(*models.SystemCfg) error + // Read reads all the configurations from store + Read() (map[string]interface{}, error) + // Write writes the configurations to store, the configurations can be + // part of all + Write(map[string]interface{}) error } diff --git a/src/adminserver/systemcfg/store/json/driver_json.go b/src/adminserver/systemcfg/store/json/driver_json.go index 4785e6006..90180ba5e 100644 --- a/src/adminserver/systemcfg/store/json/driver_json.go +++ b/src/adminserver/systemcfg/store/json/driver_json.go @@ -23,7 +23,6 @@ import ( "sync" "github.com/vmware/harbor/src/adminserver/systemcfg/store" - "github.com/vmware/harbor/src/common/models" "github.com/vmware/harbor/src/common/utils/log" ) @@ -68,11 +67,15 @@ func (c *cfgStore) Name() string { } // Read ... -func (c *cfgStore) Read() (*models.SystemCfg, error) { +func (c *cfgStore) Read() (map[string]interface{}, error) { c.RLock() defer c.RUnlock() - b, err := ioutil.ReadFile(c.path) + return read(c.path) +} + +func read(path string) (map[string]interface{}, error) { + b, err := ioutil.ReadFile(path) if err != nil { return nil, err } @@ -82,8 +85,8 @@ func (c *cfgStore) Read() (*models.SystemCfg, error) { return nil, nil } - config := &models.SystemCfg{} - if err = json.Unmarshal(b, config); err != nil { + config := map[string]interface{}{} + if err = json.Unmarshal(b, &config); err != nil { return nil, err } @@ -91,14 +94,27 @@ func (c *cfgStore) Read() (*models.SystemCfg, error) { } // Write ... -func (c *cfgStore) Write(config *models.SystemCfg) error { - b, err := json.MarshalIndent(config, "", " ") +func (c *cfgStore) Write(config map[string]interface{}) error { + c.Lock() + defer c.Unlock() + + cfg, err := read(c.path) if err != nil { return err } - c.Lock() - defer c.Unlock() + if cfg == nil { + cfg = config + } else { + for k, v := range config { + cfg[k] = v + } + } + + b, err := json.MarshalIndent(cfg, "", " ") + if err != nil { + return err + } if err = ioutil.WriteFile(c.path, b, 0600); err != nil { return err diff --git a/src/adminserver/systemcfg/store/json/driver_json_test.go b/src/adminserver/systemcfg/store/json/driver_json_test.go index c551049f6..9a5ce4968 100644 --- a/src/adminserver/systemcfg/store/json/driver_json_test.go +++ b/src/adminserver/systemcfg/store/json/driver_json_test.go @@ -18,8 +18,6 @@ package json import ( "os" "testing" - - "github.com/vmware/harbor/src/common/models" ) func TestReadWrite(t *testing.T) { @@ -34,19 +32,21 @@ func TestReadWrite(t *testing.T) { } }() - config := &models.SystemCfg{ - Authentication: &models.Authentication{ - LDAP: &models.LDAP{}, - }, - Database: &models.Database{ - MySQL: &models.MySQL{}, - }, + if store.Name() != "JSON" { + t.Errorf("unexpected name: %s != %s", store.Name(), "JSON") + return + } + + config := map[string]interface{}{ + "key": "value", } if err := store.Write(config); err != nil { - t.Fatalf("failed to write configurations to json file: %v", err) + t.Errorf("failed to write configurations to json file: %v", err) + return } if _, err = store.Read(); err != nil { - t.Fatalf("failed to read configurations from json file: %v", err) + t.Errorf("failed to read configurations from json file: %v", err) + return } } diff --git a/src/adminserver/systemcfg/systemcfg.go b/src/adminserver/systemcfg/systemcfg.go index 57f0323e8..d1d45c203 100644 --- a/src/adminserver/systemcfg/systemcfg.go +++ b/src/adminserver/systemcfg/systemcfg.go @@ -22,7 +22,7 @@ import ( "github.com/vmware/harbor/src/adminserver/systemcfg/store" "github.com/vmware/harbor/src/adminserver/systemcfg/store/json" - "github.com/vmware/harbor/src/common/models" + comcfg "github.com/vmware/harbor/src/common/config" "github.com/vmware/harbor/src/common/utils/log" ) @@ -77,112 +77,95 @@ func getCfgStore() string { } //read the following attrs from env every time boots up -func readFromEnv(cfg *models.SystemCfg) error { - cfg.DomainName = os.Getenv("EXT_ENDPOINT") +func readFromEnv(cfg map[string]interface{}) error { + cfg[comcfg.DomainName] = os.Getenv("EXT_ENDPOINT") - cfg.Database = &models.Database{ - Type: os.Getenv("DATABASE_TYPE"), - MySQL: &models.MySQL{ - Host: os.Getenv("MYSQL_HOST"), - Username: os.Getenv("MYSQL_USR"), - Password: os.Getenv("MYSQL_PWD"), - Database: os.Getenv("MYSQL_DATABASE"), - }, - SQLite: &models.SQLite{ - File: os.Getenv("SQLITE_FILE"), - }, - } + cfg[comcfg.DatabaseType] = os.Getenv("DATABASE_TYPE") + cfg[comcfg.MySQLHost] = os.Getenv("MYSQL_HOST") port, err := strconv.Atoi(os.Getenv("MYSQL_PORT")) if err != nil { return err } - cfg.Database.MySQL.Port = port - - cfg.TokenService = &models.TokenService{ - URL: os.Getenv("TOKEN_SERVICE_URL"), - } - cfg.Registry = &models.Registry{ - URL: os.Getenv("REGISTRY_URL"), - } - - //TODO remove - cfg.JobLogDir = os.Getenv("LOG_DIR") - //TODO remove - cfg.CompressJS = os.Getenv("USE_COMPRESSED_JS") == "on" - exp, err := strconv.Atoi(os.Getenv("TOKEN_EXPIRATION")) + cfg[comcfg.MySQLPort] = port + cfg[comcfg.MySQLUsername] = os.Getenv("MYSQL_USR") + cfg[comcfg.MySQLPassword] = os.Getenv("MYSQL_PWD") + cfg[comcfg.MySQLDatabase] = os.Getenv("MYSQL_DATABASE") + cfg[comcfg.SQLiteFile] = os.Getenv("SQLITE_FILE") + cfg[comcfg.TokenServiceURL] = os.Getenv("TOKEN_SERVICE_URL") + tokenExpi, err := strconv.Atoi(os.Getenv("TOKEN_EXPIRATION")) if err != nil { return err } - cfg.TokenExpiration = exp - cfg.SecretKey = os.Getenv("SECRET_KEY") - - cfgExp, err := strconv.Atoi(os.Getenv("CFG_EXPIRATION")) + cfg[comcfg.TokenExpiration] = tokenExpi + cfg[comcfg.RegistryURL] = os.Getenv("REGISTRY_URL") + //TODO remove + cfg[comcfg.JobLogDir] = os.Getenv("LOG_DIR") + //TODO remove + cfg[comcfg.UseCompressedJS] = os.Getenv("USE_COMPRESSED_JS") == "on" + cfg[comcfg.SecretKey] = os.Getenv("SECRET_KEY") + cfgExpi, err := strconv.Atoi(os.Getenv("CFG_EXPIRATION")) if err != nil { return err } - cfg.CfgExpiration = cfgExp - + cfg[comcfg.CfgExpiration] = cfgExpi workers, err := strconv.Atoi(os.Getenv("MAX_JOB_WORKERS")) if err != nil { return err } - cfg.MaxJobWorkers = workers + cfg[comcfg.MaxJobWorkers] = workers return nil } -func initFromEnv() (*models.SystemCfg, error) { - cfg := &models.SystemCfg{} +func initFromEnv() (map[string]interface{}, error) { + cfg := map[string]interface{}{} if err := readFromEnv(cfg); err != nil { return nil, err } - cfg.Authentication = &models.Authentication{ - Mode: os.Getenv("AUTH_MODE"), - SelfRegistration: os.Getenv("SELF_REGISTRATION") == "on", - LDAP: &models.LDAP{ - URL: os.Getenv("LDAP_URL"), - SearchDN: os.Getenv("LDAP_SEARCH_DN"), - SearchPwd: os.Getenv("LDAP_SEARCH_PWD"), - BaseDN: os.Getenv("LDAP_BASE_DN"), - Filter: os.Getenv("LDAP_FILTER"), - UID: os.Getenv("LDAP_UID"), - }, - } + cfg[comcfg.AUTHMode] = os.Getenv("AUTH_MODE") + cfg[comcfg.SelfRegistration] = os.Getenv("SELF_REGISTRATION") == "on" + cfg[comcfg.LDAPURL] = os.Getenv("LDAP_URL") + cfg[comcfg.LDAPSearchDN] = os.Getenv("LDAP_SEARCH_DN") + cfg[comcfg.LDAPSearchPwd] = os.Getenv("LDAP_SEARCH_PWD") + cfg[comcfg.LDAPBaseDN] = os.Getenv("LDAP_BASE_DN") + cfg[comcfg.LDAPFilter] = os.Getenv("LDAP_FILTER") + cfg[comcfg.LDAPUID] = os.Getenv("LDAP_UID") scope, err := strconv.Atoi(os.Getenv("LDAP_SCOPE")) if err != nil { return nil, err } - cfg.Authentication.LDAP.Scope = scope + cfg[comcfg.LDAPScope] = scope timeout, err := strconv.Atoi(os.Getenv("LDAP_TIMEOUT")) if err != nil { return nil, err } - cfg.Authentication.LDAP.Timeout = timeout - - cfg.Email = &models.Email{ - Host: os.Getenv("EMAIL_HOST"), - Port: os.Getenv("EMAIL_PORT"), - Username: os.Getenv("EMAIL_USR"), - Password: os.Getenv("EMAIL_PWD"), - SSL: os.Getenv("EMAIL_SSL") == "true", - From: os.Getenv("EMAIL_FROM"), - Identity: os.Getenv("EMAIL_IDENTITY"), + cfg[comcfg.LDAPTimeout] = timeout + cfg[comcfg.EmailHost] = os.Getenv("EMAIL_HOST") + port, err := strconv.Atoi(os.Getenv("EMAIL_PORT")) + if err != nil { + return nil, err } - cfg.VerifyRemoteCert = os.Getenv("VERIFY_REMOTE_CERT") == "on" - cfg.ProjectCreationRestriction = os.Getenv("PROJECT_CREATION_RESTRICTION") + cfg[comcfg.EmailPort] = port + cfg[comcfg.EmailUsername] = os.Getenv("EMAIL_USR") + cfg[comcfg.EmailPassword] = os.Getenv("EMAIL_PWD") + cfg[comcfg.EmailSSL] = os.Getenv("EMAIL_SSL") == "true" + cfg[comcfg.EmailFrom] = os.Getenv("EMAIL_FROM") + cfg[comcfg.EmailIdentity] = os.Getenv("EMAIL_IDENTITY") + cfg[comcfg.VerifyRemoteCert] = os.Getenv("VERIFY_REMOTE_CERT") == "on" + cfg[comcfg.ProjectCreationRestriction] = os.Getenv("PROJECT_CREATION_RESTRICTION") + cfg[comcfg.AdminInitialPassword] = os.Getenv("HARBOR_ADMIN_PASSWORD") - cfg.InitialAdminPwd = os.Getenv("HARBOR_ADMIN_PASSWORD") return cfg, nil } // GetSystemCfg returns the system configurations -func GetSystemCfg() (*models.SystemCfg, error) { +func GetSystemCfg() (map[string]interface{}, error) { return cfgStore.Read() } // UpdateSystemCfg updates the system configurations -func UpdateSystemCfg(cfg *models.SystemCfg) error { +func UpdateSystemCfg(cfg map[string]interface{}) error { return cfgStore.Write(cfg) } diff --git a/src/adminserver/systemcfg/systemcfg_test.go b/src/adminserver/systemcfg/systemcfg_test.go index f3f4a7f1b..14b38ed00 100644 --- a/src/adminserver/systemcfg/systemcfg_test.go +++ b/src/adminserver/systemcfg/systemcfg_test.go @@ -15,51 +15,43 @@ package systemcfg -/* import ( "os" "testing" + + comcfg "github.com/vmware/harbor/src/common/config" ) - +// test functions under adminserver/systemcfg func TestSystemcfg(t *testing.T) { key := "JSON_STORE_PATH" - tmpPath := "/tmp/config.json" - originalPath := os.Getenv(key) - defer func() { - if err := os.Remove(tmpPath); err != nil { - t.Errorf("failed to remove %s: %v", tmpPath, err) + path := "/tmp/config.json" + if _, err := os.Stat(path); err == nil { + if err := os.Remove(path); err != nil { + t.Fatalf("failed to remove %s: %v", path, err) } + } else if !os.IsNotExist(err) { + t.Fatalf("failed to check the existence of %s: %v", path, err) + } - if len(originalPath) == 0 { - if err := os.Unsetenv(key); err != nil { - t.Fatalf("failed to unset env %s: %v", key, err) - } - return - } - - if err := os.Setenv(key, originalPath); err != nil { - t.Fatalf("failed to set env %s: %v", key, err) - } - }() - - if err := os.Setenv(key, tmpPath); err != nil { + if err := os.Setenv(key, path); err != nil { t.Fatalf("failed to set env %s: %v", key, err) } m := map[string]string{ + "AUTH_MODE": comcfg.DBAuth, "LDAP_SCOPE": "1", "LDAP_TIMEOUT": "30", "MYSQL_PORT": "3306", "MAX_JOB_WORKERS": "3", "TOKEN_EXPIRATION": "30", "CFG_EXPIRATION": "5", + "EMAIL_PORT": "25", } for k, v := range m { if err := os.Setenv(k, v); err != nil { - t.Errorf("failed to set env %s: %v", k, err) - return + t.Fatalf("failed to set env %s: %v", k, err) } } @@ -67,5 +59,46 @@ func TestSystemcfg(t *testing.T) { t.Errorf("failed to initialize system configurations: %v", err) return } + defer func() { + if err := os.Remove(path); err != nil { + t.Fatalf("failed to remove %s: %v", path, err) + } + }() + + // run Init again to make sure it works well when the configuration file + // already exists + if err := Init(); err != nil { + t.Errorf("failed to initialize system configurations: %v", err) + return + } + + cfg, err := GetSystemCfg() + if err != nil { + t.Errorf("failed to get system configurations: %v", err) + return + } + + if cfg[comcfg.AUTHMode] != comcfg.DBAuth { + t.Errorf("unexpected auth mode: %s != %s", + cfg[comcfg.AUTHMode], comcfg.DBAuth) + return + } + + cfg[comcfg.AUTHMode] = comcfg.LDAPAuth + if err = UpdateSystemCfg(cfg); err != nil { + t.Errorf("failed to update system configurations: %v", err) + return + } + + cfg, err = GetSystemCfg() + if err != nil { + t.Errorf("failed to get system configurations: %v", err) + return + } + + if cfg[comcfg.AUTHMode] != comcfg.LDAPAuth { + t.Errorf("unexpected auth mode: %s != %s", + cfg[comcfg.AUTHMode], comcfg.DBAuth) + return + } } -*/ diff --git a/src/common/api/base.go b/src/common/api/base.go index 227327339..38635f76d 100644 --- a/src/common/api/base.go +++ b/src/common/api/base.go @@ -26,7 +26,6 @@ import ( "github.com/vmware/harbor/src/common/models" "github.com/vmware/harbor/src/common/utils/log" "github.com/vmware/harbor/src/ui/auth" - "github.com/vmware/harbor/src/ui/config" "github.com/astaxie/beego" ) @@ -210,12 +209,3 @@ func (b *BaseAPI) GetPaginationParams() (page, pageSize int64) { return page, pageSize } - -// GetIsInsecure ... -func GetIsInsecure() (bool, error) { - verify, err := config.VerifyRemoteCert() - if err != nil { - return false, err - } - return !verify, nil -} diff --git a/src/common/api/base_test.go b/src/common/api/base_test.go index 0352b6e76..386e30e52 100644 --- a/src/common/api/base_test.go +++ b/src/common/api/base_test.go @@ -13,23 +13,3 @@ limitations under the License. */ package api - -/* -import ( - "github.com/vmware/harbor/src/common/config" - "os" - "testing" -) - -func TestGetIsInsecure(t *testing.T) { - os.Setenv("VERIFY_REMOTE_CERT", "off") - err := config.Reload() - if err != nil { - t.Errorf("Failed to load config, error: %v", err) - } - if !GetIsInsecure() { - t.Errorf("GetIsInsecure() should be true when VERIFY_REMOTE_CERT is off, in fact: false") - } - os.Unsetenv("VERIFY_REMOTE_CERT") -} -*/ diff --git a/src/common/config/config.go b/src/common/config/config.go index 4c9aeaa61..295564d66 100644 --- a/src/common/config/config.go +++ b/src/common/config/config.go @@ -40,7 +40,15 @@ const ( LDAPScopeOnelevel = "2" LDAPScopeSubtree = "3" + DomainName = "domain_name" AUTHMode = "auth_mode" + DatabaseType = "database_type" + MySQLHost = "mysql_host" + MySQLPort = "mysql_port" + MySQLUsername = "mysql_username" + MySQLPassword = "mysql_password" + MySQLDatabase = "mysql_database" + SQLiteFile = "sqlite_file" SelfRegistration = "self_registration" LDAPURL = "ldap_url" LDAPSearchDN = "ldap_search_dn" @@ -50,6 +58,8 @@ const ( LDAPFilter = "ldap_filter" LDAPScope = "ldap_scope" LDAPTimeout = "ldap_timeout" + TokenServiceURL = "token_service_url" + RegistryURL = "registry_url" EmailHost = "email_host" EmailPort = "email_port" EmailUsername = "email_username" @@ -60,30 +70,29 @@ const ( ProjectCreationRestriction = "project_creation_restriction" VerifyRemoteCert = "verify_remote_cert" MaxJobWorkers = "max_job_workers" + TokenExpiration = "token_expiration" CfgExpiration = "cfg_expiration" + JobLogDir = "job_log_dir" + UseCompressedJS = "use_compressed_js" + SecretKey = "secret_key" + AdminInitialPassword = "admin_initial_password" ) // Manager manages configurations type Manager struct { Loader *Loader - Parser Parser + Parser *Parser Cache bool cache cache.Cache key string } -// Parser parses []byte to a specific configuration -type Parser interface { - // Parse ... - Parse([]byte) (interface{}, error) -} - // NewManager returns an instance of Manager // url: the url from which loader loads configurations -func NewManager(url, secret string, parser Parser, enableCache bool) *Manager { +func NewManager(url, secret string, enableCache bool) *Manager { m := &Manager{ Loader: NewLoader(url, secret), - Parser: parser, + Parser: &Parser{}, } if enableCache { @@ -101,7 +110,7 @@ func (m *Manager) Init() error { } // Load configurations, if cache is enabled, cache the configurations -func (m *Manager) Load() (interface{}, error) { +func (m *Manager) Load() (map[string]interface{}, error) { b, err := m.Loader.Load() if err != nil { return nil, err @@ -113,7 +122,7 @@ func (m *Manager) Load() (interface{}, error) { } if m.Cache { - expi, err := parseExpiration(b) + expi, err := getCfgExpiration(c) if err != nil { return nil, err } @@ -126,23 +135,26 @@ func (m *Manager) Load() (interface{}, error) { return c, nil } -func parseExpiration(b []byte) (int, error) { - expi := &struct { - Expi int `json:"cfg_expiration"` - }{} - if err := json.Unmarshal(b, expi); err != nil { - return 0, err +func getCfgExpiration(m map[string]interface{}) (int, error) { + if m == nil { + return 0, fmt.Errorf("can not get cfg expiration as configurations are null") } - return expi.Expi, nil + + expi, ok := m[CfgExpiration] + if !ok { + return 0, fmt.Errorf("cfg expiration is not set") + } + + return int(expi.(float64)), nil } // Get : if cache is enabled, read configurations from cache, // if cache is null or cache is disabled it loads configurations directly -func (m *Manager) Get() (interface{}, error) { +func (m *Manager) Get() (map[string]interface{}, error) { if m.Cache { c := m.cache.Get(m.key) if c != nil { - return c, nil + return c.(map[string]interface{}), nil } } return m.Load() @@ -201,6 +213,11 @@ func (l *Loader) Load() ([]byte, error) { return nil, err } defer resp.Body.Close() + + if resp.StatusCode != http.StatusOK { + return nil, fmt.Errorf("%d", resp.StatusCode) + } + b, err := ioutil.ReadAll(resp.Body) if err != nil { return nil, err @@ -230,5 +247,20 @@ func (l *Loader) Upload(b []byte) error { return fmt.Errorf("unexpected http status code: %d", resp.StatusCode) } + log.Debug("configurations uploaded") + return nil } + +// Parser parses configurations +type Parser struct { +} + +// Parse parses []byte to a map configuration +func (p *Parser) Parse(b []byte) (map[string]interface{}, error) { + c := map[string]interface{}{} + if err := json.Unmarshal(b, &c); err != nil { + return nil, err + } + return c, nil +} diff --git a/src/common/dao/config_test.go b/src/common/dao/config_test.go index ce624e313..a874a6c6e 100644 --- a/src/common/dao/config_test.go +++ b/src/common/dao/config_test.go @@ -15,175 +15,12 @@ package dao -/* import ( "testing" "github.com/vmware/harbor/src/common/models" ) -func deleteConfigByKey(key string) error { - if _, err := GetOrmer().Raw("delete from properties where k = ?", key). - Exec(); err != nil { - return err - } - return nil -} - -func TestGetConfigByKey(t *testing.T) { - cfg := &models.Config{ - Key: "key", - Value: "value", - } - - if err := InsertConfig(cfg); err != nil { - t.Fatalf("failed to insert configuration into table: %v", err) - } - defer func(key string) { - if err := deleteConfigByKey(key); err != nil { - t.Fatalf("failed to delete configuration %s: %v", key, err) - } - }(cfg.Key) - - config, err := GetConfigByKey(cfg.Key) - if err != nil { - t.Fatalf("failed to get configuration by key %s: %v", cfg.Key, err) - } - - if config == nil { - t.Fatal("configuration is nil") - } - - if config.Value != cfg.Value { - t.Fatalf("unexpected value: %s != %s", config.Value, cfg.Value) - } -} - -func TestListConfigs(t *testing.T) { - configs, err := ListConfigs() - if err != nil { - t.Fatalf("failed to list configurations: %v", err) - } - size := len(configs) - - cfg := &models.Config{ - Key: "key", - Value: "value", - } - if err := InsertConfig(cfg); err != nil { - t.Fatalf("failed to insert configuration into table: %v", err) - } - defer func(key string) { - if err := deleteConfigByKey(key); err != nil { - t.Fatalf("failed to delete configuration %s: %v", key, err) - } - }(cfg.Key) - - configs, err = ListConfigs() - if err != nil { - t.Fatalf("failed to list configurations: %v", err) - } - - if size+1 != len(configs) { - t.Fatalf("unexpected length of configurations: %d != %d", len(configs), size+1) - } -} - -func TestInsertConfig(t *testing.T) { - cfg := &models.Config{ - Key: "key1", - Value: "value1", - } - - if err := InsertConfig(cfg); err != nil { - t.Fatalf("failed to insert configuration into table: %v", err) - } - defer func(key string) { - if err := deleteConfigByKey(key); err != nil { - t.Fatalf("failed to delete configuration %s: %v", key, err) - } - }(cfg.Key) - - config, err := GetConfigByKey(cfg.Key) - if err != nil { - t.Fatalf("failed to get configuration by key %s: %v", cfg.Key, err) - } - if config == nil { - t.Fatal("configuration is nil") - } - - if config.Value != cfg.Value { - t.Fatalf("unexpected value: %s != %s", config.Value, cfg.Value) - } -} - -func TestUpdateConfig(t *testing.T) { - cfg := &models.Config{ - Key: "key", - Value: "value", - } - - if err := InsertConfig(cfg); err != nil { - t.Fatalf("failed to insert configuration into table: %v", err) - } - defer func(key string) { - if err := deleteConfigByKey(key); err != nil { - t.Fatalf("failed to delete configuration %s: %v", key, err) - } - }(cfg.Key) - - newCfg := &models.Config{ - Key: "key", - Value: "new_value", - } - if err := UpdateConfig(newCfg); err != nil { - t.Fatalf("failed to update configuration: %v", err) - } - - config, err := GetConfigByKey(cfg.Key) - if err != nil { - t.Fatalf("failed to get configuration by key %s: %v", cfg.Key, err) - } - - if config == nil { - t.Fatal("configuration is nil") - } - - if config.Value != newCfg.Value { - t.Fatalf("unexpected value: %s != %s", config.Value, newCfg.Value) - } -} - -func TestInsertOrUpdateConfigs(t *testing.T) { - cfg1 := &models.Config{ - Key: "key1", - Value: "value1", - } - - if err := InsertConfig(cfg1); err != nil { - t.Fatalf("failed to insert configuration into table: %v", err) - } - defer func(key string) { - if err := deleteConfigByKey(key); err != nil { - t.Fatalf("failed to delete configuration %s: %v", key, err) - } - }(cfg1.Key) - - cfg2 := &models.Config{ - Key: "key2", - Value: "value2", - } - - if err := InsertOrUpdateConfigs([]*models.Config{cfg1, cfg2}); err != nil { - t.Fatalf("failed to insert or update configurations: %v", err) - } - defer func(key string) { - if err := deleteConfigByKey(key); err != nil { - t.Fatalf("failed to delete configuration %s: %v", key, err) - } - }(cfg2.Key) -} - func TestAuthModeCanBeModified(t *testing.T) { c, err := GetOrmer().QueryTable(&models.User{}).Count() if err != nil { @@ -233,4 +70,3 @@ func TestAuthModeCanBeModified(t *testing.T) { } } } -*/ diff --git a/src/common/models/config.go b/src/common/models/config.go index 953f08e6c..50a73edef 100644 --- a/src/common/models/config.go +++ b/src/common/models/config.go @@ -24,14 +24,14 @@ type Authentication struct { // LDAP ... type LDAP struct { - URL string `json:"url"` - SearchDN string `json:"search_dn"` - SearchPwd string `json:"search_pwd"` - BaseDN string `json:"base_dn"` - Filter string `json:"filter"` - UID string `json:"uid"` - Scope int `json:"scope"` - Timeout int `json:"timeout"` // in second + URL string `json:"url"` + SearchDN string `json:"search_dn"` + SearchPassword string `json:"search_password"` + BaseDN string `json:"base_dn"` + Filter string `json:"filter"` + UID string `json:"uid"` + Scope int `json:"scope"` + Timeout int `json:"timeout"` // in second } // Database ... @@ -58,7 +58,7 @@ type SQLite struct { // Email ... type Email struct { Host string `json:"host"` - Port string `json:"port"` + Port int `json:"port"` Username string `json:"username"` Password string `json:"password"` SSL bool `json:"ssl"` diff --git a/src/common/utils/email/mail.go b/src/common/utils/email/mail.go index 61fc05531..f3c5108b5 100644 --- a/src/common/utils/email/mail.go +++ b/src/common/utils/email/mail.go @@ -18,6 +18,7 @@ package email import ( "bytes" "crypto/tls" + "strconv" //"strings" "net/smtp" @@ -67,11 +68,11 @@ func (m Mail) SendMail() error { } func sendMail(m Mail, auth smtp.Auth, content []byte) error { - return smtp.SendMail(mc.Host+":"+mc.Port, auth, m.From, m.To, content) + return smtp.SendMail(mc.Host+":"+strconv.Itoa(mc.Port), auth, m.From, m.To, content) } func sendMailWithTLS(m Mail, auth smtp.Auth, content []byte) error { - conn, err := tls.Dial("tcp", mc.Host+":"+mc.Port, nil) + conn, err := tls.Dial("tcp", mc.Host+":"+strconv.Itoa(mc.Port), nil) if err != nil { return err } diff --git a/src/common/utils/test/adminserver.go b/src/common/utils/test/adminserver.go index 538b861bd..18e1b91fd 100644 --- a/src/common/utils/test/adminserver.go +++ b/src/common/utils/test/adminserver.go @@ -20,17 +20,49 @@ import ( "net/http" "net/http/httptest" - "github.com/vmware/harbor/src/common/models" + "github.com/vmware/harbor/src/common/config" ) // NewAdminserver returns a mock admin server func NewAdminserver() (*httptest.Server, error) { m := []*RequestHandlerMapping{} - b, err := json.Marshal(&models.SystemCfg{ - Authentication: &models.Authentication{ - Mode: "db_auth", - }, - Registry: &models.Registry{}, + b, err := json.Marshal(map[string]interface{}{ + config.DomainName: "host01.com", + config.AUTHMode: config.DBAuth, + config.DatabaseType: "mysql", + config.MySQLHost: "127.0.0.1", + config.MySQLPort: 3306, + config.MySQLUsername: "user01", + config.MySQLPassword: "password", + config.MySQLDatabase: "registry", + config.SQLiteFile: "/tmp/registry.db", + config.SelfRegistration: true, + config.LDAPURL: "ldap://127.0.0.1", + config.LDAPSearchDN: "uid=searchuser,ou=people,dc=mydomain,dc=com", + config.LDAPSearchPwd: "password", + config.LDAPBaseDN: "ou=people,dc=mydomain,dc=com", + config.LDAPUID: "uid", + config.LDAPFilter: "", + config.LDAPScope: 3, + config.LDAPTimeout: 30, + config.TokenServiceURL: "http://token_service", + config.RegistryURL: "http://registry", + config.EmailHost: "127.0.0.1", + config.EmailPort: 25, + config.EmailUsername: "user01", + config.EmailPassword: "password", + config.EmailFrom: "from", + config.EmailSSL: true, + config.EmailIdentity: "", + config.ProjectCreationRestriction: config.ProCrtRestrAdmOnly, + config.VerifyRemoteCert: false, + config.MaxJobWorkers: 3, + config.TokenExpiration: 30, + config.CfgExpiration: 5, + config.JobLogDir: "/var/log/jobs", + config.UseCompressedJS: true, + config.SecretKey: "secret", + config.AdminInitialPassword: "password", }) if err != nil { return nil, err diff --git a/src/jobservice/config/config.go b/src/jobservice/config/config.go index 51bf1823f..e4ba0f9b1 100644 --- a/src/jobservice/config/config.go +++ b/src/jobservice/config/config.go @@ -16,7 +16,6 @@ package config import ( - "encoding/json" "os" comcfg "github.com/vmware/harbor/src/common/config" @@ -25,35 +24,13 @@ import ( var mg *comcfg.Manager -// Configuration of Jobservice -type Configuration struct { - Database *models.Database `json:"database"` - Registry *models.Registry `json:"registry"` - VerifyRemoteCert bool `json:"verify_remote_cert"` - MaxJobWorkers int `json:"max_job_workers"` - JobLogDir string `json:"job_log_dir"` - SecretKey string `json:"secret_key"` - CfgExpiration int `json:"cfg_expiration"` -} - -type parser struct { -} - -func (p *parser) Parse(b []byte) (interface{}, error) { - c := &Configuration{} - if err := json.Unmarshal(b, c); err != nil { - return nil, err - } - return c, nil -} - // Init configurations func Init() error { adminServerURL := os.Getenv("ADMIN_SERVER_URL") if len(adminServerURL) == 0 { adminServerURL = "http://adminserver" } - mg = comcfg.NewManager(adminServerURL, UISecret(), &parser{}, true) + mg = comcfg.NewManager(adminServerURL, UISecret(), true) if err := mg.Init(); err != nil { return err @@ -66,39 +43,44 @@ func Init() error { return nil } -func get() (*Configuration, error) { - c, err := mg.Get() - if err != nil { - return nil, err - } - return c.(*Configuration), nil -} - // VerifyRemoteCert returns bool value. func VerifyRemoteCert() (bool, error) { - cfg, err := get() + cfg, err := mg.Get() if err != nil { return true, err } - return cfg.VerifyRemoteCert, nil + return cfg[comcfg.VerifyRemoteCert].(bool), nil } // Database ... func Database() (*models.Database, error) { - cfg, err := get() + cfg, err := mg.Get() if err != nil { return nil, err } - return cfg.Database, nil + database := &models.Database{} + database.Type = cfg[comcfg.DatabaseType].(string) + mysql := &models.MySQL{} + mysql.Host = cfg[comcfg.MySQLHost].(string) + mysql.Port = int(cfg[comcfg.MySQLPort].(float64)) + mysql.Username = cfg[comcfg.MySQLUsername].(string) + mysql.Password = cfg[comcfg.MySQLPassword].(string) + mysql.Database = cfg[comcfg.MySQLDatabase].(string) + database.MySQL = mysql + sqlite := &models.SQLite{} + sqlite.File = cfg[comcfg.SQLiteFile].(string) + database.SQLite = sqlite + + return database, nil } // MaxJobWorkers ... func MaxJobWorkers() (int, error) { - cfg, err := get() + cfg, err := mg.Get() if err != nil { return 0, err } - return cfg.MaxJobWorkers, nil + return int(cfg[comcfg.MaxJobWorkers].(float64)), nil } // LocalUIURL returns the local ui url, job service will use this URL to call API hosted on ui process @@ -108,29 +90,29 @@ func LocalUIURL() string { // LocalRegURL returns the local registry url, job service will use this URL to pull image from the registry func LocalRegURL() (string, error) { - cfg, err := get() + cfg, err := mg.Get() if err != nil { return "", err } - return cfg.Registry.URL, nil + return cfg[comcfg.RegistryURL].(string), nil } // LogDir returns the absolute path to which the log file will be written func LogDir() (string, error) { - cfg, err := get() + cfg, err := mg.Get() if err != nil { return "", err } - return cfg.JobLogDir, nil + return cfg[comcfg.JobLogDir].(string), nil } // SecretKey will return the secret key for encryption/decryption password in target. func SecretKey() (string, error) { - cfg, err := get() + cfg, err := mg.Get() if err != nil { return "", err } - return cfg.SecretKey, nil + return cfg[comcfg.SecretKey].(string), nil } // UISecret returns the value of UI secret cookie, used for communication between UI and JobService diff --git a/src/ui/api/config.go b/src/ui/api/config.go index 1c17ad8e0..7c1f2a2f1 100644 --- a/src/ui/api/config.go +++ b/src/ui/api/config.go @@ -30,6 +30,41 @@ import ( "github.com/vmware/harbor/src/ui/config" ) +// keys of attrs which user can modify +var validKeys = []string{ + comcfg.AUTHMode, + comcfg.EmailFrom, + comcfg.EmailHost, + comcfg.EmailIdentity, + comcfg.EmailPassword, + comcfg.EmailPort, + comcfg.EmailSSL, + comcfg.EmailUsername, + comcfg.LDAPBaseDN, + comcfg.LDAPFilter, + comcfg.LDAPScope, + comcfg.LDAPSearchDN, + comcfg.LDAPSearchPwd, + comcfg.LDAPTimeout, + comcfg.LDAPUID, + comcfg.LDAPURL, + comcfg.ProjectCreationRestriction, + comcfg.SelfRegistration, + comcfg.VerifyRemoteCert, +} + +var numKeys = []string{ + comcfg.EmailPort, + comcfg.LDAPScope, + comcfg.LDAPTimeout, +} + +var boolKeys = []string{ + comcfg.EmailSSL, + comcfg.SelfRegistration, + comcfg.VerifyRemoteCert, +} + // ConfigAPI ... type ConfigAPI struct { api.BaseAPI @@ -49,6 +84,11 @@ func (c *ConfigAPI) Prepare() { } } +type value struct { + Value interface{} `json:"value"` + Editable bool `json:"editable"` +} + // Get returns configurations func (c *ConfigAPI) Get() { cfg, err := config.GetSystemCfg() @@ -57,24 +97,12 @@ func (c *ConfigAPI) Get() { c.CustomAbort(http.StatusInternalServerError, http.StatusText(http.StatusInternalServerError)) } - if cfg.Database.MySQL != nil { - cfg.Database.MySQL.Password = "" - } - - cfg.InitialAdminPwd = "" - cfg.SecretKey = "" - - m := map[string]interface{}{} - m["config"] = cfg - - editable, err := dao.AuthModeCanBeModified() + m, err := convertForGet(cfg) if err != nil { - log.Errorf("failed to determinie whether auth mode can be modified: %v", err) + log.Errorf("failed to convert configurations: %v", err) c.CustomAbort(http.StatusInternalServerError, http.StatusText(http.StatusInternalServerError)) } - m["auth_mode_editable"] = editable - c.Data["json"] = m c.ServeJSON() } @@ -83,11 +111,19 @@ func (c *ConfigAPI) Get() { func (c *ConfigAPI) Put() { m := map[string]string{} c.DecodeJSONReq(&m) - if err := validateCfg(m); err != nil { + + cfg := map[string]string{} + for _, k := range validKeys { + if v, ok := m[k]; ok { + cfg[k] = v + } + } + + if err := validateCfg(cfg); err != nil { c.CustomAbort(http.StatusBadRequest, err.Error()) } - if value, ok := m[comcfg.AUTHMode]; ok { + if value, ok := cfg[comcfg.AUTHMode]; ok { mode, err := config.AuthMode() if err != nil { log.Errorf("failed to get auth mode: %v", err) @@ -109,7 +145,13 @@ func (c *ConfigAPI) Put() { } } - if err := config.Upload(m); err != nil { + result, err := convertForPut(cfg) + if err != nil { + log.Errorf("failed to convert configurations: %v", err) + c.CustomAbort(http.StatusInternalServerError, http.StatusText(http.StatusInternalServerError)) + } + + if err := config.Upload(result); err != nil { log.Errorf("failed to upload configurations: %v", err) c.CustomAbort(http.StatusInternalServerError, http.StatusText(http.StatusInternalServerError)) } @@ -200,60 +242,72 @@ func validateCfg(c map[string]string) error { return nil } -/* -func convert() ([]*models.Config, error) { - cfgs := []*models.Config{} - var err error - pwdKeys := []string{config.LDAP_SEARCH_PWD, config.EMAIL_PWD} - for _, pwdKey := range pwdKeys { - if pwd, ok := c[pwdKey]; ok && len(pwd) != 0 { - c[pwdKey], err = utils.ReversibleEncrypt(pwd, ui_cfg.SecretKey()) - if err != nil { - return nil, err +//encode passwords and convert map[string]string to map[string]interface{} +func convertForPut(m map[string]string) (map[string]interface{}, error) { + cfg := map[string]interface{}{} + + /* + pwdKeys := []string{config.LDAP_SEARCH_PWD, config.EMAIL_PWD} + for _, pwdKey := range pwdKeys { + if pwd, ok := c[pwdKey]; ok && len(pwd) != 0 { + c[pwdKey], err = utils.ReversibleEncrypt(pwd, ui_cfg.SecretKey()) + if err != nil { + return nil, err + } } } + */ + for k, v := range m { + cfg[k] = v } - for _, key := range configKeys { - if value, ok := c[key]; ok { - cfgs = append(cfgs, &models.Config{ - Key: key, - Value: value, - }) + for _, k := range numKeys { + v, err := strconv.Atoi(cfg[k].(string)) + if err != nil { + return nil, err } + cfg[k] = v } - return cfgs, nil + for _, k := range boolKeys { + cfg[k] = cfg[k] == "1" + } + + return cfg, nil } -*/ -/* -//[]*models.Config >> cfgForGet -func convert(cfg *config.Configuration) (map[string]interface{}, error) { - result := map[string]interface{}{} - for _, config := range configs { - cfg[config.Key] = &value{ - Value: config.Value, - Editable: true, - } - } +// delete sensitive attrs and add editable field to every attr +func convertForGet(cfg map[string]interface{}) (map[string]*value, error) { + result := map[string]*value{} - dels := []string{config.LDAP_SEARCH_PWD, config.EMAIL_PWD} + dels := []string{ + comcfg.AdminInitialPassword, + comcfg.EmailPassword, + comcfg.LDAPSearchPwd, + comcfg.MySQLPassword, + comcfg.SecretKey} for _, del := range dels { if _, ok := cfg[del]; ok { delete(cfg, del) } } + for k, v := range cfg { + result[k] = &value{ + Value: v, + Editable: true, + } + } + flag, err := authModeCanBeModified() if err != nil { return nil, err } - cfg[config.AUTH_MODE].Editable = flag + result[comcfg.AUTHMode].Editable = flag - return cfgForGet(cfg), nil + return result, nil } -*/ + func authModeCanBeModified() (bool, error) { return dao.AuthModeCanBeModified() } diff --git a/src/ui/api/repository.go b/src/ui/api/repository.go index 5eca32805..b229c0e96 100644 --- a/src/ui/api/repository.go +++ b/src/ui/api/repository.go @@ -366,14 +366,14 @@ func (ra *RepositoryAPI) initRepositoryClient(repoName string) (r *registry.Repo return nil, err } - insecure, err := api.GetIsInsecure() + verify, err := config.VerifyRemoteCert() if err != nil { return nil, err } username, password, ok := ra.Ctx.Request.BasicAuth() if ok { - return newRepositoryClient(endpoint, insecure, username, password, + return newRepositoryClient(endpoint, !verify, username, password, repoName, "repository", repoName, "pull", "push", "*") } @@ -382,7 +382,7 @@ func (ra *RepositoryAPI) initRepositoryClient(repoName string) (r *registry.Repo return nil, err } - return cache.NewRepositoryClient(endpoint, insecure, username, repoName, + return cache.NewRepositoryClient(endpoint, !verify, username, repoName, "repository", repoName, "pull", "push", "*") } diff --git a/src/ui/api/target.go b/src/ui/api/target.go index c1f88d2ba..becda414e 100644 --- a/src/ui/api/target.go +++ b/src/ui/api/target.go @@ -102,12 +102,12 @@ func (t *TargetAPI) Ping() { password = t.GetString("password") } - insecure, err := api.GetIsInsecure() + verify, err := config.VerifyRemoteCert() if err != nil { log.Errorf("failed to check whether insecure or not: %v", err) t.CustomAbort(http.StatusInternalServerError, http.StatusText(http.StatusInternalServerError)) } - registry, err := newRegistryClient(endpoint, insecure, username, password, + registry, err := newRegistryClient(endpoint, !verify, username, password, "", "", "") if err != nil { // timeout, dns resolve error, connection refused, etc. diff --git a/src/ui/auth/ldap/ldap.go b/src/ui/auth/ldap/ldap.go index e3928a201..7b531e118 100644 --- a/src/ui/auth/ldap/ldap.go +++ b/src/ui/auth/ldap/ldap.go @@ -116,7 +116,7 @@ func (l *Auth) Authenticate(m models.AuthModel) (*models.User, error) { ldapSearchDn := settings.SearchDN if ldapSearchDn != "" { log.Debug("Search DN: ", ldapSearchDn) - ldapSearchPwd := settings.SearchPwd + ldapSearchPwd := settings.SearchPassword err = ldap.Bind(ldapSearchDn, ldapSearchPwd) if err != nil { log.Debug("Bind search dn error", err) diff --git a/src/ui/config/config.go b/src/ui/config/config.go index ccb760f80..5833b16e7 100644 --- a/src/ui/config/config.go +++ b/src/ui/config/config.go @@ -26,35 +26,6 @@ import ( var mg *comcfg.Manager -// Configuration of UI -type Configuration struct { - DomainName string `json:"domain_name"` // Harbor external URL: protocal://host:port - Authentication *models.Authentication `json:"authentication"` - Database *models.Database `json:"database"` - TokenService *models.TokenService `json:"token_service"` - Registry *models.Registry `json:"registry"` - Email *models.Email `json:"email"` - VerifyRemoteCert bool `json:"verify_remote_cert"` - ProjectCreationRestriction string `json:"project_creation_restriction"` - InitialAdminPwd string `json:"initial_admin_pwd"` - //TODO remove - CompressJS bool `json:"compress_js"` - TokenExpiration int `json:"token_expiration"` - SecretKey string `json:"secret_key"` - CfgExpiration int `json:"cfg_expiration"` -} - -type parser struct { -} - -func (p *parser) Parse(b []byte) (interface{}, error) { - c := &Configuration{} - if err := json.Unmarshal(b, c); err != nil { - return nil, err - } - return c, nil -} - // Init configurations func Init() error { adminServerURL := os.Getenv("ADMIN_SERVER_URL") @@ -62,7 +33,7 @@ func Init() error { adminServerURL = "http://adminserver" } log.Debugf("admin server URL: %s", adminServerURL) - mg = comcfg.NewManager(adminServerURL, UISecret(), &parser{}, true) + mg = comcfg.NewManager(adminServerURL, UISecret(), true) if err := mg.Init(); err != nil { return err @@ -75,14 +46,6 @@ func Init() error { return nil } -func get() (*Configuration, error) { - c, err := mg.Get() - if err != nil { - return nil, err - } - return c.(*Configuration), nil -} - // Load configurations func Load() error { _, err := mg.Load() @@ -90,7 +53,7 @@ func Load() error { } // Upload uploads all system configutations to admin server -func Upload(cfg map[string]string) error { +func Upload(cfg map[string]interface{}) error { b, err := json.Marshal(cfg) if err != nil { return err @@ -99,80 +62,92 @@ func Upload(cfg map[string]string) error { } // GetSystemCfg returns the system configurations -func GetSystemCfg() (*models.SystemCfg, error) { +func GetSystemCfg() (map[string]interface{}, error) { raw, err := mg.Loader.Load() if err != nil { return nil, err } - cfg := &models.SystemCfg{} - if err = json.Unmarshal(raw, cfg); err != nil { + c, err := mg.Parser.Parse(raw) + if err != nil { return nil, err } - return cfg, nil + + return c, nil } // AuthMode ... func AuthMode() (string, error) { - cfg, err := get() + cfg, err := mg.Get() if err != nil { return "", err } - return cfg.Authentication.Mode, nil + return cfg[comcfg.AUTHMode].(string), nil } // LDAP returns the setting of ldap server func LDAP() (*models.LDAP, error) { - cfg, err := get() + cfg, err := mg.Get() if err != nil { return nil, err } - return cfg.Authentication.LDAP, nil + + ldap := &models.LDAP{} + ldap.URL = cfg[comcfg.LDAPURL].(string) + ldap.SearchDN = cfg[comcfg.LDAPSearchDN].(string) + ldap.SearchPassword = cfg[comcfg.LDAPSearchPwd].(string) + ldap.BaseDN = cfg[comcfg.LDAPBaseDN].(string) + ldap.UID = cfg[comcfg.LDAPUID].(string) + ldap.Filter = cfg[comcfg.LDAPFilter].(string) + ldap.Scope = int(cfg[comcfg.LDAPScope].(float64)) + ldap.Timeout = int(cfg[comcfg.LDAPTimeout].(float64)) + + return ldap, nil } // TokenExpiration returns the token expiration time (in minute) func TokenExpiration() (int, error) { - cfg, err := get() + cfg, err := mg.Get() if err != nil { return 0, err } - return cfg.TokenExpiration, nil + return int(cfg[comcfg.TokenExpiration].(float64)), nil } // DomainName returns the external URL of Harbor: protocal://host:port func DomainName() (string, error) { - cfg, err := get() + cfg, err := mg.Get() if err != nil { return "", err } - return cfg.DomainName, nil + return cfg[comcfg.DomainName].(string), nil } // SecretKey returns the secret key to encrypt the password of target func SecretKey() (string, error) { - cfg, err := get() + cfg, err := mg.Get() if err != nil { return "", err } - return cfg.SecretKey, nil + return cfg[comcfg.SecretKey].(string), nil } // SelfRegistration returns the enablement of self registration func SelfRegistration() (bool, error) { - cfg, err := get() + cfg, err := mg.Get() if err != nil { return false, err } - return cfg.Authentication.SelfRegistration, nil + return cfg[comcfg.SelfRegistration].(bool), nil } // RegistryURL ... func RegistryURL() (string, error) { - cfg, err := get() + cfg, err := mg.Get() if err != nil { return "", err } - return cfg.Registry.URL, nil + return cfg[comcfg.RegistryURL].(string), nil } // InternalJobServiceURL returns jobservice URL for internal communication between Harbor containers @@ -182,47 +157,70 @@ func InternalJobServiceURL() string { // InitialAdminPassword returns the initial password for administrator func InitialAdminPassword() (string, error) { - cfg, err := get() + cfg, err := mg.Get() if err != nil { return "", err } - return cfg.InitialAdminPwd, nil + return cfg[comcfg.AdminInitialPassword].(string), nil } // OnlyAdminCreateProject returns the flag to restrict that only sys admin can create project func OnlyAdminCreateProject() (bool, error) { - cfg, err := get() + cfg, err := mg.Get() if err != nil { return true, err } - return cfg.ProjectCreationRestriction == comcfg.ProCrtRestrAdmOnly, nil + return cfg[comcfg.ProjectCreationRestriction].(string) == comcfg.ProCrtRestrAdmOnly, nil } // VerifyRemoteCert returns bool value. func VerifyRemoteCert() (bool, error) { - cfg, err := get() + cfg, err := mg.Get() if err != nil { return true, err } - return cfg.VerifyRemoteCert, nil + return cfg[comcfg.VerifyRemoteCert].(bool), nil } // Email returns email server settings func Email() (*models.Email, error) { - cfg, err := get() + cfg, err := mg.Get() if err != nil { return nil, err } - return cfg.Email, nil + + email := &models.Email{} + email.Host = cfg[comcfg.EmailHost].(string) + email.Port = int(cfg[comcfg.EmailPort].(float64)) + email.Username = cfg[comcfg.EmailUsername].(string) + email.Password = cfg[comcfg.EmailPassword].(string) + email.SSL = cfg[comcfg.EmailSSL].(bool) + email.From = cfg[comcfg.EmailFrom].(string) + email.Identity = cfg[comcfg.EmailIdentity].(string) + + return email, nil } // Database returns database settings func Database() (*models.Database, error) { - cfg, err := get() + cfg, err := mg.Get() if err != nil { return nil, err } - return cfg.Database, nil + database := &models.Database{} + database.Type = cfg[comcfg.DatabaseType].(string) + mysql := &models.MySQL{} + mysql.Host = cfg[comcfg.MySQLHost].(string) + mysql.Port = int(cfg[comcfg.MySQLPort].(float64)) + mysql.Username = cfg[comcfg.MySQLUsername].(string) + mysql.Password = cfg[comcfg.MySQLPassword].(string) + mysql.Database = cfg[comcfg.MySQLDatabase].(string) + database.MySQL = mysql + sqlite := &models.SQLite{} + sqlite.File = cfg[comcfg.SQLiteFile].(string) + database.SQLite = sqlite + + return database, nil } // UISecret returns the value of UI secret cookie, used for communication between UI and JobService diff --git a/src/ui/config/config_test.go b/src/ui/config/config_test.go index e71160d2e..4846dd952 100644 --- a/src/ui/config/config_test.go +++ b/src/ui/config/config_test.go @@ -44,7 +44,7 @@ func TestConfig(t *testing.T) { t.Fatalf("failed to load configurations: %v", err) } - if err := Upload(map[string]string{}); err != nil { + if err := Upload(map[string]interface{}{}); err != nil { t.Fatalf("failed to upload configurations: %v", err) }