This commit is contained in:
Wenkai Yin 2017-01-20 13:30:49 +08:00
parent b6e27f6ea2
commit f113f4a54f
22 changed files with 512 additions and 534 deletions

View File

@ -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)
}
}

View File

@ -20,11 +20,8 @@ import (
"io/ioutil" "io/ioutil"
"net/http" "net/http"
"os" "os"
"strconv"
cfg "github.com/vmware/harbor/src/adminserver/systemcfg" 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" "github.com/vmware/harbor/src/common/utils/log"
) )
@ -93,31 +90,20 @@ func UpdateCfgs(w http.ResponseWriter, r *http.Request) {
return return
} }
m := &map[string]string{} m := map[string]interface{}{}
if err = json.Unmarshal(b, m); err != nil { if err = json.Unmarshal(b, &m); err != nil {
handleBadRequestError(w, err.Error()) handleBadRequestError(w, err.Error())
return return
} }
system, err := cfg.GetSystemCfg() if err = cfg.UpdateSystemCfg(m); err != nil {
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 {
log.Errorf("failed to update system configurations: %v", err) log.Errorf("failed to update system configurations: %v", err)
handleInternalServerError(w) handleInternalServerError(w)
return return
} }
} }
/*
// populate attrs of cfg according to m // populate attrs of cfg according to m
func populate(cfg *models.SystemCfg, m map[string]string) error { func populate(cfg *models.SystemCfg, m map[string]string) error {
if mode, ok := m[comcfg.AUTHMode]; ok { 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 cfg.Authentication.LDAP.SearchDN = dn
} }
if pwd, ok := m[comcfg.LDAPSearchPwd]; ok { if pwd, ok := m[comcfg.LDAPSearchPwd]; ok {
cfg.Authentication.LDAP.SearchPwd = pwd cfg.Authentication.LDAP.SearchPassword = pwd
} }
if dn, ok := m[comcfg.LDAPBaseDN]; ok { if dn, ok := m[comcfg.LDAPBaseDN]; ok {
cfg.Authentication.LDAP.BaseDN = dn cfg.Authentication.LDAP.BaseDN = dn
@ -191,3 +177,4 @@ func populate(cfg *models.SystemCfg, m map[string]string) error {
return nil return nil
} }
*/

View File

@ -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()
}

View File

@ -15,16 +15,13 @@
package store package store
import (
"github.com/vmware/harbor/src/common/models"
)
// Driver defines methods that a configuration store driver must implement // Driver defines methods that a configuration store driver must implement
type Driver interface { type Driver interface {
// Name returns a human-readable name of the driver // Name returns a human-readable name of the driver
Name() string Name() string
// Read reads the configurations from store // Read reads all the configurations from store
Read() (*models.SystemCfg, error) Read() (map[string]interface{}, error)
// Write writes the configurations to store // Write writes the configurations to store, the configurations can be
Write(*models.SystemCfg) error // part of all
Write(map[string]interface{}) error
} }

View File

