abstract adminserver client into a single package

This commit is contained in:
Wenkai Yin 2017-03-20 17:20:31 +08:00
parent a40994852f
commit 67612aa2e3
17 changed files with 818 additions and 525 deletions

View File

@ -26,7 +26,7 @@ import (
"testing" "testing"
"github.com/vmware/harbor/src/adminserver/systemcfg" "github.com/vmware/harbor/src/adminserver/systemcfg"
comcfg "github.com/vmware/harbor/src/common/config" "github.com/vmware/harbor/src/common"
"github.com/vmware/harbor/src/common/utils/test" "github.com/vmware/harbor/src/common/utils/test"
) )
@ -43,7 +43,7 @@ func TestConfigAPI(t *testing.T) {
secret := "secret" secret := "secret"
envs := map[string]string{ envs := map[string]string{
"AUTH_MODE": comcfg.DBAuth, "AUTH_MODE": common.DBAuth,
"JSON_CFG_STORE_PATH": configPath, "JSON_CFG_STORE_PATH": configPath,
"KEY_PATH": secretKeyPath, "KEY_PATH": secretKeyPath,
"UI_SECRET": secret, "UI_SECRET": secret,
@ -97,7 +97,7 @@ func TestConfigAPI(t *testing.T) {
return return
} }
scope := int(m[comcfg.LDAPScope].(float64)) scope := int(m[common.LDAPScope].(float64))
if scope != 3 { if scope != 3 {
t.Errorf("unexpected ldap scope: %d != %d", scope, 3) t.Errorf("unexpected ldap scope: %d != %d", scope, 3)
return return
@ -105,7 +105,7 @@ func TestConfigAPI(t *testing.T) {
// modify configurations // modify configurations
c := map[string]interface{}{ c := map[string]interface{}{
comcfg.AUTHMode: comcfg.LDAPAuth, common.AUTHMode: common.LDAPAuth,
} }
b, err := json.Marshal(c) b, err := json.Marshal(c)
@ -155,9 +155,9 @@ func TestConfigAPI(t *testing.T) {
return return
} }
mode := m[comcfg.AUTHMode].(string) mode := m[common.AUTHMode].(string)
if mode != comcfg.LDAPAuth { if mode != common.LDAPAuth {
t.Errorf("unexpected auth mode: %s != %s", mode, comcfg.LDAPAuth) t.Errorf("unexpected auth mode: %s != %s", mode, common.LDAPAuth)
return return
} }
@ -203,9 +203,9 @@ func TestConfigAPI(t *testing.T) {
return return
} }
mode = m[comcfg.AUTHMode].(string) mode = m[common.AUTHMode].(string)
if mode != comcfg.DBAuth { if mode != common.DBAuth {
t.Errorf("unexpected auth mode: %s != %s", mode, comcfg.LDAPAuth) t.Errorf("unexpected auth mode: %s != %s", mode, common.LDAPAuth)
return return
} }
} }

View File

@ -0,0 +1,51 @@
/*
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 auth
import (
"net/http"
)
// Authorizer authorizes request
type Authorizer interface {
Authorize(*http.Request) error
}
// NewSecretAuthorizer returns an instance of secretAuthorizer
func NewSecretAuthorizer(cookieName, secret string) Authorizer {
return &secretAuthorizer{
cookieName: cookieName,
secret: secret,
}
}
type secretAuthorizer struct {
cookieName string
secret string
}
func (s *secretAuthorizer) Authorize(req *http.Request) error {
if req == nil {
return nil
}
req.AddCookie(&http.Cookie{
Name: s.cookieName,
Value: s.secret,
})
return nil
}

View File

@ -0,0 +1,44 @@
/*
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 auth
import (
"net/http"
"testing"
"github.com/stretchr/testify/assert"
)
func TestAuthorize(t *testing.T) {
cookieName := "secret"
secret := "secret"
authorizer := NewSecretAuthorizer(cookieName, secret)
req, err := http.NewRequest("", "", nil)
if !assert.Nil(t, err, "unexpected error") {
return
}
err = authorizer.Authorize(req)
if !assert.Nil(t, err, "unexpected error") {
return
}
cookie, err := req.Cookie(cookieName)
if !assert.Nil(t, err, "unexpected error") {
return
}
assert.Equal(t, secret, cookie.Value, "unexpected cookie")
}

View File

@ -0,0 +1,184 @@
/*
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 client
import (
"bytes"
"encoding/json"
"fmt"
"io"
"io/ioutil"
"net/http"
"strings"
"github.com/vmware/harbor/src/adminserver/client/auth"
"github.com/vmware/harbor/src/adminserver/systeminfo/imagestorage"
"github.com/vmware/harbor/src/common/utils"
)
// Client defines methods that an Adminserver client should implement
type Client interface {
// Ping tests the connection with server
Ping() error
// GetCfgs returns system configurations
GetCfgs() (map[string]interface{}, error)
// UpdateCfgs updates system configurations
UpdateCfgs(map[string]interface{}) error
// ResetCfgs resets system configuratoins form environment variables
ResetCfgs() error
// Capacity returns the capacity of image storage
Capacity() (*imagestorage.Capacity, error)
}
// NewClient return an instance of Adminserver client
func NewClient(baseURL string, authorizer auth.Authorizer) Client {
baseURL = strings.TrimRight(baseURL, "/")
if !strings.Contains(baseURL, "://") {
baseURL = "http://" + baseURL
}
return &client{
baseURL: baseURL,
client: &http.Client{},
authorizer: authorizer,
}
}
type client struct {
baseURL string
client *http.Client
authorizer auth.Authorizer
}
// do creates request and authorizes it if authorizer is not nil
func (c *client) do(method, relativePath string, body io.Reader) (*http.Response, error) {
url := c.baseURL + relativePath
req, err := http.NewRequest(method, url, body)
if err != nil {
return nil, err
}
if c.authorizer != nil {
if err := c.authorizer.Authorize(req); err != nil {
return nil, err
}
}
return c.client.Do(req)
}
func (c *client) Ping() error {
addr := strings.Split(c.baseURL, "://")[1]
if !strings.Contains(addr, ":") {
addr = addr + ":80"
}
return utils.TestTCPConn(addr, 60, 2)
}
// GetCfgs ...
func (c *client) GetCfgs() (map[string]interface{}, error) {
resp, err := c.do(http.MethodGet, "/api/configurations", nil)
if err != nil {
return nil, err
}
defer resp.Body.Close()
b, err := ioutil.ReadAll(resp.Body)
if err != nil {
return nil, err
}
if resp.StatusCode != http.StatusOK {
return nil, fmt.Errorf("failed to get configurations: %d %s",
resp.StatusCode, b)
}
cfgs := map[string]interface{}{}
if err = json.Unmarshal(b, &cfgs); err != nil {
return nil, err
}
return cfgs, nil
}
// UpdateCfgs ...
func (c *client) UpdateCfgs(cfgs map[string]interface{}) error {
data, err := json.Marshal(cfgs)
if err != nil {
return err
}
resp, err := c.do(http.MethodPut, "/api/configurations", bytes.NewReader(data))
if err != nil {
return err
}
defer resp.Body.Close()
if resp.StatusCode != http.StatusOK {
b, err := ioutil.ReadAll(resp.Body)
if err != nil {
return err
}
return fmt.Errorf("failed to update configurations: %d %s",
resp.StatusCode, b)
}
return nil
}
// ResetCfgs ...
func (c *client) ResetCfgs() error {
resp, err := c.do(http.MethodPost, "/api/configurations/reset", nil)
if err != nil {
return err
}
if resp.StatusCode != http.StatusOK {
b, err := ioutil.ReadAll(resp.Body)
if err != nil {
return err
}
return fmt.Errorf("failed to reset configurations: %d %s",
resp.StatusCode, b)
}
return nil
}
// Capacity ...
func (c *client) Capacity() (*imagestorage.Capacity, error) {
resp, err := c.do(http.MethodGet, "/api/systeminfo/capacity", nil)
if err != nil {
return nil, err
}
defer resp.Body.Close()
b, err := ioutil.ReadAll(resp.Body)
if err != nil {
return nil, err
}
if resp.StatusCode != http.StatusOK {
return nil, fmt.Errorf("failed to get capacity: %d %s",
resp.StatusCode, b)
}
capacity := &imagestorage.Capacity{}
if err = json.Unmarshal(b, capacity); err != nil {
return nil, err
}
return capacity, nil
}

View File

@ -0,0 +1,82 @@
/*
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 client
import (
"fmt"
"os"
"testing"
"github.com/stretchr/testify/assert"
"github.com/vmware/harbor/src/common"
"github.com/vmware/harbor/src/common/utils/test"
)
var c Client
func TestMain(m *testing.M) {
server, err := test.NewAdminserver(nil)
if err != nil {
fmt.Printf("failed to create adminserver: %v", err)
os.Exit(1)
}
c = NewClient(server.URL, nil)
os.Exit(m.Run())
}
func TestPing(t *testing.T) {
err := c.Ping()
assert.Nil(t, err, "unexpected error")
}
func TestGetCfgs(t *testing.T) {
cfgs, err := c.GetCfgs()
if !assert.Nil(t, err, "unexpected error") {
return
}
assert.Equal(t, common.DBAuth, cfgs[common.AUTHMode], "unexpected configuration")
}
func TestUpdateCfgs(t *testing.T) {
cfgs := map[string]interface{}{
common.AUTHMode: common.LDAPAuth,
}
err := c.UpdateCfgs(cfgs)
if !assert.Nil(t, err, "unexpected error") {
return
}
}
func TestResetCfgs(t *testing.T) {
err := c.ResetCfgs()
if !assert.Nil(t, err, "unexpected error") {
return
}
}
func TestCapacity(t *testing.T) {
capacity, err := c.Capacity()
if !assert.Nil(t, err, "unexpected error") {
return
}
assert.Equal(t, uint64(100), capacity.Total)
assert.Equal(t, uint64(90), capacity.Free)
}

View File

@ -23,6 +23,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"
comcfg "github.com/vmware/harbor/src/common/config" comcfg "github.com/vmware/harbor/src/common/config"
"github.com/vmware/harbor/src/common/utils" "github.com/vmware/harbor/src/common/utils"
"github.com/vmware/harbor/src/common/utils/log" "github.com/vmware/harbor/src/common/utils/log"
@ -40,82 +41,82 @@ var (
// attrs need to be encrypted or decrypted // attrs need to be encrypted or decrypted
attrs = []string{ attrs = []string{
comcfg.EmailPassword, common.EmailPassword,
comcfg.LDAPSearchPwd, common.LDAPSearchPwd,
comcfg.MySQLPassword, common.MySQLPassword,
comcfg.AdminInitialPassword, common.AdminInitialPassword,
} }
// all configurations need read from environment variables // all configurations need read from environment variables
allEnvs = map[string]interface{}{ allEnvs = map[string]interface{}{
comcfg.ExtEndpoint: "EXT_ENDPOINT", common.ExtEndpoint: "EXT_ENDPOINT",
comcfg.AUTHMode: "AUTH_MODE", common.AUTHMode: "AUTH_MODE",
comcfg.SelfRegistration: &parser{ common.SelfRegistration: &parser{
env: "SELF_REGISTRATION", env: "SELF_REGISTRATION",
parse: parseStringToBool, parse: parseStringToBool,
}, },
comcfg.DatabaseType: "DATABASE_TYPE", common.DatabaseType: "DATABASE_TYPE",
comcfg.MySQLHost: "MYSQL_HOST", common.MySQLHost: "MYSQL_HOST",
comcfg.MySQLPort: &parser{ common.MySQLPort: &parser{
env: "MYSQL_PORT", env: "MYSQL_PORT",
parse: parseStringToInt, parse: parseStringToInt,
}, },
comcfg.MySQLUsername: "MYSQL_USR", common.MySQLUsername: "MYSQL_USR",
comcfg.MySQLPassword: "MYSQL_PWD", common.MySQLPassword: "MYSQL_PWD",
comcfg.MySQLDatabase: "MYSQL_DATABASE", common.MySQLDatabase: "MYSQL_DATABASE",
comcfg.SQLiteFile: "SQLITE_FILE", common.SQLiteFile: "SQLITE_FILE",
comcfg.LDAPURL: "LDAP_URL", common.LDAPURL: "LDAP_URL",
comcfg.LDAPSearchDN: "LDAP_SEARCH_DN", common.LDAPSearchDN: "LDAP_SEARCH_DN",
comcfg.LDAPSearchPwd: "LDAP_SEARCH_PWD", common.LDAPSearchPwd: "LDAP_SEARCH_PWD",
comcfg.LDAPBaseDN: "LDAP_BASE_DN", common.LDAPBaseDN: "LDAP_BASE_DN",
comcfg.LDAPFilter: "LDAP_FILTER", common.LDAPFilter: "LDAP_FILTER",
comcfg.LDAPUID: "LDAP_UID", common.LDAPUID: "LDAP_UID",
comcfg.LDAPScope: &parser{ common.LDAPScope: &parser{
env: "LDAP_SCOPE", env: "LDAP_SCOPE",
parse: parseStringToInt, parse: parseStringToInt,
}, },
comcfg.LDAPTimeout: &parser{ common.LDAPTimeout: &parser{
env: "LDAP_TIMEOUT", env: "LDAP_TIMEOUT",
parse: parseStringToInt, parse: parseStringToInt,
}, },
comcfg.EmailHost: "EMAIL_HOST", common.EmailHost: "EMAIL_HOST",
comcfg.EmailPort: &parser{ common.EmailPort: &parser{
env: "EMAIL_PORT", env: "EMAIL_PORT",
parse: parseStringToInt, parse: parseStringToInt,
}, },
comcfg.EmailUsername: "EMAIL_USR", common.EmailUsername: "EMAIL_USR",
comcfg.EmailPassword: "EMAIL_PWD", common.EmailPassword: "EMAIL_PWD",
comcfg.EmailSSL: &parser{ common.EmailSSL: &parser{
env: "EMAIL_SSL", env: "EMAIL_SSL",
parse: parseStringToBool, parse: parseStringToBool,
}, },
comcfg.EmailFrom: "EMAIL_FROM", common.EmailFrom: "EMAIL_FROM",
comcfg.EmailIdentity: "EMAIL_IDENTITY", common.EmailIdentity: "EMAIL_IDENTITY",
comcfg.RegistryURL: "REGISTRY_URL", common.RegistryURL: "REGISTRY_URL",
comcfg.TokenExpiration: &parser{ common.TokenExpiration: &parser{
env: "TOKEN_EXPIRATION", env: "TOKEN_EXPIRATION",
parse: parseStringToInt, parse: parseStringToInt,
}, },
comcfg.UseCompressedJS: &parser{ common.UseCompressedJS: &parser{
env: "USE_COMPRESSED_JS", env: "USE_COMPRESSED_JS",
parse: parseStringToBool, parse: parseStringToBool,
}, },
comcfg.CfgExpiration: &parser{ common.CfgExpiration: &parser{
env: "CFG_EXPIRATION", env: "CFG_EXPIRATION",
parse: parseStringToInt, parse: parseStringToInt,
}, },
comcfg.MaxJobWorkers: &parser{ common.MaxJobWorkers: &parser{
env: "MAX_JOB_WORKERS", env: "MAX_JOB_WORKERS",
parse: parseStringToInt, parse: parseStringToInt,
}, },
comcfg.VerifyRemoteCert: &parser{ common.VerifyRemoteCert: &parser{
env: "VERIFY_REMOTE_CERT", env: "VERIFY_REMOTE_CERT",
parse: parseStringToBool, parse: parseStringToBool,
}, },
comcfg.ProjectCreationRestriction: "PROJECT_CREATION_RESTRICTION", common.ProjectCreationRestriction: "PROJECT_CREATION_RESTRICTION",
comcfg.AdminInitialPassword: "HARBOR_ADMIN_PASSWORD", common.AdminInitialPassword: "HARBOR_ADMIN_PASSWORD",
comcfg.AdmiralEndpoint: "ADMIRAL_URL", common.AdmiralEndpoint: "ADMIRAL_URL",
comcfg.WithNotary: &parser{ common.WithNotary: &parser{
env: "WITH_NOTARY", env: "WITH_NOTARY",
parse: parseStringToBool, parse: parseStringToBool,
}, },
@ -124,23 +125,23 @@ var (
// configurations need read from environment variables // configurations need read from environment variables
// every time the system startup // every time the system startup
repeatLoadEnvs = map[string]interface{}{ repeatLoadEnvs = map[string]interface{}{
comcfg.ExtEndpoint: "EXT_ENDPOINT", common.ExtEndpoint: "EXT_ENDPOINT",
comcfg.MySQLPassword: "MYSQL_PWD", common.MySQLPassword: "MYSQL_PWD",
comcfg.MaxJobWorkers: &parser{ common.MaxJobWorkers: &parser{
env: "MAX_JOB_WORKERS", env: "MAX_JOB_WORKERS",
parse: parseStringToInt, parse: parseStringToInt,
}, },
// TODO remove this config? // TODO remove this config?
comcfg.UseCompressedJS: &parser{ common.UseCompressedJS: &parser{
env: "USE_COMPRESSED_JS", env: "USE_COMPRESSED_JS",
parse: parseStringToBool, parse: parseStringToBool,
}, },
comcfg.CfgExpiration: &parser{ common.CfgExpiration: &parser{
env: "CFG_EXPIRATION", env: "CFG_EXPIRATION",
parse: parseStringToInt, parse: parseStringToInt,
}, },
comcfg.AdmiralEndpoint: "ADMIRAL_URL", common.AdmiralEndpoint: "ADMIRAL_URL",
comcfg.WithNotary: &parser{ common.WithNotary: &parser{
env: "WITH_NOTARY", env: "WITH_NOTARY",
parse: parseStringToBool, parse: parseStringToBool,
}, },

View File

@ -19,7 +19,7 @@ import (
"os" "os"
"testing" "testing"
comcfg "github.com/vmware/harbor/src/common/config" "github.com/vmware/harbor/src/common"
"github.com/vmware/harbor/src/common/utils/test" "github.com/vmware/harbor/src/common/utils/test"
) )
@ -54,7 +54,7 @@ func TestSystemcfg(t *testing.T) {
} }
m := map[string]string{ m := map[string]string{
"AUTH_MODE": comcfg.DBAuth, "AUTH_MODE": common.DBAuth,
"LDAP_SCOPE": "1", "LDAP_SCOPE": "1",
"LDAP_TIMEOUT": "30", "LDAP_TIMEOUT": "30",
"MYSQL_PORT": "3306", "MYSQL_PORT": "3306",
@ -93,13 +93,13 @@ func TestSystemcfg(t *testing.T) {
return return
} }
if cfg[comcfg.AUTHMode] != comcfg.DBAuth { if cfg[common.AUTHMode] != common.DBAuth {
t.Errorf("unexpected auth mode: %s != %s", t.Errorf("unexpected auth mode: %s != %s",
cfg[comcfg.AUTHMode], comcfg.DBAuth) cfg[common.AUTHMode], common.DBAuth)
return return
} }
cfg[comcfg.AUTHMode] = comcfg.LDAPAuth cfg[common.AUTHMode] = common.LDAPAuth
if err = UpdateSystemCfg(cfg); err != nil { if err = UpdateSystemCfg(cfg); err != nil {
t.Errorf("failed to update system configurations: %v", err) t.Errorf("failed to update system configurations: %v", err)
@ -112,9 +112,9 @@ func TestSystemcfg(t *testing.T) {
return return
} }
if cfg[comcfg.AUTHMode] != comcfg.LDAPAuth { if cfg[common.AUTHMode] != common.LDAPAuth {
t.Errorf("unexpected auth mode: %s != %s", t.Errorf("unexpected auth mode: %s != %s",
cfg[comcfg.AUTHMode], comcfg.DBAuth) cfg[common.AUTHMode], common.DBAuth)
return return
} }
@ -129,9 +129,9 @@ func TestSystemcfg(t *testing.T) {
return return
} }
if cfg[comcfg.AUTHMode] != comcfg.DBAuth { if cfg[common.AUTHMode] != common.DBAuth {
t.Errorf("unexpected auth mode: %s != %s", t.Errorf("unexpected auth mode: %s != %s",
cfg[comcfg.AUTHMode], comcfg.DBAuth) cfg[common.AUTHMode], common.DBAuth)
return return
} }
} }

View File

@ -17,83 +17,26 @@
package config package config
import ( import (
"bytes"
"encoding/json"
"fmt" "fmt"
"io/ioutil"
"net/http"
"strings"
"time" "time"
"github.com/astaxie/beego/cache" "github.com/astaxie/beego/cache"
"github.com/vmware/harbor/src/common/utils" "github.com/vmware/harbor/src/adminserver/client"
"github.com/vmware/harbor/src/common/utils/log" "github.com/vmware/harbor/src/common"
)
// const variables
const (
DBAuth = "db_auth"
LDAPAuth = "ldap_auth"
ProCrtRestrEveryone = "everyone"
ProCrtRestrAdmOnly = "adminonly"
LDAPScopeBase = "1"
LDAPScopeOnelevel = "2"
LDAPScopeSubtree = "3"
ExtEndpoint = "ext_endpoint"
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"
LDAPSearchPwd = "ldap_search_password"
LDAPBaseDN = "ldap_base_dn"
LDAPUID = "ldap_uid"
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"
EmailPassword = "email_password"
EmailFrom = "email_from"
EmailSSL = "email_ssl"
EmailIdentity = "email_identity"
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"
AdminInitialPassword = "admin_initial_password"
AdmiralEndpoint = "admiral_url"
WithNotary = "with_notary"
) )
// Manager manages configurations // Manager manages configurations
type Manager struct { type Manager struct {
Loader *Loader client client.Client
Parser *Parser
Cache bool Cache bool
cache cache.Cache cache cache.Cache
key string key string
} }
// NewManager returns an instance of Manager // NewManager returns an instance of Manager
// url: the url from which loader loads configurations func NewManager(client client.Client, enableCache bool) *Manager {
func NewManager(url, secret string, enableCache bool) *Manager {
m := &Manager{ m := &Manager{
Loader: NewLoader(url, secret), client: client,
Parser: &Parser{},
} }
if enableCache { if enableCache {
@ -105,19 +48,9 @@ func NewManager(url, secret string, enableCache bool) *Manager {
return m return m
} }
// Init loader
func (m *Manager) Init() error {
return m.Loader.Init()
}
// Load configurations, if cache is enabled, cache the configurations // Load configurations, if cache is enabled, cache the configurations
func (m *Manager) Load() (map[string]interface{}, error) { func (m *Manager) Load() (map[string]interface{}, error) {
b, err := m.Loader.Load() c, err := m.client.GetCfgs()
if err != nil {
return nil, err
}
c, err := m.Parser.Parse(b)
if err != nil { if err != nil {
return nil, err return nil, err
} }
@ -127,7 +60,15 @@ func (m *Manager) Load() (map[string]interface{}, error) {
if err != nil { if err != nil {
return nil, err return nil, err
} }
if err = m.cache.Put(m.key, c,
// copy the configuration map so that later modification to the
// map does not effect the cached value
cachedCfgs := map[string]interface{}{}
for k, v := range c {
cachedCfgs[k] = v
}
if err = m.cache.Put(m.key, cachedCfgs,
time.Duration(expi)*time.Second); err != nil { time.Duration(expi)*time.Second); err != nil {
return nil, err return nil, err
} }
@ -138,7 +79,7 @@ func (m *Manager) Load() (map[string]interface{}, error) {
// Reset configurations // Reset configurations
func (m *Manager) Reset() error { func (m *Manager) Reset() error {
return m.Loader.Reset() return m.client.ResetCfgs()
} }
func getCfgExpiration(m map[string]interface{}) (int, error) { func getCfgExpiration(m map[string]interface{}) (int, error) {
@ -146,7 +87,7 @@ func getCfgExpiration(m map[string]interface{}) (int, error) {
return 0, fmt.Errorf("can not get cfg expiration as configurations are null") return 0, fmt.Errorf("can not get cfg expiration as configurations are null")
} }
expi, ok := m[CfgExpiration] expi, ok := m[common.CfgExpiration]
if !ok { if !ok {
return 0, fmt.Errorf("cfg expiration is not set") return 0, fmt.Errorf("cfg expiration is not set")
} }
@ -167,133 +108,6 @@ func (m *Manager) Get() (map[string]interface{}, error) {
} }
// Upload configurations // Upload configurations
func (m *Manager) Upload(b []byte) error { func (m *Manager) Upload(cfgs map[string]interface{}) error {
return m.Loader.Upload(b) return m.client.UpdateCfgs(cfgs)
}
// Loader loads and uploads configurations
type Loader struct {
url string
secret string
client *http.Client
}
// NewLoader ...
func NewLoader(url, secret string) *Loader {
return &Loader{
url: url,
secret: secret,
client: &http.Client{},
}
}
// Init waits remote server to be ready by testing connections with it
func (l *Loader) Init() error {
addr := l.url
if strings.Contains(addr, "://") {
addr = strings.Split(addr, "://")[1]
}
if !strings.Contains(addr, ":") {
addr = addr + ":80"
}
return utils.TestTCPConn(addr, 60, 2)
}
// Load configurations from remote server
func (l *Loader) Load() ([]byte, error) {
log.Debug("loading configurations...")
req, err := http.NewRequest("GET", l.url+"/api/configurations", nil)
if err != nil {
return nil, err
}
req.AddCookie(&http.Cookie{
Name: "secret",
Value: l.secret,
})
resp, err := l.client.Do(req)
if err != nil {
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
}
log.Debug("configurations load completed")
return b, nil
}
// Upload configurations to remote server
func (l *Loader) Upload(b []byte) error {
req, err := http.NewRequest("PUT", l.url+"/api/configurations", bytes.NewReader(b))
if err != nil {
return err
}
req.AddCookie(&http.Cookie{
Name: "secret",
Value: l.secret,
})
resp, err := l.client.Do(req)
if err != nil {
return err
}
if resp.StatusCode != http.StatusOK {
return fmt.Errorf("unexpected http status code: %d", resp.StatusCode)
}
log.Debug("configurations uploaded")
return nil
}
// Reset sends configurations resetting command to
// remote server
func (l *Loader) Reset() error {
req, err := http.NewRequest("POST", l.url+"/api/configurations/reset", nil)
if err != nil {
return err
}
req.AddCookie(&http.Cookie{
Name: "secret",
Value: l.secret,
})
resp, err := l.client.Do(req)
if err != nil {
return err
}
if resp.StatusCode != http.StatusOK {
return fmt.Errorf("unexpected http status code: %d", resp.StatusCode)
}
log.Debug("configurations resetted")
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
} }

65
src/common/const.go Normal file
View File

@ -0,0 +1,65 @@
/*
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 common
// const variables
const (
DBAuth = "db_auth"
LDAPAuth = "ldap_auth"
ProCrtRestrEveryone = "everyone"
ProCrtRestrAdmOnly = "adminonly"
LDAPScopeBase = "1"
LDAPScopeOnelevel = "2"
LDAPScopeSubtree = "3"
ExtEndpoint = "ext_endpoint"
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"
LDAPSearchPwd = "ldap_search_password"
LDAPBaseDN = "ldap_base_dn"
LDAPUID = "ldap_uid"
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"
EmailPassword = "email_password"
EmailFrom = "email_from"
EmailSSL = "email_ssl"
EmailIdentity = "email_identity"
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"
AdminInitialPassword = "admin_initial_password"
AdmiralEndpoint = "admiral_url"
WithNotary = "with_notary"
)

View File

@ -21,7 +21,7 @@ import (
"os" "os"
"testing" "testing"
"github.com/vmware/harbor/src/common/config" "github.com/vmware/harbor/src/common"
"github.com/vmware/harbor/src/common/dao" "github.com/vmware/harbor/src/common/dao"
"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"
@ -30,24 +30,24 @@ import (
) )
var adminServerLdapTestConfig = map[string]interface{}{ var adminServerLdapTestConfig = map[string]interface{}{
config.ExtEndpoint: "host01.com", common.ExtEndpoint: "host01.com",
config.AUTHMode: "ldap_auth", common.AUTHMode: "ldap_auth",
config.DatabaseType: "mysql", common.DatabaseType: "mysql",
config.MySQLHost: "127.0.0.1", common.MySQLHost: "127.0.0.1",
config.MySQLPort: 3306, common.MySQLPort: 3306,
config.MySQLUsername: "root", common.MySQLUsername: "root",
config.MySQLPassword: "root123", common.MySQLPassword: "root123",
config.MySQLDatabase: "registry", common.MySQLDatabase: "registry",
config.SQLiteFile: "/tmp/registry.db", common.SQLiteFile: "/tmp/registry.db",
//config.SelfRegistration: true, //config.SelfRegistration: true,
config.LDAPURL: "ldap://127.0.0.1", common.LDAPURL: "ldap://127.0.0.1",
config.LDAPSearchDN: "cn=admin,dc=example,dc=com", common.LDAPSearchDN: "cn=admin,dc=example,dc=com",
config.LDAPSearchPwd: "admin", common.LDAPSearchPwd: "admin",
config.LDAPBaseDN: "dc=example,dc=com", common.LDAPBaseDN: "dc=example,dc=com",
config.LDAPUID: "uid", common.LDAPUID: "uid",
config.LDAPFilter: "", common.LDAPFilter: "",
config.LDAPScope: 3, common.LDAPScope: 3,
config.LDAPTimeout: 30, common.LDAPTimeout: 30,
// config.TokenServiceURL: "", // config.TokenServiceURL: "",
// config.RegistryURL: "", // config.RegistryURL: "",
// config.EmailHost: "", // config.EmailHost: "",
@ -61,10 +61,10 @@ var adminServerLdapTestConfig = map[string]interface{}{
// config.VerifyRemoteCert: false, // config.VerifyRemoteCert: false,
// config.MaxJobWorkers: 3, // config.MaxJobWorkers: 3,
// config.TokenExpiration: 30, // config.TokenExpiration: 30,
config.CfgExpiration: 5, common.CfgExpiration: 5,
// config.JobLogDir: "/var/log/jobs", // config.JobLogDir: "/var/log/jobs",
// config.UseCompressedJS: true, // config.UseCompressedJS: true,
config.AdminInitialPassword: "password", common.AdminInitialPassword: "password",
} }
func TestMain(t *testing.T) { func TestMain(t *testing.T) {

View File

@ -20,46 +20,47 @@ import (
"net/http" "net/http"
"net/http/httptest" "net/http/httptest"
"github.com/vmware/harbor/src/common/config" "github.com/vmware/harbor/src/adminserver/systeminfo/imagestorage"
"github.com/vmware/harbor/src/common"
) )
var adminServerDefaultConfig = map[string]interface{}{ var adminServerDefaultConfig = map[string]interface{}{
config.ExtEndpoint: "https://host01.com", common.ExtEndpoint: "https://host01.com",
config.AUTHMode: config.DBAuth, common.AUTHMode: common.DBAuth,
config.DatabaseType: "mysql", common.DatabaseType: "mysql",
config.MySQLHost: "127.0.0.1", common.MySQLHost: "127.0.0.1",
config.MySQLPort: 3306, common.MySQLPort: 3306,
config.MySQLUsername: "user01", common.MySQLUsername: "user01",
config.MySQLPassword: "password", common.MySQLPassword: "password",
config.MySQLDatabase: "registry", common.MySQLDatabase: "registry",
config.SQLiteFile: "/tmp/registry.db", common.SQLiteFile: "/tmp/registry.db",
config.SelfRegistration: true, common.SelfRegistration: true,
config.LDAPURL: "ldap://127.0.0.1", common.LDAPURL: "ldap://127.0.0.1",
config.LDAPSearchDN: "uid=searchuser,ou=people,dc=mydomain,dc=com", common.LDAPSearchDN: "uid=searchuser,ou=people,dc=mydomain,dc=com",
config.LDAPSearchPwd: "password", common.LDAPSearchPwd: "password",
config.LDAPBaseDN: "ou=people,dc=mydomain,dc=com", common.LDAPBaseDN: "ou=people,dc=mydomain,dc=com",
config.LDAPUID: "uid", common.LDAPUID: "uid",
config.LDAPFilter: "", common.LDAPFilter: "",
config.LDAPScope: 3, common.LDAPScope: 3,
config.LDAPTimeout: 30, common.LDAPTimeout: 30,
config.TokenServiceURL: "http://token_service", common.TokenServiceURL: "http://token_service",
config.RegistryURL: "http://registry", common.RegistryURL: "http://registry",
config.EmailHost: "127.0.0.1", common.EmailHost: "127.0.0.1",
config.EmailPort: 25, common.EmailPort: 25,
config.EmailUsername: "user01", common.EmailUsername: "user01",
config.EmailPassword: "password", common.EmailPassword: "password",
config.EmailFrom: "from", common.EmailFrom: "from",
config.EmailSSL: true, common.EmailSSL: true,
config.EmailIdentity: "", common.EmailIdentity: "",
config.ProjectCreationRestriction: config.ProCrtRestrAdmOnly, common.ProjectCreationRestriction: common.ProCrtRestrAdmOnly,
config.VerifyRemoteCert: false, common.VerifyRemoteCert: false,
config.MaxJobWorkers: 3, common.MaxJobWorkers: 3,
config.TokenExpiration: 30, common.TokenExpiration: 30,
config.CfgExpiration: 5, common.CfgExpiration: 5,
config.UseCompressedJS: true, common.UseCompressedJS: true,
config.AdminInitialPassword: "password", common.AdminInitialPassword: "password",
config.AdmiralEndpoint: "http://www.vmware.com", common.AdmiralEndpoint: "http://www.vmware.com",
config.WithNotary: false, common.WithNotary: false,
} }
// NewAdminserver returns a mock admin server // NewAdminserver returns a mock admin server
@ -101,5 +102,32 @@ func NewAdminserver(config map[string]interface{}) (*httptest.Server, error) {
}), }),
}) })
capacityHandler, err := NewCapacityHandle()
if err != nil {
return nil, err
}
m = append(m, &RequestHandlerMapping{
Method: "GET",
Pattern: "/api/systeminfo/capacity",
Handler: capacityHandler,
})
return NewServer(m...), nil return NewServer(m...), nil
} }
// NewCapacityHandle ...
func NewCapacityHandle() (func(http.ResponseWriter, *http.Request), error) {
capacity := imagestorage.Capacity{
Total: 100,
Free: 90,
}
b, err := json.Marshal(capacity)
if err != nil {
return nil, err
}
resp := &Response{
StatusCode: http.StatusOK,
Body: b,
}
return Handler(resp), nil
}

View File

@ -16,21 +16,28 @@
package config package config
import ( import (
"fmt"
"os" "os"
"github.com/vmware/harbor/src/adminserver/client"
"github.com/vmware/harbor/src/adminserver/client/auth"
"github.com/vmware/harbor/src/common"
comcfg "github.com/vmware/harbor/src/common/config" comcfg "github.com/vmware/harbor/src/common/config"
"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"
) )
const ( const (
defaultKeyPath string = "/etc/jobservice/key" defaultKeyPath string = "/etc/jobservice/key"
defaultLogDir string = "/var/log/jobs" defaultLogDir string = "/var/log/jobs"
secretCookieName string = "secret"
) )
var ( var (
mg *comcfg.Manager // AdminserverClient is a client for adminserver
keyProvider comcfg.KeyProvider AdminserverClient client.Client
mg *comcfg.Manager
keyProvider comcfg.KeyProvider
) )
// Init configurations // Init configurations
@ -42,12 +49,15 @@ func Init() error {
if len(adminServerURL) == 0 { if len(adminServerURL) == 0 {
adminServerURL = "http://adminserver" adminServerURL = "http://adminserver"
} }
mg = comcfg.NewManager(adminServerURL, JobserviceSecret(), true) log.Infof("initializing client for adminserver %s ...", adminServerURL)
authorizer := auth.NewSecretAuthorizer(secretCookieName, UISecret())
if err := mg.Init(); err != nil { AdminserverClient = client.NewClient(adminServerURL, authorizer)
return err if err := AdminserverClient.Ping(); err != nil {
return fmt.Errorf("failed to ping adminserver: %v", err)
} }
mg = comcfg.NewManager(AdminserverClient, true)
if _, err := mg.Load(); err != nil { if _, err := mg.Load(); err != nil {
return err return err
} }
@ -71,7 +81,7 @@ func VerifyRemoteCert() (bool, error) {
if err != nil { if err != nil {
return true, err return true, err
} }
return cfg[comcfg.VerifyRemoteCert].(bool), nil return cfg[common.VerifyRemoteCert].(bool), nil
} }
// Database ... // Database ...
@ -81,16 +91,16 @@ func Database() (*models.Database, error) {
return nil, err return nil, err
} }
database := &models.Database{} database := &models.Database{}
database.Type = cfg[comcfg.DatabaseType].(string) database.Type = cfg[common.DatabaseType].(string)
mysql := &models.MySQL{} mysql := &models.MySQL{}
mysql.Host = cfg[comcfg.MySQLHost].(string) mysql.Host = cfg[common.MySQLHost].(string)
mysql.Port = int(cfg[comcfg.MySQLPort].(float64)) mysql.Port = int(cfg[common.MySQLPort].(float64))
mysql.Username = cfg[comcfg.MySQLUsername].(string) mysql.Username = cfg[common.MySQLUsername].(string)
mysql.Password = cfg[comcfg.MySQLPassword].(string) mysql.Password = cfg[common.MySQLPassword].(string)
mysql.Database = cfg[comcfg.MySQLDatabase].(string) mysql.Database = cfg[common.MySQLDatabase].(string)
database.MySQL = mysql database.MySQL = mysql
sqlite := &models.SQLite{} sqlite := &models.SQLite{}
sqlite.File = cfg[comcfg.SQLiteFile].(string) sqlite.File = cfg[common.SQLiteFile].(string)
database.SQLite = sqlite database.SQLite = sqlite
return database, nil return database, nil
@ -102,7 +112,7 @@ func MaxJobWorkers() (int, error) {
if err != nil { if err != nil {
return 0, err return 0, err
} }
return int(cfg[comcfg.MaxJobWorkers].(float64)), nil return int(cfg[common.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
@ -116,7 +126,7 @@ func LocalRegURL() (string, error) {
if err != nil { if err != nil {
return "", err return "", err
} }
return cfg[comcfg.RegistryURL].(string), nil return cfg[common.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
@ -151,7 +161,7 @@ func ExtEndpoint() (string, error) {
if err != nil { if err != nil {
return "", err return "", err
} }
return cfg[comcfg.ExtEndpoint].(string), nil return cfg[common.ExtEndpoint].(string), nil
} }
// InternalTokenServiceEndpoint ... // InternalTokenServiceEndpoint ...

View File

@ -20,8 +20,8 @@ import (
"net/http" "net/http"
"strconv" "strconv"
"github.com/vmware/harbor/src/common"
"github.com/vmware/harbor/src/common/api" "github.com/vmware/harbor/src/common/api"
comcfg "github.com/vmware/harbor/src/common/config"
"github.com/vmware/harbor/src/common/dao" "github.com/vmware/harbor/src/common/dao"
"github.com/vmware/harbor/src/common/utils/log" "github.com/vmware/harbor/src/common/utils/log"
"github.com/vmware/harbor/src/ui/config" "github.com/vmware/harbor/src/ui/config"
@ -30,65 +30,65 @@ import (
var ( var (
// valid keys of configurations which user can modify // valid keys of configurations which user can modify
validKeys = []string{ validKeys = []string{
comcfg.ExtEndpoint, common.ExtEndpoint,
comcfg.AUTHMode, common.AUTHMode,
comcfg.DatabaseType, common.DatabaseType,
comcfg.MySQLHost, common.MySQLHost,
comcfg.MySQLPort, common.MySQLPort,
comcfg.MySQLUsername, common.MySQLUsername,
comcfg.MySQLPassword, common.MySQLPassword,
comcfg.MySQLDatabase, common.MySQLDatabase,
comcfg.SQLiteFile, common.SQLiteFile,
comcfg.SelfRegistration, common.SelfRegistration,
comcfg.LDAPURL, common.LDAPURL,
comcfg.LDAPSearchDN, common.LDAPSearchDN,
comcfg.LDAPSearchPwd, common.LDAPSearchPwd,
comcfg.LDAPBaseDN, common.LDAPBaseDN,
comcfg.LDAPUID, common.LDAPUID,
comcfg.LDAPFilter, common.LDAPFilter,
comcfg.LDAPScope, common.LDAPScope,
comcfg.LDAPTimeout, common.LDAPTimeout,
comcfg.TokenServiceURL, common.TokenServiceURL,
comcfg.RegistryURL, common.RegistryURL,
comcfg.EmailHost, common.EmailHost,
comcfg.EmailPort, common.EmailPort,
comcfg.EmailUsername, common.EmailUsername,
comcfg.EmailPassword, common.EmailPassword,
comcfg.EmailFrom, common.EmailFrom,
comcfg.EmailSSL, common.EmailSSL,
comcfg.EmailIdentity, common.EmailIdentity,
comcfg.ProjectCreationRestriction, common.ProjectCreationRestriction,
comcfg.VerifyRemoteCert, common.VerifyRemoteCert,
comcfg.MaxJobWorkers, common.MaxJobWorkers,
comcfg.TokenExpiration, common.TokenExpiration,
comcfg.CfgExpiration, common.CfgExpiration,
comcfg.JobLogDir, common.JobLogDir,
comcfg.UseCompressedJS, common.UseCompressedJS,
comcfg.AdminInitialPassword, common.AdminInitialPassword,
} }
numKeys = []string{ numKeys = []string{
comcfg.EmailPort, common.EmailPort,
comcfg.LDAPScope, common.LDAPScope,
comcfg.LDAPTimeout, common.LDAPTimeout,
comcfg.MySQLPort, common.MySQLPort,
comcfg.MaxJobWorkers, common.MaxJobWorkers,
comcfg.TokenExpiration, common.TokenExpiration,
comcfg.CfgExpiration, common.CfgExpiration,
} }
boolKeys = []string{ boolKeys = []string{
comcfg.EmailSSL, common.EmailSSL,
comcfg.SelfRegistration, common.SelfRegistration,
comcfg.VerifyRemoteCert, common.VerifyRemoteCert,
comcfg.UseCompressedJS, common.UseCompressedJS,
} }
passwordKeys = []string{ passwordKeys = []string{
comcfg.AdminInitialPassword, common.AdminInitialPassword,
comcfg.EmailPassword, common.EmailPassword,
comcfg.LDAPSearchPwd, common.LDAPSearchPwd,
comcfg.MySQLPassword, common.MySQLPassword,
} }
) )
@ -157,7 +157,7 @@ func (c *ConfigAPI) Put() {
c.CustomAbort(http.StatusBadRequest, err.Error()) c.CustomAbort(http.StatusBadRequest, err.Error())
} }
if value, ok := cfg[comcfg.AUTHMode]; ok { if value, ok := cfg[common.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)
@ -174,7 +174,7 @@ func (c *ConfigAPI) Put() {
if !flag { if !flag {
c.CustomAbort(http.StatusBadRequest, c.CustomAbort(http.StatusBadRequest,
fmt.Sprintf("%s can not be modified as new users have been inserted into database", fmt.Sprintf("%s can not be modified as new users have been inserted into database",
comcfg.AUTHMode)) common.AUTHMode))
} }
} }
} }
@ -213,14 +213,14 @@ func validateCfg(c map[string]string) (bool, error) {
return isSysErr, err return isSysErr, err
} }
if value, ok := c[comcfg.AUTHMode]; ok { if value, ok := c[common.AUTHMode]; ok {
if value != comcfg.DBAuth && value != comcfg.LDAPAuth { if value != common.DBAuth && value != common.LDAPAuth {
return isSysErr, fmt.Errorf("invalid %s, shoud be %s or %s", comcfg.AUTHMode, comcfg.DBAuth, comcfg.LDAPAuth) return isSysErr, fmt.Errorf("invalid %s, shoud be %s or %s", common.AUTHMode, common.DBAuth, common.LDAPAuth)
} }
mode = value mode = value
} }
if mode == comcfg.LDAPAuth { if mode == common.LDAPAuth {
ldap, err := config.LDAP() ldap, err := config.LDAP()
if err != nil { if err != nil {
isSysErr = true isSysErr = true
@ -228,46 +228,46 @@ func validateCfg(c map[string]string) (bool, error) {
} }
if len(ldap.URL) == 0 { if len(ldap.URL) == 0 {
if _, ok := c[comcfg.LDAPURL]; !ok { if _, ok := c[common.LDAPURL]; !ok {
return isSysErr, fmt.Errorf("%s is missing", comcfg.LDAPURL) return isSysErr, fmt.Errorf("%s is missing", common.LDAPURL)
} }
} }
if len(ldap.BaseDN) == 0 { if len(ldap.BaseDN) == 0 {
if _, ok := c[comcfg.LDAPBaseDN]; !ok { if _, ok := c[common.LDAPBaseDN]; !ok {
return isSysErr, fmt.Errorf("%s is missing", comcfg.LDAPBaseDN) return isSysErr, fmt.Errorf("%s is missing", common.LDAPBaseDN)
} }
} }
if len(ldap.UID) == 0 { if len(ldap.UID) == 0 {
if _, ok := c[comcfg.LDAPUID]; !ok { if _, ok := c[common.LDAPUID]; !ok {
return isSysErr, fmt.Errorf("%s is missing", comcfg.LDAPUID) return isSysErr, fmt.Errorf("%s is missing", common.LDAPUID)
} }
} }
if ldap.Scope == 0 { if ldap.Scope == 0 {
if _, ok := c[comcfg.LDAPScope]; !ok { if _, ok := c[common.LDAPScope]; !ok {
return isSysErr, fmt.Errorf("%s is missing", comcfg.LDAPScope) return isSysErr, fmt.Errorf("%s is missing", common.LDAPScope)
} }
} }
} }
if ldapURL, ok := c[comcfg.LDAPURL]; ok && len(ldapURL) == 0 { if ldapURL, ok := c[common.LDAPURL]; ok && len(ldapURL) == 0 {
return isSysErr, fmt.Errorf("%s is empty", comcfg.LDAPURL) return isSysErr, fmt.Errorf("%s is empty", common.LDAPURL)
} }
if baseDN, ok := c[comcfg.LDAPBaseDN]; ok && len(baseDN) == 0 { if baseDN, ok := c[common.LDAPBaseDN]; ok && len(baseDN) == 0 {
return isSysErr, fmt.Errorf("%s is empty", comcfg.LDAPBaseDN) return isSysErr, fmt.Errorf("%s is empty", common.LDAPBaseDN)
} }
if uID, ok := c[comcfg.LDAPUID]; ok && len(uID) == 0 { if uID, ok := c[common.LDAPUID]; ok && len(uID) == 0 {
return isSysErr, fmt.Errorf("%s is empty", comcfg.LDAPUID) return isSysErr, fmt.Errorf("%s is empty", common.LDAPUID)
} }
if scope, ok := c[comcfg.LDAPScope]; ok && if scope, ok := c[common.LDAPScope]; ok &&
scope != comcfg.LDAPScopeBase && scope != common.LDAPScopeBase &&
scope != comcfg.LDAPScopeOnelevel && scope != common.LDAPScopeOnelevel &&
scope != comcfg.LDAPScopeSubtree { scope != common.LDAPScopeSubtree {
return isSysErr, fmt.Errorf("invalid %s, should be %s, %s or %s", return isSysErr, fmt.Errorf("invalid %s, should be %s, %s or %s",
comcfg.LDAPScope, common.LDAPScope,
comcfg.LDAPScopeBase, common.LDAPScopeBase,
comcfg.LDAPScopeOnelevel, common.LDAPScopeOnelevel,
comcfg.LDAPScopeSubtree) common.LDAPScopeSubtree)
} }
for _, k := range boolKeys { for _, k := range boolKeys {
@ -293,19 +293,19 @@ func validateCfg(c map[string]string) (bool, error) {
return isSysErr, fmt.Errorf("invalid %s: %s", k, v) return isSysErr, fmt.Errorf("invalid %s: %s", k, v)
} }
if (k == comcfg.EmailPort || if (k == common.EmailPort ||
k == comcfg.MySQLPort) && n > 65535 { k == common.MySQLPort) && n > 65535 {
return isSysErr, fmt.Errorf("invalid %s: %s", k, v) return isSysErr, fmt.Errorf("invalid %s: %s", k, v)
} }
} }
if crt, ok := c[comcfg.ProjectCreationRestriction]; ok && if crt, ok := c[common.ProjectCreationRestriction]; ok &&
crt != comcfg.ProCrtRestrEveryone && crt != common.ProCrtRestrEveryone &&
crt != comcfg.ProCrtRestrAdmOnly { crt != common.ProCrtRestrAdmOnly {
return isSysErr, fmt.Errorf("invalid %s, should be %s or %s", return isSysErr, fmt.Errorf("invalid %s, should be %s or %s",
comcfg.ProjectCreationRestriction, common.ProjectCreationRestriction,
comcfg.ProCrtRestrAdmOnly, common.ProCrtRestrAdmOnly,
comcfg.ProCrtRestrEveryone) common.ProCrtRestrEveryone)
} }
return isSysErr, nil return isSysErr, nil
@ -363,7 +363,7 @@ func convertForGet(cfg map[string]interface{}) (map[string]*value, error) {
if err != nil { if err != nil {
return nil, err return nil, err
} }
result[comcfg.AUTHMode].Editable = flag result[common.AUTHMode].Editable = flag
return result, nil return result, nil
} }

View File

@ -20,7 +20,8 @@ import (
"testing" "testing"
"github.com/stretchr/testify/assert" "github.com/stretchr/testify/assert"
"github.com/vmware/harbor/src/common/config" "github.com/vmware/harbor/src/common"
"github.com/vmware/harbor/src/ui/config"
) )
func TestGetConfig(t *testing.T) { func TestGetConfig(t *testing.T) {
@ -46,8 +47,13 @@ func TestGetConfig(t *testing.T) {
return return
} }
mode := cfg[config.AUTHMode].Value.(string) mode := cfg[common.AUTHMode].Value.(string)
assert.Equal(config.DBAuth, mode, fmt.Sprintf("the auth mode should be %s", config.DBAuth)) assert.Equal(common.DBAuth, mode, fmt.Sprintf("the auth mode should be %s", common.DBAuth))
ccc, err := config.GetSystemCfg()
if err != nil {
t.Logf("failed to get system configurations: %v", err)
}
t.Logf("%v", ccc)
} }
func TestPutConfig(t *testing.T) { func TestPutConfig(t *testing.T) {
@ -56,7 +62,7 @@ func TestPutConfig(t *testing.T) {
apiTest := newHarborAPI() apiTest := newHarborAPI()
cfg := map[string]string{ cfg := map[string]string{
config.VerifyRemoteCert: "0", common.VerifyRemoteCert: "0",
} }
code, err := apiTest.PutConfig(*admin, cfg) code, err := apiTest.PutConfig(*admin, cfg)
@ -67,6 +73,11 @@ func TestPutConfig(t *testing.T) {
if !assert.Equal(200, code, "the status code of modifying configurations with admin user should be 200") { if !assert.Equal(200, code, "the status code of modifying configurations with admin user should be 200") {
return return
} }
ccc, err := config.GetSystemCfg()
if err != nil {
t.Logf("failed to get system configurations: %v", err)
}
t.Logf("%v", ccc)
} }
func TestResetConfig(t *testing.T) { func TestResetConfig(t *testing.T) {
@ -94,11 +105,17 @@ func TestResetConfig(t *testing.T) {
return return
} }
value, ok := cfgs[config.VerifyRemoteCert] value, ok := cfgs[common.VerifyRemoteCert]
if !ok { if !ok {
t.Errorf("%s not found", config.VerifyRemoteCert) t.Errorf("%s not found", common.VerifyRemoteCert)
return return
} }
assert.Equal(value.Value.(bool), true, "unexpected value") assert.Equal(value.Value.(bool), true, "unexpected value")
ccc, err := config.GetSystemCfg()
if err != nil {
t.Logf("failed to get system configurations: %v", err)
}
t.Logf("%v", ccc)
} }

View File

@ -7,8 +7,8 @@ import (
"strings" "strings"
"syscall" "syscall"
"github.com/vmware/harbor/src/common"
"github.com/vmware/harbor/src/common/api" "github.com/vmware/harbor/src/common/api"
comcfg "github.com/vmware/harbor/src/common/config"
"github.com/vmware/harbor/src/common/dao" "github.com/vmware/harbor/src/common/dao"
"github.com/vmware/harbor/src/common/utils/log" "github.com/vmware/harbor/src/common/utils/log"
"github.com/vmware/harbor/src/ui/config" "github.com/vmware/harbor/src/ui/config"
@ -108,18 +108,18 @@ func (sia *SystemInfoAPI) GetGeneralInfo() {
sia.CustomAbort(http.StatusInternalServerError, "Unexpected error") sia.CustomAbort(http.StatusInternalServerError, "Unexpected error")
} }
var registryURL string var registryURL string
if l := strings.Split(cfg[comcfg.ExtEndpoint].(string), "://"); len(l) > 1 { if l := strings.Split(cfg[common.ExtEndpoint].(string), "://"); len(l) > 1 {
registryURL = l[1] registryURL = l[1]
} else { } else {
registryURL = l[0] registryURL = l[0]
} }
info := GeneralInfo{ info := GeneralInfo{
AdmiralEndpoint: cfg[comcfg.AdmiralEndpoint].(string), AdmiralEndpoint: cfg[common.AdmiralEndpoint].(string),
WithAdmiral: config.WithAdmiral(), WithAdmiral: config.WithAdmiral(),
WithNotary: config.WithNotary(), WithNotary: config.WithNotary(),
AuthMode: cfg[comcfg.AUTHMode].(string), AuthMode: cfg[common.AUTHMode].(string),
ProjectCreationRestrict: cfg[comcfg.ProjectCreationRestriction].(string), ProjectCreationRestrict: cfg[common.ProjectCreationRestriction].(string),
SelfRegistration: cfg[comcfg.SelfRegistration].(bool), SelfRegistration: cfg[common.SelfRegistration].(bool),
RegistryURL: registryURL, RegistryURL: registryURL,
} }
sia.Data["json"] = info sia.Data["json"] = info

View File

@ -6,7 +6,7 @@ import (
"os" "os"
"testing" "testing"
"github.com/vmware/harbor/src/common/config" "github.com/vmware/harbor/src/common"
"github.com/vmware/harbor/src/common/dao" "github.com/vmware/harbor/src/common/dao"
"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"
@ -15,24 +15,24 @@ import (
) )
var adminServerLdapTestConfig = map[string]interface{}{ var adminServerLdapTestConfig = map[string]interface{}{
config.ExtEndpoint: "host01.com", common.ExtEndpoint: "host01.com",
config.AUTHMode: "ldap_auth", common.AUTHMode: "ldap_auth",
config.DatabaseType: "mysql", common.DatabaseType: "mysql",
config.MySQLHost: "127.0.0.1", common.MySQLHost: "127.0.0.1",
config.MySQLPort: 3306, common.MySQLPort: 3306,
config.MySQLUsername: "root", common.MySQLUsername: "root",
config.MySQLPassword: "root123", common.MySQLPassword: "root123",
config.MySQLDatabase: "registry", common.MySQLDatabase: "registry",
config.SQLiteFile: "/tmp/registry.db", common.SQLiteFile: "/tmp/registry.db",
//config.SelfRegistration: true, //config.SelfRegistration: true,
config.LDAPURL: "ldap://127.0.0.1", common.LDAPURL: "ldap://127.0.0.1",
config.LDAPSearchDN: "cn=admin,dc=example,dc=com", common.LDAPSearchDN: "cn=admin,dc=example,dc=com",
config.LDAPSearchPwd: "admin", common.LDAPSearchPwd: "admin",
config.LDAPBaseDN: "dc=example,dc=com", common.LDAPBaseDN: "dc=example,dc=com",
config.LDAPUID: "uid", common.LDAPUID: "uid",
config.LDAPFilter: "", common.LDAPFilter: "",
config.LDAPScope: 3, common.LDAPScope: 3,
config.LDAPTimeout: 30, common.LDAPTimeout: 30,
// config.TokenServiceURL: "", // config.TokenServiceURL: "",
// config.RegistryURL: "", // config.RegistryURL: "",
// config.EmailHost: "", // config.EmailHost: "",
@ -46,10 +46,10 @@ var adminServerLdapTestConfig = map[string]interface{}{
// config.VerifyRemoteCert: false, // config.VerifyRemoteCert: false,
// config.MaxJobWorkers: 3, // config.MaxJobWorkers: 3,
// config.TokenExpiration: 30, // config.TokenExpiration: 30,
config.CfgExpiration: 5, common.CfgExpiration: 5,
// config.JobLogDir: "/var/log/jobs", // config.JobLogDir: "/var/log/jobs",
// config.UseCompressedJS: true, // config.UseCompressedJS: true,
config.AdminInitialPassword: "password", common.AdminInitialPassword: "password",
} }
func TestMain(t *testing.T) { func TestMain(t *testing.T) {

View File

@ -16,20 +16,28 @@
package config package config
import ( import (
"encoding/json" "fmt"
"os" "os"
"strings" "strings"
"github.com/vmware/harbor/src/adminserver/client"
"github.com/vmware/harbor/src/adminserver/client/auth"
"github.com/vmware/harbor/src/common"
comcfg "github.com/vmware/harbor/src/common/config" comcfg "github.com/vmware/harbor/src/common/config"
"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"
) )
const defaultKeyPath string = "/etc/ui/key" const (
defaultKeyPath string = "/etc/ui/key"
secretCookieName string = "secret"
)
var ( var (
mg *comcfg.Manager // AdminserverClient is a client for adminserver
keyProvider comcfg.KeyProvider AdminserverClient client.Client
mg *comcfg.Manager
keyProvider comcfg.KeyProvider
) )
// Init configurations // Init configurations
@ -41,14 +49,17 @@ func Init() error {
if len(adminServerURL) == 0 { if len(adminServerURL) == 0 {
adminServerURL = "http://adminserver" adminServerURL = "http://adminserver"
} }
log.Debugf("admin server URL: %s", adminServerURL)
mg = comcfg.NewManager(adminServerURL, UISecret(), true)
if err := mg.Init(); err != nil { log.Infof("initializing client for adminserver %s ...", adminServerURL)
return err authorizer := auth.NewSecretAuthorizer(secretCookieName, UISecret())
AdminserverClient = client.NewClient(adminServerURL, authorizer)
if err := AdminserverClient.Ping(); err != nil {
return fmt.Errorf("failed to ping adminserver: %v", err)
} }
if _, err := mg.Load(); err != nil { mg = comcfg.NewManager(AdminserverClient, true)
if err := Load(); err != nil {
return err return err
} }
@ -78,26 +89,12 @@ func Reset() error {
// Upload uploads all system configutations to admin server // Upload uploads all system configutations to admin server
func Upload(cfg map[string]interface{}) error { func Upload(cfg map[string]interface{}) error {
b, err := json.Marshal(cfg) return mg.Upload(cfg)
if err != nil {
return err
}
return mg.Upload(b)
} }
// GetSystemCfg returns the system configurations // GetSystemCfg returns the system configurations
func GetSystemCfg() (map[string]interface{}, error) { func GetSystemCfg() (map[string]interface{}, error) {
raw, err := mg.Loader.Load() return mg.Load()
if err != nil {
return nil, err
}
c, err := mg.Parser.Parse(raw)
if err != nil {
return nil, err
}
return c, nil
} }
// AuthMode ... // AuthMode ...
@ -106,7 +103,7 @@ func AuthMode() (string, error) {
if err != nil { if err != nil {
return "", err return "", err
} }
return cfg[comcfg.AUTHMode].(string), nil return cfg[common.AUTHMode].(string), nil
} }
// LDAP returns the setting of ldap server // LDAP returns the setting of ldap server
@ -117,14 +114,14 @@ func LDAP() (*models.LDAP, error) {
} }
ldap := &models.LDAP{} ldap := &models.LDAP{}
ldap.URL = cfg[comcfg.LDAPURL].(string) ldap.URL = cfg[common.LDAPURL].(string)
ldap.SearchDN = cfg[comcfg.LDAPSearchDN].(string) ldap.SearchDN = cfg[common.LDAPSearchDN].(string)
ldap.SearchPassword = cfg[comcfg.LDAPSearchPwd].(string) ldap.SearchPassword = cfg[common.LDAPSearchPwd].(string)
ldap.BaseDN = cfg[comcfg.LDAPBaseDN].(string) ldap.BaseDN = cfg[common.LDAPBaseDN].(string)
ldap.UID = cfg[comcfg.LDAPUID].(string) ldap.UID = cfg[common.LDAPUID].(string)
ldap.Filter = cfg[comcfg.LDAPFilter].(string) ldap.Filter = cfg[common.LDAPFilter].(string)
ldap.Scope = int(cfg[comcfg.LDAPScope].(float64)) ldap.Scope = int(cfg[common.LDAPScope].(float64))
ldap.Timeout = int(cfg[comcfg.LDAPTimeout].(float64)) ldap.Timeout = int(cfg[common.LDAPTimeout].(float64))
return ldap, nil return ldap, nil
} }
@ -135,7 +132,7 @@ func TokenExpiration() (int, error) {
if err != nil { if err != nil {
return 0, err return 0, err
} }
return int(cfg[comcfg.TokenExpiration].(float64)), nil return int(cfg[common.TokenExpiration].(float64)), nil
} }
// ExtEndpoint returns the external URL of Harbor: protocol://host:port // ExtEndpoint returns the external URL of Harbor: protocol://host:port
@ -144,7 +141,7 @@ func ExtEndpoint() (string, error) {
if err != nil { if err != nil {
return "", err return "", err
} }
return cfg[comcfg.ExtEndpoint].(string), nil return cfg[common.ExtEndpoint].(string), nil
} }
// ExtURL returns the external URL: host:port // ExtURL returns the external URL: host:port
@ -171,7 +168,7 @@ func SelfRegistration() (bool, error) {
if err != nil { if err != nil {
return false, err return false, err
} }
return cfg[comcfg.SelfRegistration].(bool), nil return cfg[common.SelfRegistration].(bool), nil
} }
// RegistryURL ... // RegistryURL ...
@ -180,7 +177,7 @@ func RegistryURL() (string, error) {
if err != nil { if err != nil {
return "", err return "", err
} }
return cfg[comcfg.RegistryURL].(string), nil return cfg[common.RegistryURL].(string), nil
} }
// InternalJobServiceURL returns jobservice URL for internal communication between Harbor containers // InternalJobServiceURL returns jobservice URL for internal communication between Harbor containers
@ -205,7 +202,7 @@ func InitialAdminPassword() (string, error) {
if err != nil { if err != nil {
return "", err return "", err
} }
return cfg[comcfg.AdminInitialPassword].(string), nil return cfg[common.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
@ -214,7 +211,7 @@ func OnlyAdminCreateProject() (bool, error) {
if err != nil { if err != nil {
return true, err return true, err
} }
return cfg[comcfg.ProjectCreationRestriction].(string) == comcfg.ProCrtRestrAdmOnly, nil return cfg[common.ProjectCreationRestriction].(string) == common.ProCrtRestrAdmOnly, nil
} }
// VerifyRemoteCert returns bool value. // VerifyRemoteCert returns bool value.
@ -223,7 +220,7 @@ func VerifyRemoteCert() (bool, error) {
if err != nil { if err != nil {
return true, err return true, err
} }
return cfg[comcfg.VerifyRemoteCert].(bool), nil return cfg[common.VerifyRemoteCert].(bool), nil
} }
// Email returns email server settings // Email returns email server settings
@ -234,13 +231,13 @@ func Email() (*models.Email, error) {
} }
email := &models.Email{} email := &models.Email{}
email.Host = cfg[comcfg.EmailHost].(string) email.Host = cfg[common.EmailHost].(string)
email.Port = int(cfg[comcfg.EmailPort].(float64)) email.Port = int(cfg[common.EmailPort].(float64))
email.Username = cfg[comcfg.EmailUsername].(string) email.Username = cfg[common.EmailUsername].(string)
email.Password = cfg[comcfg.EmailPassword].(string) email.Password = cfg[common.EmailPassword].(string)
email.SSL = cfg[comcfg.EmailSSL].(bool) email.SSL = cfg[common.EmailSSL].(bool)
email.From = cfg[comcfg.EmailFrom].(string) email.From = cfg[common.EmailFrom].(string)
email.Identity = cfg[comcfg.EmailIdentity].(string) email.Identity = cfg[common.EmailIdentity].(string)
return email, nil return email, nil
} }
@ -252,16 +249,16 @@ func Database() (*models.Database, error) {
return nil, err return nil, err
} }
database := &models.Database{} database := &models.Database{}
database.Type = cfg[comcfg.DatabaseType].(string) database.Type = cfg[common.DatabaseType].(string)
mysql := &models.MySQL{} mysql := &models.MySQL{}
mysql.Host = cfg[comcfg.MySQLHost].(string) mysql.Host = cfg[common.MySQLHost].(string)
mysql.Port = int(cfg[comcfg.MySQLPort].(float64)) mysql.Port = int(cfg[common.MySQLPort].(float64))
mysql.Username = cfg[comcfg.MySQLUsername].(string) mysql.Username = cfg[common.MySQLUsername].(string)
mysql.Password = cfg[comcfg.MySQLPassword].(string) mysql.Password = cfg[common.MySQLPassword].(string)
mysql.Database = cfg[comcfg.MySQLDatabase].(string) mysql.Database = cfg[common.MySQLDatabase].(string)
database.MySQL = mysql database.MySQL = mysql
sqlite := &models.SQLite{} sqlite := &models.SQLite{}
sqlite.File = cfg[comcfg.SQLiteFile].(string) sqlite.File = cfg[common.SQLiteFile].(string)
database.SQLite = sqlite database.SQLite = sqlite
return database, nil return database, nil
@ -286,7 +283,7 @@ func WithNotary() bool {
log.Errorf("Failed to get configuration, will return WithNotary == false") log.Errorf("Failed to get configuration, will return WithNotary == false")
return false return false
} }
return cfg[comcfg.WithNotary].(bool) return cfg[common.WithNotary].(bool)
} }
// AdmiralEndpoint returns the URL of admiral, if Harbor is not deployed with admiral it should return an empty string. // AdmiralEndpoint returns the URL of admiral, if Harbor is not deployed with admiral it should return an empty string.
@ -297,10 +294,10 @@ func AdmiralEndpoint() string {
return "" return ""
} }
if e, ok := cfg[comcfg.AdmiralEndpoint].(string); !ok || e == "NA" { if e, ok := cfg[common.AdmiralEndpoint].(string); !ok || e == "NA" {
cfg[comcfg.AdmiralEndpoint] = "" cfg[common.AdmiralEndpoint] = ""
} }
return cfg[comcfg.AdmiralEndpoint].(string) return cfg[common.AdmiralEndpoint].(string)
} }
// WithAdmiral returns a bool to indicate if Harbor's deployed with admiral. // WithAdmiral returns a bool to indicate if Harbor's deployed with admiral.