mirror of
https://github.com/goharbor/harbor.git
synced 2024-09-30 06:18:02 +02:00
update
This commit is contained in:
parent
b6e27f6ea2
commit
f113f4a54f
32
src/adminserver/api/base_test.go
Normal file
32
src/adminserver/api/base_test.go
Normal 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)
|
||||
}
|
||||
|
||||
}
|
@ -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
|
||||
}
|
||||
*/
|
||||
|
25
src/adminserver/api/cfg_test.go
Normal file
25
src/adminserver/api/cfg_test.go
Normal 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()
|
||||
|
||||
}
|
@ -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
|
||||
}
|
||||
|
@ -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
|
||||
|
@ -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
|
||||
}
|
||||
}
|
||||
|
@ -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)
|
||||
}
|
||||
|
@ -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
|
||||
}
|
||||
}
|
||||
*/
|
||||
|
@ -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
|
||||
}
|
||||
|
@ -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")
|
||||
}
|
||||
*/
|
||||
|
@ -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
|
||||
}
|
||||
|
@ -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) {
|
||||
}
|
||||
}
|
||||
}
|
||||
*/
|
||||
|
@ -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"`
|
||||
|
@ -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
|
||||
}
|
||||
|
@ -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
|
||||
|
@ -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
|
||||
|
@ -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()
|
||||
}
|
||||
|
@ -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", "*")
|
||||
}
|
||||
|
||||
|
@ -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.
|
||||
|
@ -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)
|
||||
|
@ -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
|
||||
|
@ -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)
|
||||
}
|
||||
|
||||
|
Loading…
Reference in New Issue
Block a user