@ -23,7 +23,6 @@ import (
"sync" "sync"
"github.com/vmware/harbor/src/adminserver/systemcfg/store" "github.com/vmware/harbor/src/adminserver/systemcfg/store"
"github.com/vmware/harbor/src/common/models"
"github.com/vmware/harbor/src/common/utils/log" "github.com/vmware/harbor/src/common/utils/log"
) )
@ -68,11 +67,15 @@ func (c *cfgStore) Name() string {
} }
// Read ... // Read ...
func (c *cfgStore) Read() (*models.SystemCfg, error) { func (c *cfgStore) Read() (map[string]interface{}, error) {
c.RLock() c.RLock()
defer c.RUnlock() 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 { if err != nil {
return nil, err return nil, err
} }
@ -82,8 +85,8 @@ func (c *cfgStore) Read() (*models.SystemCfg, error) {
return nil, nil return nil, nil
} }
config := &models.SystemCfg{} config := map[string]interface{}{}
if err = json.Unmarshal(b, config); err != nil { if err = json.Unmarshal(b, &config); err != nil {
return nil, err return nil, err
} }
@ -91,14 +94,27 @@ func (c *cfgStore) Read() (*models.SystemCfg, error) {
} }
// Write ... // Write ...
func (c *cfgStore) Write(config *models.SystemCfg) error { func (c *cfgStore) Write(config map[string]interface{}) error {
b, err := json.MarshalIndent(config, "", " ") c.Lock()
defer c.Unlock()
cfg, err := read(c.path)
if err != nil { if err != nil {
return err return err
} }
c.Lock() if cfg == nil {
defer c.Unlock() 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 { if err = ioutil.WriteFile(c.path, b, 0600); err != nil {
return err return err

View File

@ -18,8 +18,6 @@ package json
import ( import (
"os" "os"
"testing" "testing"
"github.com/vmware/harbor/src/common/models"
) )
func TestReadWrite(t *testing.T) { func TestReadWrite(t *testing.T) {
@ -34,19 +32,21 @@ func TestReadWrite(t *testing.T) {
} }
}() }()
config := &models.SystemCfg{ if store.Name() != "JSON" {
Authentication: &models.Authentication{ t.Errorf("unexpected name: %s != %s", store.Name(), "JSON")
LDAP: &models.LDAP{}, return
}, }
Database: &models.Database{
MySQL: &models.MySQL{}, config := map[string]interface{}{
}, "key": "value",
} }
if err := store.Write(config); err != nil { 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 { 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
} }
} }

View File

@ -22,7 +22,7 @@ import (
"github.com/vmware/harbor/src/adminserver/systemcfg/store" "github.com/vmware/harbor/src/adminserver/systemcfg/store"
"github.com/vmware/harbor/src/adminserver/systemcfg/store/json" "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" "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 //read the following attrs from env every time boots up
func readFromEnv(cfg *models.SystemCfg) error { func readFromEnv(cfg map[string]interface{}) error {
cfg.DomainName = os.Getenv("EXT_ENDPOINT") cfg[comcfg.DomainName] = os.Getenv("EXT_ENDPOINT")
cfg.Database = &models.Database{ cfg[comcfg.DatabaseType] = os.Getenv("DATABASE_TYPE")
Type: os.Getenv("DATABASE_TYPE"), cfg[comcfg.MySQLHost] = os.Getenv("MYSQL_HOST")
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"),
},
}
port, err := strconv.Atoi(os.Getenv("MYSQL_PORT")) port, err := strconv.Atoi(os.Getenv("MYSQL_PORT"))
if err != nil { if err != nil {
return err return err
} }
cfg.Database.MySQL.Port = port cfg[comcfg.MySQLPort] = port
cfg[comcfg.MySQLUsername] = os.Getenv("MYSQL_USR")
cfg.TokenService = &models.TokenService{ cfg[comcfg.MySQLPassword] = os.Getenv("MYSQL_PWD")
URL: os.Getenv("TOKEN_SERVICE_URL"), cfg[comcfg.MySQLDatabase] = os.Getenv("MYSQL_DATABASE")
} cfg[comcfg.SQLiteFile] = os.Getenv("SQLITE_FILE")
cfg.Registry = &models.Registry{ cfg[comcfg.TokenServiceURL] = os.Getenv("TOKEN_SERVICE_URL")
URL: os.Getenv("REGISTRY_URL"), tokenExpi, err := strconv.Atoi(os.Getenv("TOKEN_EXPIRATION"))
}
//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"))
if err != nil { if err != nil {
return err return err
} }
cfg.TokenExpiration = exp cfg[comcfg.TokenExpiration] = tokenExpi
cfg.SecretKey = os.Getenv("SECRET_KEY") cfg[comcfg.RegistryURL] = os.Getenv("REGISTRY_URL")
//TODO remove
cfgExp, err := strconv.Atoi(os.Getenv("CFG_EXPIRATION")) 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 { if err != nil {
return err return err
} }
cfg.CfgExpiration = cfgExp cfg[comcfg.CfgExpiration] = cfgExpi
workers, err := strconv.Atoi(os.Getenv("MAX_JOB_WORKERS")) workers, err := strconv.Atoi(os.Getenv("MAX_JOB_WORKERS"))
if err != nil { if err != nil {
return err return err
} }
cfg.MaxJobWorkers = workers cfg[comcfg.MaxJobWorkers] = workers
return nil return nil
} }
func initFromEnv() (*models.SystemCfg, error) { func initFromEnv() (map[string]interface{}, error) {
cfg := &models.SystemCfg{} cfg := map[string]interface{}{}
if err := readFromEnv(cfg); err != nil { if err := readFromEnv(cfg); err != nil {
return nil, err return nil, err
} }
cfg.Authentication = &models.Authentication{ cfg[comcfg.AUTHMode] = os.Getenv("AUTH_MODE")
Mode: os.Getenv("AUTH_MODE"), cfg[comcfg.SelfRegistration] = os.Getenv("SELF_REGISTRATION") == "on"
SelfRegistration: os.Getenv("SELF_REGISTRATION") == "on", cfg[comcfg.LDAPURL] = os.Getenv("LDAP_URL")
LDAP: &models.LDAP{ cfg[comcfg.LDAPSearchDN] = os.Getenv("LDAP_SEARCH_DN")
URL: os.Getenv("LDAP_URL"), cfg[comcfg.LDAPSearchPwd] = os.Getenv("LDAP_SEARCH_PWD")
SearchDN: os.Getenv("LDAP_SEARCH_DN"), cfg[comcfg.LDAPBaseDN] = os.Getenv("LDAP_BASE_DN")
SearchPwd: os.Getenv("LDAP_SEARCH_PWD"), cfg[comcfg.LDAPFilter] = os.Getenv("LDAP_FILTER")
BaseDN: os.Getenv("LDAP_BASE_DN"), cfg[comcfg.LDAPUID] = os.Getenv("LDAP_UID")
Filter: os.Getenv("LDAP_FILTER"),
UID: os.Getenv("LDAP_UID"),
},
}
scope, err := strconv.Atoi(os.Getenv("LDAP_SCOPE")) scope, err := strconv.Atoi(os.Getenv("LDAP_SCOPE"))
if err != nil { if err != nil {
return nil, err return nil, err
} }
cfg.Authentication.LDAP.Scope = scope cfg[comcfg.LDAPScope] = scope
timeout, err := strconv.Atoi(os.Getenv("LDAP_TIMEOUT")) timeout, err := strconv.Atoi(os.Getenv("LDAP_TIMEOUT"))
if err != nil { if err != nil {
return nil, err return nil, err
} }
cfg.Authentication.LDAP.Timeout = timeout cfg[comcfg.LDAPTimeout] = timeout
cfg[comcfg.EmailHost] = os.Getenv("EMAIL_HOST")
cfg.Email = &models.Email{ port, err := strconv.Atoi(os.Getenv("EMAIL_PORT"))
Host: os.Getenv("EMAIL_HOST"), if err != nil {
Port: os.Getenv("EMAIL_PORT"), return nil, err
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.VerifyRemoteCert = os.Getenv("VERIFY_REMOTE_CERT") == "on" cfg[comcfg.EmailPort] = port
cfg.ProjectCreationRestriction = os.Getenv("PROJECT_CREATION_RESTRICTION") 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 return cfg, nil
} }
// GetSystemCfg returns the system configurations // GetSystemCfg returns the system configurations
func GetSystemCfg() (*models.SystemCfg, error) { func GetSystemCfg() (map[string]interface{}, error) {
return cfgStore.Read() return cfgStore.Read()
} }
// UpdateSystemCfg updates the system configurations // UpdateSystemCfg updates the system configurations
func UpdateSystemCfg(cfg *models.SystemCfg) error { func UpdateSystemCfg(cfg map[string]interface{}) error {
return cfgStore.Write(cfg) return cfgStore.Write(cfg)
} }

View File

@ -15,51 +15,43 @@
package systemcfg package systemcfg
/*
import ( import (
"os" "os"
"testing" "testing"
comcfg "github.com/vmware/harbor/src/common/config"
) )
// test functions under adminserver/systemcfg
func TestSystemcfg(t *testing.T) { func TestSystemcfg(t *testing.T) {
key := "JSON_STORE_PATH" key := "JSON_STORE_PATH"
tmpPath := "/tmp/config.json" path := "/tmp/config.json"
originalPath := os.Getenv(key) if _, err := os.Stat(path); err == nil {
defer func() { if err := os.Remove(path); err != nil {
if err := os.Remove(tmpPath); err != nil { t.Fatalf("failed to remove %s: %v", path, err)
t.Errorf("failed to remove %s: %v", tmpPath, 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.Setenv(key, path); err != nil {
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 {
t.Fatalf("failed to set env %s: %v", key, err) t.Fatalf("failed to set env %s: %v", key, err)
} }
m := map[string]string{ m := map[string]string{
"AUTH_MODE": comcfg.DBAuth,
"LDAP_SCOPE": "1", "LDAP_SCOPE": "1",
"LDAP_TIMEOUT": "30", "LDAP_TIMEOUT": "30",
"MYSQL_PORT": "3306", "MYSQL_PORT": "3306",
"MAX_JOB_WORKERS": "3", "MAX_JOB_WORKERS": "3",
"TOKEN_EXPIRATION": "30", "TOKEN_EXPIRATION": "30",
"CFG_EXPIRATION": "5", "CFG_EXPIRATION": "5",
"EMAIL_PORT": "25",
} }
for k, v := range m { for k, v := range m {
if err := os.Setenv(k, v); err != nil { if err := os.Setenv(k, v); err != nil {
t.Errorf("failed to set env %s: %v", k, err) t.Fatalf("failed to set env %s: %v", k, err)
return
} }
} }
@ -67,5 +59,46 @@ func TestSystemcfg(t *testing.T) {
t.Errorf("failed to initialize system configurations: %v", err) t.Errorf("failed to initialize system configurations: %v", err)
return 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
}
} }
*/

View File

@ -26,7 +26,6 @@ import (
"github.com/vmware/harbor/src/common/models" "github.com/vmware/harbor/src/common/models"
"github.com/vmware/harbor/src/common/utils/log" "github.com/vmware/harbor/src/common/utils/log"
"github.com/vmware/harbor/src/ui/auth" "github.com/vmware/harbor/src/ui/auth"
"github.com/vmware/harbor/src/ui/config"
"github.com/astaxie/beego" "github.com/astaxie/beego"
) )
@ -210,12 +209,3 @@ func (b *BaseAPI) GetPaginationParams() (page, pageSize int64) {
return page, pageSize return page, pageSize
} }
// GetIsInsecure ...
func GetIsInsecure() (bool, error) {
verify, err := config.VerifyRemoteCert()
if err != nil {
return false, err
}
return !verify, nil
}

View File

@ -13,23 +13,3 @@
limitations under the License. limitations under the License.
*/ */
package api 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")
}
*/

View File

@ -40,7 +40,15 @@ const (
LDAPScopeOnelevel = "2" LDAPScopeOnelevel = "2"
LDAPScopeSubtree = "3" LDAPScopeSubtree = "3"
DomainName = "domain_name"
AUTHMode = "auth_mode" 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" SelfRegistration = "self_registration"
LDAPURL = "ldap_url" LDAPURL = "ldap_url"
LDAPSearchDN = "ldap_search_dn" LDAPSearchDN = "ldap_search_dn"
@ -50,6 +58,8 @@ const (
LDAPFilter = "ldap_filter" LDAPFilter = "ldap_filter"
LDAPScope = "ldap_scope" LDAPScope = "ldap_scope"
LDAPTimeout = "ldap_timeout" LDAPTimeout = "ldap_timeout"
TokenServiceURL = "token_service_url"
RegistryURL = "registry_url"
EmailHost = "email_host" EmailHost = "email_host"
EmailPort = "email_port" EmailPort = "email_port"
EmailUsername = "email_username" EmailUsername = "email_username"
@ -60,30 +70,29 @@ const (
ProjectCreationRestriction = "project_creation_restriction" ProjectCreationRestriction = "project_creation_restriction"
VerifyRemoteCert = "verify_remote_cert" VerifyRemoteCert = "verify_remote_cert"
MaxJobWorkers = "max_job_workers" MaxJobWorkers = "max_job_workers"
TokenExpiration = "token_expiration"
CfgExpiration = "cfg_expiration" CfgExpiration = "cfg_expiration"
JobLogDir = "job_log_dir"
UseCompressedJS = "use_compressed_js"
SecretKey = "secret_key"
AdminInitialPassword = "admin_initial_password"
) )
// Manager manages configurations // Manager manages configurations
type Manager struct { type Manager struct {
Loader *Loader Loader *Loader
Parser Parser Parser *Parser
Cache bool Cache bool
cache cache.Cache cache cache.Cache
key string key string
} }
// Parser parses []byte to a specific configuration
type Parser interface {
// Parse ...
Parse([]byte) (interface{}, error)
}
// NewManager returns an instance of Manager // NewManager returns an instance of Manager
// url: the url from which loader loads configurations // 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{ m := &Manager{
Loader: NewLoader(url, secret), Loader: NewLoader(url, secret),
Parser: parser, Parser: &Parser{},
} }
if enableCache { if enableCache {
@ -101,7 +110,7 @@ func (m *Manager) Init() error {
} }
// Load configurations, if cache is enabled, cache the configurations // 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() b, err := m.Loader.Load()
if err != nil { if err != nil {
return nil, err return nil, err
@ -113,7 +122,7 @@ func (m *Manager) Load() (interface{}, error) {
} }
if m.Cache { if m.Cache {
expi, err := parseExpiration(b) expi, err := getCfgExpiration(c)
if err != nil { if err != nil {
return nil, err return nil, err
} }
@ -126,23 +135,26 @@ func (m *Manager) Load() (interface{}, error) {
return c, nil return c, nil
} }
func parseExpiration(b []byte) (int, error) { func getCfgExpiration(m map[string]interface{}) (int, error) {
expi := &struct { if m == nil {
Expi int `json:"cfg_expiration"` return 0, fmt.Errorf("can not get cfg expiration as configurations are null")
}{}
if err := json.Unmarshal(b, expi); err != nil {
return 0, err
} }
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, // Get : if cache is enabled, read configurations from cache,
// if cache is null or cache is disabled it loads configurations directly // 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 { if m.Cache {
c := m.cache.Get(m.key) c := m.cache.Get(m.key)
if c != nil { if c != nil {
return c, nil return c.(map[string]interface{}), nil
} }
} }
return m.Load() return m.Load()
@ -201,6 +213,11 @@ func (l *Loader) Load() ([]byte, error) {
return nil, err return nil, err
} }
defer resp.Body.Close() defer resp.Body.Close()
if resp.StatusCode != http.StatusOK {
return nil, fmt.Errorf("%d", resp.StatusCode)
}
b, err := ioutil.ReadAll(resp.Body) b, err := ioutil.ReadAll(resp.Body)
if err != nil { if err != nil {
return nil, err return nil, err
@ -230,5 +247,20 @@ func (l *Loader) Upload(b []byte) error {
return fmt.Errorf("unexpected http status code: %d", resp.StatusCode) return fmt.Errorf("unexpected http status code: %d", resp.StatusCode)
} }
log.Debug("configurations uploaded")
return nil 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
}

View File

@ -15,175 +15,12 @@
package dao package dao
/*
import ( import (
"testing" "testing"
"github.com/vmware/harbor/src/common/models" "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) { func TestAuthModeCanBeModified(t *testing.T) {
c, err := GetOrmer().QueryTable(&models.User{}).Count() c, err := GetOrmer().QueryTable(&models.User{}).Count()
if err != nil { if err != nil {
@ -233,4 +70,3 @@ func TestAuthModeCanBeModified(t *testing.T) {
} }
} }
} }
*/

View File

@ -26,7 +26,7 @@ type Authentication struct {
type LDAP struct { type LDAP struct {
URL string `json:"url"` URL string `json:"url"`
SearchDN string `json:"search_dn"` SearchDN string `json:"search_dn"`
SearchPwd string `json:"search_pwd"` SearchPassword string `json:"search_password"`
BaseDN string `json:"base_dn"` BaseDN string `json:"base_dn"`
Filter string `json:"filter"` Filter string `json:"filter"`
UID string `json:"uid"` UID string `json:"uid"`
@ -58,7 +58,7 @@ type SQLite struct {
// Email ... // Email ...
type Email struct { type Email struct {
Host string `json:"host"` Host string `json:"host"`
Port string `json:"port"` Port int `json:"port"`
Username string `json:"username"` Username string `json:"username"`
Password string `json:"password"` Password string `json:"password"`
SSL bool `json:"ssl"` SSL bool `json:"ssl"`

View File

@ -18,6 +18,7 @@ package email
import ( import (
"bytes" "bytes"
"crypto/tls" "crypto/tls"
"strconv"
//"strings" //"strings"
"net/smtp" "net/smtp"
@ -67,11 +68,11 @@ func (m Mail) SendMail() error {
} }
func sendMail(m Mail, auth smtp.Auth, content []byte) 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 { 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 { if err != nil {
return err return err
} }

View File

@ -20,17 +20,49 @@ import (
"net/http" "net/http"
"net/http/httptest" "net/http/httptest"
"github.com/vmware/harbor/src/common/models" "github.com/vmware/harbor/src/common/config"
) )
// NewAdminserver returns a mock admin server // NewAdminserver returns a mock admin server
func NewAdminserver() (*httptest.Server, error) { func NewAdminserver() (*httptest.Server, error) {
m := []*RequestHandlerMapping{} m := []*RequestHandlerMapping{}
b, err := json.Marshal(&models.SystemCfg{ b, err := json.Marshal(map[string]interface{}{
Authentication: &models.Authentication{ config.DomainName: "host01.com",
Mode: "db_auth", config.AUTHMode: config.DBAuth,
}, config.DatabaseType: "mysql",
Registry: &models.Registry{}, 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 { if err != nil {
return nil, err return nil, err

View File

@ -16,7 +16,6 @@
package config package config
import ( import (
"encoding/json"
"os" "os"
comcfg "github.com/vmware/harbor/src/common/config" comcfg "github.com/vmware/harbor/src/common/config"
@ -25,35 +24,13 @@ import (
var mg *comcfg.Manager 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 // Init configurations
func Init() error { func Init() error {
adminServerURL := os.Getenv("ADMIN_SERVER_URL") adminServerURL := os.Getenv("ADMIN_SERVER_URL")
if len(adminServerURL) == 0 { if len(adminServerURL) == 0 {
adminServerURL = "http://adminserver" adminServerURL = "http://adminserver"
} }
mg = comcfg.NewManager(adminServerURL, UISecret(), &parser{}, true) mg = comcfg.NewManager(adminServerURL, UISecret(), true)
if err := mg.Init(); err != nil { if err := mg.Init(); err != nil {
return err return err
@ -66,39 +43,44 @@ func Init() error {
return nil return nil
} }
func get() (*Configuration, error) {
c, err := mg.Get()
if err != nil {
return nil, err
}
return c.(*Configuration), nil
}
// VerifyRemoteCert returns bool value. // VerifyRemoteCert returns bool value.
func VerifyRemoteCert() (bool, error) { func VerifyRemoteCert() (bool, error) {
cfg, err := get() cfg, err := mg.Get()
if err != nil { if err != nil {
return true, err return true, err
} }
return cfg.VerifyRemoteCert, nil return cfg[comcfg.VerifyRemoteCert].(bool), nil
} }
// Database ... // Database ...
func Database() (*models.Database, error) { func Database() (*models.Database, error) {
cfg, err := get() cfg, err := mg.Get()
if err != nil { if err != nil {
return nil, err 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 ... // MaxJobWorkers ...
func MaxJobWorkers() (int, error) { func MaxJobWorkers() (int, error) {
cfg, err := get() cfg, err := mg.Get()
if err != nil { if err != nil {
return 0, err 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 // 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 // LocalRegURL returns the local registry url, job service will use this URL to pull image from the registry
func LocalRegURL() (string, error) { func LocalRegURL() (string, error) {
cfg, err := get() cfg, err := mg.Get()
if err != nil { if err != nil {
return "", err 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 // LogDir returns the absolute path to which the log file will be written
func LogDir() (string, error) { func LogDir() (string, error) {
cfg, err := get() cfg, err := mg.Get()
if err != nil { if err != nil {
return "", err return "", err
} }
return cfg.JobLogDir, nil return cfg[comcfg.JobLogDir].(string), nil
} }
// SecretKey will return the secret key for encryption/decryption password in target. // SecretKey will return the secret key for encryption/decryption password in target.
func SecretKey() (string, error) { func SecretKey() (string, error) {
cfg, err := get() cfg, err := mg.Get()
if err != nil { if err != nil {
return "", err 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 // UISecret returns the value of UI secret cookie, used for communication between UI and JobService

View File

@ -30,6 +30,41 @@ import (
"github.com/vmware/harbor/src/ui/config" "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 ... // ConfigAPI ...
type ConfigAPI struct { type ConfigAPI struct {
api.BaseAPI api.BaseAPI
@ -49,6 +84,11 @@ func (c *ConfigAPI) Prepare() {
} }
} }
type value struct {
Value interface{} `json:"value"`
Editable bool `json:"editable"`
}
// Get returns configurations // Get returns configurations
func (c *ConfigAPI) Get() { func (c *ConfigAPI) Get() {
cfg, err := config.GetSystemCfg() cfg, err := config.GetSystemCfg()
@ -57,24 +97,12 @@ func (c *ConfigAPI) Get() {
c.CustomAbort(http.StatusInternalServerError, http.StatusText(http.StatusInternalServerError)) c.CustomAbort(http.StatusInternalServerError, http.StatusText(http.StatusInternalServerError))
} }
if cfg.Database.MySQL != nil { m, err := convertForGet(cfg)
cfg.Database.MySQL.Password = ""
}
cfg.InitialAdminPwd = ""
cfg.SecretKey = ""
m := map[string]interface{}{}
m["config"] = cfg
editable, err := dao.AuthModeCanBeModified()
if err != nil { 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)) c.CustomAbort(http.StatusInternalServerError, http.StatusText(http.StatusInternalServerError))
} }
m["auth_mode_editable"] = editable
c.Data["json"] = m c.Data["json"] = m
c.ServeJSON() c.ServeJSON()
} }
@ -83,11 +111,19 @@ func (c *ConfigAPI) Get() {
func (c *ConfigAPI) Put() { func (c *ConfigAPI) Put() {
m := map[string]string{} m := map[string]string{}
c.DecodeJSONReq(&m) 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()) c.CustomAbort(http.StatusBadRequest, err.Error())
} }
if value, ok := m[comcfg.AUTHMode]; ok { if value, ok := cfg[comcfg.AUTHMode]; ok {
mode, err := config.AuthMode() mode, err := config.AuthMode()
if err != nil { if err != nil {
log.Errorf("failed to get auth mode: %v", err) 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) log.Errorf("failed to upload configurations: %v", err)
c.CustomAbort(http.StatusInternalServerError, http.StatusText(http.StatusInternalServerError)) c.CustomAbort(http.StatusInternalServerError, http.StatusText(http.StatusInternalServerError))
} }
@ -200,10 +242,11 @@ func validateCfg(c map[string]string) error {
return nil return nil
} }
/* //encode passwords and convert map[string]string to map[string]interface{}
func convert() ([]*models.Config, error) { func convertForPut(m map[string]string) (map[string]interface{}, error) {
cfgs := []*models.Config{} cfg := map[string]interface{}{}
var err error
/*
pwdKeys := []string{config.LDAP_SEARCH_PWD, config.EMAIL_PWD} pwdKeys := []string{config.LDAP_SEARCH_PWD, config.EMAIL_PWD}
for _, pwdKey := range pwdKeys { for _, pwdKey := range pwdKeys {
if pwd, ok := c[pwdKey]; ok && len(pwd) != 0 { if pwd, ok := c[pwdKey]; ok && len(pwd) != 0 {
@ -213,47 +256,58 @@ func convert() ([]*models.Config, error) {
} }
} }
} }
*/
for _, key := range configKeys { for k, v := range m {
if value, ok := c[key]; ok { cfg[k] = v
cfgs = append(cfgs, &models.Config{
Key: key,
Value: value,
})
}
} }
return cfgs, nil for _, k := range numKeys {
v, err := strconv.Atoi(cfg[k].(string))
if err != nil {
return nil, err
}
cfg[k] = v
}
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 { // delete sensitive attrs and add editable field to every attr
cfg[config.Key] = &value{ func convertForGet(cfg map[string]interface{}) (map[string]*value, error) {
Value: config.Value, result := map[string]*value{}
Editable: true,
}
}
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 { for _, del := range dels {
if _, ok := cfg[del]; ok { if _, ok := cfg[del]; ok {
delete(cfg, del) delete(cfg, del)
} }
} }
for k, v := range cfg {
result[k] = &value{
Value: v,
Editable: true,
}
}
flag, err := authModeCanBeModified() flag, err := authModeCanBeModified()
if err != nil { if err != nil {
return nil, err 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) { func authModeCanBeModified() (bool, error) {
return dao.AuthModeCanBeModified() return dao.AuthModeCanBeModified()
} }

View File

@ -366,14 +366,14 @@ func (ra *RepositoryAPI) initRepositoryClient(repoName string) (r *registry.Repo
return nil, err return nil, err
} }
insecure, err := api.GetIsInsecure() verify, err := config.VerifyRemoteCert()
if err != nil { if err != nil {
return nil, err return nil, err
} }
username, password, ok := ra.Ctx.Request.BasicAuth() username, password, ok := ra.Ctx.Request.BasicAuth()
if ok { if ok {
return newRepositoryClient(endpoint, insecure, username, password, return newRepositoryClient(endpoint, !verify, username, password,
repoName, "repository", repoName, "pull", "push", "*") repoName, "repository", repoName, "pull", "push", "*")
} }
@ -382,7 +382,7 @@ func (ra *RepositoryAPI) initRepositoryClient(repoName string) (r *registry.Repo
return nil, err return nil, err
} }
return cache.NewRepositoryClient(endpoint, insecure, username, repoName, return cache.NewRepositoryClient(endpoint, !verify, username, repoName,
"repository", repoName, "pull", "push", "*") "repository", repoName, "pull", "push", "*")
} }

View File

@ -102,12 +102,12 @@ func (t *TargetAPI) Ping() {
password = t.GetString("password") password = t.GetString("password")
} }
insecure, err := api.GetIsInsecure() verify, err := config.VerifyRemoteCert()
if err != nil { if err != nil {
log.Errorf("failed to check whether insecure or not: %v", err) log.Errorf("failed to check whether insecure or not: %v", err)
t.CustomAbort(http.StatusInternalServerError, http.StatusText(http.StatusInternalServerError)) 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 { if err != nil {
// timeout, dns resolve error, connection refused, etc. // timeout, dns resolve error, connection refused, etc.

View File

@ -116,7 +116,7 @@ func (l *Auth) Authenticate(m models.AuthModel) (*models.User, error) {
ldapSearchDn := settings.SearchDN ldapSearchDn := settings.SearchDN
if ldapSearchDn != "" { if ldapSearchDn != "" {
log.Debug("Search DN: ", ldapSearchDn) log.Debug("Search DN: ", ldapSearchDn)
ldapSearchPwd := settings.SearchPwd ldapSearchPwd := settings.SearchPassword
err = ldap.Bind(ldapSearchDn, ldapSearchPwd) err = ldap.Bind(ldapSearchDn, ldapSearchPwd)
if err != nil { if err != nil {
log.Debug("Bind search dn error", err) log.Debug("Bind search dn error", err)

View File

@ -26,35 +26,6 @@ import (
var mg *comcfg.Manager 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 // Init configurations
func Init() error { func Init() error {
adminServerURL := os.Getenv("ADMIN_SERVER_URL") adminServerURL := os.Getenv("ADMIN_SERVER_URL")
@ -62,7 +33,7 @@ func Init() error {
adminServerURL = "http://adminserver" adminServerURL = "http://adminserver"
} }
log.Debugf("admin server URL: %s", adminServerURL) 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 { if err := mg.Init(); err != nil {
return err return err
@ -75,14 +46,6 @@ func Init() error {
return nil return nil
} }
func get() (*Configuration, error) {
c, err := mg.Get()
if err != nil {
return nil, err
}
return c.(*Configuration), nil
}
// Load configurations // Load configurations
func Load() error { func Load() error {
_, err := mg.Load() _, err := mg.Load()
@ -90,7 +53,7 @@ func Load() error {
} }
// Upload uploads all system configutations to admin server // 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) b, err := json.Marshal(cfg)
if err != nil { if err != nil {
return err return err
@ -99,80 +62,92 @@ func Upload(cfg map[string]string) error {
} }
// GetSystemCfg returns the system configurations // GetSystemCfg returns the system configurations
func GetSystemCfg() (*models.SystemCfg, error) { func GetSystemCfg() (map[string]interface{}, error) {
raw, err := mg.Loader.Load() raw, err := mg.Loader.Load()
if err != nil { if err != nil {
return nil, err return nil, err
} }
cfg := &models.SystemCfg{} c, err := mg.Parser.Parse(raw)
if err = json.Unmarshal(raw, cfg); err != nil { if err != nil {
return nil, err return nil, err
} }
return cfg, nil
return c, nil
} }
// AuthMode ... // AuthMode ...
func AuthMode() (string, error) { func AuthMode() (string, error) {
cfg, err := get() cfg, err := mg.Get()
if err != nil { if err != nil {
return "", err return "", err
} }
return cfg.Authentication.Mode, nil return cfg[comcfg.AUTHMode].(string), nil
} }
// LDAP returns the setting of ldap server // LDAP returns the setting of ldap server
func LDAP() (*models.LDAP, error) { func LDAP() (*models.LDAP, error) {
cfg, err := get() cfg, err := mg.Get()
if err != nil { if err != nil {
return nil, err 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) // TokenExpiration returns the token expiration time (in minute)
func TokenExpiration() (int, error) { func TokenExpiration() (int, error) {
cfg, err := get() cfg, err := mg.Get()
if err != nil { if err != nil {
return 0, err return 0, err
} }
return cfg.TokenExpiration, nil return int(cfg[comcfg.TokenExpiration].(float64)), nil
} }
// DomainName returns the external URL of Harbor: protocal://host:port // DomainName returns the external URL of Harbor: protocal://host:port
func DomainName() (string, error) { func DomainName() (string, error) {
cfg, err := get() cfg, err := mg.Get()
if err != nil { if err != nil {
return "", err return "", err
} }
return cfg.DomainName, nil return cfg[comcfg.DomainName].(string), nil
} }
// SecretKey returns the secret key to encrypt the password of target // SecretKey returns the secret key to encrypt the password of target
func SecretKey() (string, error) { func SecretKey() (string, error) {
cfg, err := get() cfg, err := mg.Get()
if err != nil { if err != nil {
return "", err return "", err
} }
return cfg.SecretKey, nil return cfg[comcfg.SecretKey].(string), nil
} }
// SelfRegistration returns the enablement of self registration // SelfRegistration returns the enablement of self registration
func SelfRegistration() (bool, error) { func SelfRegistration() (bool, error) {
cfg, err := get() cfg, err := mg.Get()
if err != nil { if err != nil {
return false, err return false, err
} }
return cfg.Authentication.SelfRegistration, nil return cfg[comcfg.SelfRegistration].(bool), nil
} }
// RegistryURL ... // RegistryURL ...
func RegistryURL() (string, error) { func RegistryURL() (string, error) {
cfg, err := get() cfg, err := mg.Get()
if err != nil { if err != nil {
return "", err return "", err
} }
return cfg.Registry.URL, nil return cfg[comcfg.RegistryURL].(string), nil
} }
// InternalJobServiceURL returns jobservice URL for internal communication between Harbor containers // InternalJobServiceURL returns jobservice URL for internal communication between Harbor containers
@ -182,47 +157,70 @@ func InternalJobServiceURL() string {
// InitialAdminPassword returns the initial password for administrator // InitialAdminPassword returns the initial password for administrator
func InitialAdminPassword() (string, error) { func InitialAdminPassword() (string, error) {
cfg, err := get() cfg, err := mg.Get()
if err != nil { if err != nil {
return "", err 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 // OnlyAdminCreateProject returns the flag to restrict that only sys admin can create project
func OnlyAdminCreateProject() (bool, error) { func OnlyAdminCreateProject() (bool, error) {
cfg, err := get() cfg, err := mg.Get()
if err != nil { if err != nil {
return true, err return true, err
} }
return cfg.ProjectCreationRestriction == comcfg.ProCrtRestrAdmOnly, nil return cfg[comcfg.ProjectCreationRestriction].(string) == comcfg.ProCrtRestrAdmOnly, nil
} }
// VerifyRemoteCert returns bool value. // VerifyRemoteCert returns bool value.
func VerifyRemoteCert() (bool, error) { func VerifyRemoteCert() (bool, error) {
cfg, err := get() cfg, err := mg.Get()
if err != nil { if err != nil {
return true, err return true, err
} }
return cfg.VerifyRemoteCert, nil return cfg[comcfg.VerifyRemoteCert].(bool), nil
} }
// Email returns email server settings // Email returns email server settings
func Email() (*models.Email, error) { func Email() (*models.Email, error) {
cfg, err := get() cfg, err := mg.Get()
if err != nil { if err != nil {
return nil, err 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 // Database returns database settings
func Database() (*models.Database, error) { func Database() (*models.Database, error) {
cfg, err := get() cfg, err := mg.Get()
if err != nil { if err != nil {
return nil, err 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 // UISecret returns the value of UI secret cookie, used for communication between UI and JobService

View File

@ -44,7 +44,7 @@ func TestConfig(t *testing.T) {
t.Fatalf("failed to load configurations: %v", err) 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) t.Fatalf("failed to upload configurations: %v", err)
} }