mirror of
https://github.com/goharbor/harbor.git
synced 2024-10-02 23:37:38 +02:00
Merge remote-tracking branch 'upstream/master' into 190220_sync
This commit is contained in:
commit
0c0c833dda
2
Makefile
2
Makefile
@ -104,7 +104,7 @@ MIGRATORVERSION=$(VERSIONTAG)
|
|||||||
REDISVERSION=$(VERSIONTAG)
|
REDISVERSION=$(VERSIONTAG)
|
||||||
|
|
||||||
# version of chartmuseum
|
# version of chartmuseum
|
||||||
CHARTMUSEUMVERSION=v0.7.1
|
CHARTMUSEUMVERSION=v0.8.1
|
||||||
|
|
||||||
# docker parameters
|
# docker parameters
|
||||||
DOCKERCMD=$(shell which docker)
|
DOCKERCMD=$(shell which docker)
|
||||||
|
45
docs/permissions.md
Normal file
45
docs/permissions.md
Normal file
@ -0,0 +1,45 @@
|
|||||||
|
# Permissions
|
||||||
|
|
||||||
|
Users have different abilities depending on the role they in a project.
|
||||||
|
|
||||||
|
On public projects all users will be able to see the list of repositories, images, image vulnerabilities, helm charts and helm chart versions, pull images, retag images (need push permission for destination image), download helm charts, download helm chart versions.
|
||||||
|
|
||||||
|
System admin have all permissions for the project.
|
||||||
|
|
||||||
|
## Project members permissions
|
||||||
|
|
||||||
|
The following table depicts the various user permission levels in a project.
|
||||||
|
|
||||||
|
| Action | Guest | Developer | Master | Project Admin |
|
||||||
|
| --------------------------------------- | ----- | --------- | ------ | ------------- |
|
||||||
|
| See the porject configurations | ✓ | ✓ | ✓ | ✓ |
|
||||||
|
| Edit the project configurations | | | | ✓ |
|
||||||
|
| See a list of project members | ✓ | ✓ | ✓ | ✓ |
|
||||||
|
| Create/edit/delete project members | | | | ✓ |
|
||||||
|
| See a list of project logs | ✓ | ✓ | ✓ | ✓ |
|
||||||
|
| See a list of project replications | | | ✓ | ✓ |
|
||||||
|
| See a list of project replication jobs | | | | ✓ |
|
||||||
|
| See a list of project labels | | | ✓ | ✓ |
|
||||||
|
| Create/edit/delete project lables | | | ✓ | ✓ |
|
||||||
|
| See a list of repositories | ✓ | ✓ | ✓ | ✓ |
|
||||||
|
| Create repositories | | ✓ | ✓ | ✓ |
|
||||||
|
| Edit/delete repositories | | | ✓ | ✓ |
|
||||||
|
| See a list of images | ✓ | ✓ | ✓ | ✓ |
|
||||||
|
| Retag image | ✓ | ✓ | ✓ | ✓ |
|
||||||
|
| Pull image | ✓ | ✓ | ✓ | ✓ |
|
||||||
|
| Push image | | ✓ | ✓ | ✓ |
|
||||||
|
| Scan/delete image | | | ✓ | ✓ |
|
||||||
|
| See a list of image vulnerabilities | ✓ | ✓ | ✓ | ✓ |
|
||||||
|
| See image build history | ✓ | ✓ | ✓ | ✓ |
|
||||||
|
| Add/Remove labels of image | | ✓ | ✓ | ✓ |
|
||||||
|
| See a list of helm charts | ✓ | ✓ | ✓ | ✓ |
|
||||||
|
| Download helm charts | ✓ | ✓ | ✓ | ✓ |
|
||||||
|
| Upload helm charts | | ✓ | ✓ | ✓ |
|
||||||
|
| Delete helm charts | | | ✓ | ✓ |
|
||||||
|
| See a list of helm chart versions | ✓ | ✓ | ✓ | ✓ |
|
||||||
|
| Download helm chart versions | ✓ | ✓ | ✓ | ✓ |
|
||||||
|
| Upload helm chart versions | | ✓ | ✓ | ✓ |
|
||||||
|
| Delete helm chart versions | | | ✓ | ✓ |
|
||||||
|
| Add/Remove labels of helm chart version | | ✓ | ✓ | ✓ |
|
||||||
|
| See a list of project robots | | | ✓ | ✓ |
|
||||||
|
| Create/edit/delete project robots | | | | ✓ |
|
@ -2749,24 +2749,6 @@ paths:
|
|||||||
description: User does not have permission of admin role.
|
description: User does not have permission of admin role.
|
||||||
'500':
|
'500':
|
||||||
description: Unexpected internal errors.
|
description: Unexpected internal errors.
|
||||||
/configurations/reset:
|
|
||||||
post:
|
|
||||||
summary: Reset system configurations.
|
|
||||||
description: |
|
|
||||||
Reset system configurations from environment variables. Can only be accessed by admin user.
|
|
||||||
tags:
|
|
||||||
- Products
|
|
||||||
responses:
|
|
||||||
'200':
|
|
||||||
description: Reset system configurations successfully.
|
|
||||||
'401':
|
|
||||||
description: User need to log in first.
|
|
||||||
'403':
|
|
||||||
description: User does not have permission of admin role.
|
|
||||||
'415':
|
|
||||||
$ref: '#/responses/UnsupportedMediaType'
|
|
||||||
'500':
|
|
||||||
description: Unexpected internal errors.
|
|
||||||
/email/ping:
|
/email/ping:
|
||||||
post:
|
post:
|
||||||
summary: Test connection and authentication with email server.
|
summary: Test connection and authentication with email server.
|
||||||
@ -3266,7 +3248,9 @@ paths:
|
|||||||
description: The ID of robot account.
|
description: The ID of robot account.
|
||||||
responses:
|
responses:
|
||||||
'200':
|
'200':
|
||||||
description: '#/definitions/RobotAccount'
|
description: Robot account information.
|
||||||
|
schema:
|
||||||
|
$ref: '#/definitions/RobotAccount'
|
||||||
'401':
|
'401':
|
||||||
description: User need to log in first.
|
description: User need to log in first.
|
||||||
'403':
|
'403':
|
||||||
|
@ -94,37 +94,12 @@ services:
|
|||||||
options:
|
options:
|
||||||
syslog-address: "tcp://127.0.0.1:1514"
|
syslog-address: "tcp://127.0.0.1:1514"
|
||||||
tag: "postgresql"
|
tag: "postgresql"
|
||||||
adminserver:
|
|
||||||
image: goharbor/harbor-adminserver:__version__
|
|
||||||
container_name: harbor-adminserver
|
|
||||||
env_file:
|
|
||||||
- ./common/config/adminserver/env
|
|
||||||
restart: always
|
|
||||||
cap_drop:
|
|
||||||
- ALL
|
|
||||||
cap_add:
|
|
||||||
- CHOWN
|
|
||||||
- SETGID
|
|
||||||
- SETUID
|
|
||||||
volumes:
|
|
||||||
- /data/config/:/etc/adminserver/config/:z
|
|
||||||
- /data/secretkey:/etc/adminserver/key:z
|
|
||||||
- /data/:/data/:z
|
|
||||||
networks:
|
|
||||||
- harbor
|
|
||||||
dns_search: .
|
|
||||||
depends_on:
|
|
||||||
- log
|
|
||||||
logging:
|
|
||||||
driver: "syslog"
|
|
||||||
options:
|
|
||||||
syslog-address: "tcp://127.0.0.1:1514"
|
|
||||||
tag: "adminserver"
|
|
||||||
core:
|
core:
|
||||||
image: goharbor/harbor-core:__version__
|
image: goharbor/harbor-core:__version__
|
||||||
container_name: harbor-core
|
container_name: harbor-core
|
||||||
env_file:
|
env_file:
|
||||||
- ./common/config/core/env
|
- ./common/config/core/env
|
||||||
|
- ./common/config/adminserver/env
|
||||||
restart: always
|
restart: always
|
||||||
cap_drop:
|
cap_drop:
|
||||||
- ALL
|
- ALL
|
||||||
@ -144,7 +119,6 @@ services:
|
|||||||
dns_search: .
|
dns_search: .
|
||||||
depends_on:
|
depends_on:
|
||||||
- log
|
- log
|
||||||
- adminserver
|
|
||||||
- registry
|
- registry
|
||||||
logging:
|
logging:
|
||||||
driver: "syslog"
|
driver: "syslog"
|
||||||
@ -195,7 +169,6 @@ services:
|
|||||||
depends_on:
|
depends_on:
|
||||||
- redis
|
- redis
|
||||||
- core
|
- core
|
||||||
- adminserver
|
|
||||||
logging:
|
logging:
|
||||||
driver: "syslog"
|
driver: "syslog"
|
||||||
options:
|
options:
|
||||||
|
@ -148,7 +148,7 @@ _build_chart_server:
|
|||||||
@if [ "$(CHARTFLAG)" = "true" ] ; then \
|
@if [ "$(CHARTFLAG)" = "true" ] ; then \
|
||||||
if [ "$(BUILDBIN)" != "true" ] ; then \
|
if [ "$(BUILDBIN)" != "true" ] ; then \
|
||||||
rm -rf $(DOCKERFILEPATH_CHART_SERVER)/binary && mkdir -p $(DOCKERFILEPATH_CHART_SERVER)/binary && \
|
rm -rf $(DOCKERFILEPATH_CHART_SERVER)/binary && mkdir -p $(DOCKERFILEPATH_CHART_SERVER)/binary && \
|
||||||
$(call _get_binary, https://storage.googleapis.com/harbor-builds/bin/chartm, $(DOCKERFILEPATH_CHART_SERVER)/binary/chartm); \
|
$(call _get_binary, https://storage.googleapis.com/harbor-builds/bin/chartmuseum/release-$(CHARTMUSEUMVERSION)/chartm, $(DOCKERFILEPATH_CHART_SERVER)/binary/chartm); \
|
||||||
else \
|
else \
|
||||||
cd $(DOCKERFILEPATH_CHART_SERVER) && $(DOCKERFILEPATH_CHART_SERVER)/builder $(GOBUILDIMAGE) $(CHART_SERVER_CODE_BASE) $(CHARTMUSEUMVERSION) $(CHART_SERVER_MAIN_PATH) $(CHART_SERVER_BIN_NAME) && cd - ; \
|
cd $(DOCKERFILEPATH_CHART_SERVER) && $(DOCKERFILEPATH_CHART_SERVER)/builder $(GOBUILDIMAGE) $(CHART_SERVER_CODE_BASE) $(CHARTMUSEUMVERSION) $(CHART_SERVER_MAIN_PATH) $(CHART_SERVER_BIN_NAME) && cd - ; \
|
||||||
fi ; \
|
fi ; \
|
||||||
|
@ -8,6 +8,7 @@ RUN tdnf install sudo -y >> /dev/null\
|
|||||||
HEALTHCHECK CMD curl --fail -s http://127.0.0.1:8080/api/ping || exit 1
|
HEALTHCHECK CMD curl --fail -s http://127.0.0.1:8080/api/ping || exit 1
|
||||||
COPY ./make/photon/core/harbor_core ./make/photon/core/start.sh ./UIVERSION /harbor/
|
COPY ./make/photon/core/harbor_core ./make/photon/core/start.sh ./UIVERSION /harbor/
|
||||||
COPY ./src/core/views /harbor/views
|
COPY ./src/core/views /harbor/views
|
||||||
|
COPY ./make/migrations /harbor/migrations
|
||||||
|
|
||||||
RUN chmod u+x /harbor/start.sh /harbor/harbor_core
|
RUN chmod u+x /harbor/start.sh /harbor/harbor_core
|
||||||
WORKDIR /harbor/
|
WORKDIR /harbor/
|
||||||
|
@ -10,12 +10,15 @@ COPY ./make/photon/log/rsyslog.conf /etc/rsyslog.conf
|
|||||||
# rsyslog configuration file for docker
|
# rsyslog configuration file for docker
|
||||||
COPY ./make/photon/log/rsyslog_docker.conf /etc/rsyslog.d/
|
COPY ./make/photon/log/rsyslog_docker.conf /etc/rsyslog.d/
|
||||||
|
|
||||||
# run logrotate hourly
|
# remove the original "logrotate" in directory "/etc/cron.daily/"
|
||||||
RUN mv /etc/cron.daily/logrotate /etc/cron.hourly/logrotate
|
# and copy the customized one to directory "/etc/cron.hourly/"
|
||||||
|
# to run logrotate hourly
|
||||||
|
RUN rm /etc/cron.daily/logrotate
|
||||||
|
COPY ./make/photon/log/logrotate /etc/cron.hourly/
|
||||||
|
|
||||||
COPY ./make/photon/log/start.sh /usr/local/bin/
|
COPY ./make/photon/log/start.sh /usr/local/bin/
|
||||||
RUN chmod +x /usr/local/bin/start.sh /etc/rsyslog.d/ && \
|
RUN chmod +x /usr/local/bin/start.sh /etc/rsyslog.d/ && \
|
||||||
chown -R 10000:10000 /etc/rsyslog.conf /etc/rsyslog.d/ /run
|
chown -R 10000:10000 /etc/rsyslog.conf /etc/rsyslog.d/ /run /var/lib/logrotate/
|
||||||
|
|
||||||
HEALTHCHECK CMD netstat -ltun|grep 10514
|
HEALTHCHECK CMD netstat -ltun|grep 10514
|
||||||
|
|
||||||
|
6
make/photon/log/logrotate
Executable file
6
make/photon/log/logrotate
Executable file
@ -0,0 +1,6 @@
|
|||||||
|
#!/bin/sh
|
||||||
|
|
||||||
|
# run the logrotate with user 10000, the state file "/var/lib/logrotate/logrotate.status"
|
||||||
|
# is specified to avoid the permission error
|
||||||
|
sudo -u \#10000 -E /usr/sbin/logrotate -s /var/lib/logrotate/logrotate.status /etc/logrotate.conf
|
||||||
|
exit 0
|
@ -16,14 +16,16 @@ package config
|
|||||||
|
|
||||||
import (
|
import (
|
||||||
"fmt"
|
"fmt"
|
||||||
|
"os"
|
||||||
|
|
||||||
"github.com/goharbor/harbor/src/common"
|
"github.com/goharbor/harbor/src/common"
|
||||||
"github.com/goharbor/harbor/src/common/config/metadata"
|
"github.com/goharbor/harbor/src/common/config/metadata"
|
||||||
"github.com/goharbor/harbor/src/common/config/store"
|
"github.com/goharbor/harbor/src/common/config/store"
|
||||||
"github.com/goharbor/harbor/src/common/config/store/driver"
|
"github.com/goharbor/harbor/src/common/config/store/driver"
|
||||||
"github.com/goharbor/harbor/src/common/http/modifier/auth"
|
"github.com/goharbor/harbor/src/common/http/modifier/auth"
|
||||||
"github.com/goharbor/harbor/src/common/models"
|
"github.com/goharbor/harbor/src/common/models"
|
||||||
|
"github.com/goharbor/harbor/src/common/utils"
|
||||||
"github.com/goharbor/harbor/src/common/utils/log"
|
"github.com/goharbor/harbor/src/common/utils/log"
|
||||||
"os"
|
|
||||||
)
|
)
|
||||||
|
|
||||||
// CfgManager ... Configure Manager
|
// CfgManager ... Configure Manager
|
||||||
@ -48,18 +50,20 @@ func NewRESTCfgManager(configURL, secret string) *CfgManager {
|
|||||||
return manager
|
return manager
|
||||||
}
|
}
|
||||||
|
|
||||||
// InmemoryDriver driver for unit testing
|
// InMemoryDriver driver for unit testing
|
||||||
type InmemoryDriver struct {
|
type InMemoryDriver struct {
|
||||||
cfgMap map[string]interface{}
|
cfgMap map[string]interface{}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Load ...
|
// Load load data from driver, for example load from database,
|
||||||
func (d *InmemoryDriver) Load() (map[string]interface{}, error) {
|
// it should be invoked before get any user scope config
|
||||||
|
// for system scope config, because it is immutable, no need to call this method
|
||||||
|
func (d *InMemoryDriver) Load() (map[string]interface{}, error) {
|
||||||
return d.cfgMap, nil
|
return d.cfgMap, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
// Save ...
|
// Save only save user config setting to driver, for example: database, REST
|
||||||
func (d *InmemoryDriver) Save(cfg map[string]interface{}) error {
|
func (d *InMemoryDriver) Save(cfg map[string]interface{}) error {
|
||||||
for k, v := range cfg {
|
for k, v := range cfg {
|
||||||
d.cfgMap[k] = v
|
d.cfgMap[k] = v
|
||||||
}
|
}
|
||||||
@ -68,7 +72,12 @@ func (d *InmemoryDriver) Save(cfg map[string]interface{}) error {
|
|||||||
|
|
||||||
// NewInMemoryManager create a manager for unit testing, doesn't involve database or REST
|
// NewInMemoryManager create a manager for unit testing, doesn't involve database or REST
|
||||||
func NewInMemoryManager() *CfgManager {
|
func NewInMemoryManager() *CfgManager {
|
||||||
return &CfgManager{store: store.NewConfigStore(&InmemoryDriver{cfgMap: map[string]interface{}{}})}
|
manager := &CfgManager{store: store.NewConfigStore(&InMemoryDriver{cfgMap: map[string]interface{}{}})}
|
||||||
|
// load default value
|
||||||
|
manager.loadDefault()
|
||||||
|
// load system config from env
|
||||||
|
manager.loadSystemConfigFromEnv()
|
||||||
|
return manager
|
||||||
}
|
}
|
||||||
|
|
||||||
// loadDefault ...
|
// loadDefault ...
|
||||||
@ -106,23 +115,47 @@ func (c *CfgManager) loadSystemConfigFromEnv() {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// GetAll ... Get all settings
|
// GetAll get all settings.
|
||||||
func (c *CfgManager) GetAll() []metadata.ConfigureValue {
|
func (c *CfgManager) GetAll() map[string]interface{} {
|
||||||
results := make([]metadata.ConfigureValue, 0)
|
resultMap := map[string]interface{}{}
|
||||||
if err := c.store.Load(); err != nil {
|
if err := c.store.Load(); err != nil {
|
||||||
log.Errorf("GetAll failed, error %v", err)
|
log.Errorf("GetAll failed, error %v", err)
|
||||||
return results
|
return resultMap
|
||||||
}
|
}
|
||||||
metaDataList := metadata.Instance().GetAll()
|
metaDataList := metadata.Instance().GetAll()
|
||||||
for _, item := range metaDataList {
|
for _, item := range metaDataList {
|
||||||
if cfgValue, err := c.store.Get(item.Name); err == nil {
|
cfgValue, err := c.store.GetAnyType(item.Name)
|
||||||
results = append(results, *cfgValue)
|
if err != metadata.ErrValueNotSet && err != nil {
|
||||||
|
log.Errorf("Failed to get value of key %v, error %v", item.Name, err)
|
||||||
|
continue
|
||||||
}
|
}
|
||||||
|
resultMap[item.Name] = cfgValue
|
||||||
}
|
}
|
||||||
return results
|
return resultMap
|
||||||
}
|
}
|
||||||
|
|
||||||
// Load - Load configuration from storage, like database or redis
|
// GetUserCfgs retrieve all user configs
|
||||||
|
func (c *CfgManager) GetUserCfgs() map[string]interface{} {
|
||||||
|
resultMap := map[string]interface{}{}
|
||||||
|
if err := c.store.Load(); err != nil {
|
||||||
|
log.Errorf("GetUserCfgs failed, error %v", err)
|
||||||
|
return resultMap
|
||||||
|
}
|
||||||
|
metaDataList := metadata.Instance().GetAll()
|
||||||
|
for _, item := range metaDataList {
|
||||||
|
if item.Scope == metadata.UserScope {
|
||||||
|
cfgValue, err := c.store.GetAnyType(item.Name)
|
||||||
|
if err != metadata.ErrValueNotSet && err != nil {
|
||||||
|
log.Errorf("Failed to get value of key %v, error %v", item.Name, err)
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
resultMap[item.Name] = cfgValue
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return resultMap
|
||||||
|
}
|
||||||
|
|
||||||
|
// Load load configuration from storage, like database or redis
|
||||||
func (c *CfgManager) Load() error {
|
func (c *CfgManager) Load() error {
|
||||||
return c.store.Load()
|
return c.store.Load()
|
||||||
}
|
}
|
||||||
@ -144,7 +177,7 @@ func (c *CfgManager) Get(key string) *metadata.ConfigureValue {
|
|||||||
|
|
||||||
// Set ...
|
// Set ...
|
||||||
func (c *CfgManager) Set(key string, value interface{}) {
|
func (c *CfgManager) Set(key string, value interface{}) {
|
||||||
configValue, err := metadata.NewCfgValue(key, fmt.Sprintf("%v", value))
|
configValue, err := metadata.NewCfgValue(key, utils.GetStrValueOfAnyType(value))
|
||||||
if err != nil {
|
if err != nil {
|
||||||
log.Errorf("error when setting key: %v, error %v", key, err)
|
log.Errorf("error when setting key: %v, error %v", key, err)
|
||||||
return
|
return
|
||||||
@ -153,14 +186,6 @@ func (c *CfgManager) Set(key string, value interface{}) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// GetDatabaseCfg - Get database configurations
|
// GetDatabaseCfg - Get database configurations
|
||||||
/*
|
|
||||||
In database related testing, call it in the TestMain to initialize database schema and set testing configures
|
|
||||||
|
|
||||||
cfgMgr := config.NewDBCfgManager()
|
|
||||||
dao.InitDatabase(cfgMgr.GetDatabaseCfg())
|
|
||||||
cfgMgr.Load()
|
|
||||||
cfgMrg.UpdateConfig(testingConfigs)
|
|
||||||
*/
|
|
||||||
func (c *CfgManager) GetDatabaseCfg() *models.Database {
|
func (c *CfgManager) GetDatabaseCfg() *models.Database {
|
||||||
return &models.Database{
|
return &models.Database{
|
||||||
Type: c.Get(common.DatabaseType).GetString(),
|
Type: c.Get(common.DatabaseType).GetString(),
|
||||||
@ -175,7 +200,27 @@ func (c *CfgManager) GetDatabaseCfg() *models.Database {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// UpdateConfig - Update config store with a specified configuration and also save updated configure
|
// UpdateConfig - Update config store with a specified configuration and also save updated configure.
|
||||||
func (c *CfgManager) UpdateConfig(cfgs map[string]interface{}) error {
|
func (c *CfgManager) UpdateConfig(cfgs map[string]interface{}) error {
|
||||||
return c.store.Update(cfgs)
|
return c.store.Update(cfgs)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// ValidateCfg validate config by metadata. return the first error if exist.
|
||||||
|
func (c *CfgManager) ValidateCfg(cfgs map[string]interface{}) error {
|
||||||
|
for key, value := range cfgs {
|
||||||
|
strVal := utils.GetStrValueOfAnyType(value)
|
||||||
|
_, err := metadata.NewCfgValue(key, strVal)
|
||||||
|
if err != nil {
|
||||||
|
return fmt.Errorf("%v, item name: %v", err, key)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// DumpTrace dump all configurations
|
||||||
|
func (c *CfgManager) DumpTrace() {
|
||||||
|
cfgs := c.GetAll()
|
||||||
|
for k, v := range cfgs {
|
||||||
|
log.Info(k, ":=", v)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
@ -2,7 +2,7 @@ package config
|
|||||||
|
|
||||||
import (
|
import (
|
||||||
"fmt"
|
"fmt"
|
||||||
"github.com/goharbor/harbor/src/common/dao"
|
"github.com/goharbor/harbor/src/common/utils/test"
|
||||||
"github.com/stretchr/testify/assert"
|
"github.com/stretchr/testify/assert"
|
||||||
"os"
|
"os"
|
||||||
"testing"
|
"testing"
|
||||||
@ -16,32 +16,30 @@ var TestDBConfig = map[string]interface{}{
|
|||||||
"postgresql_sslmode": "disable",
|
"postgresql_sslmode": "disable",
|
||||||
"email_host": "127.0.0.1",
|
"email_host": "127.0.0.1",
|
||||||
"clair_url": "http://clair:6060",
|
"clair_url": "http://clair:6060",
|
||||||
|
"scan_all_policy": `{"parameter":{"daily_time":0},"type":"daily"}`,
|
||||||
}
|
}
|
||||||
|
|
||||||
var configManager *CfgManager
|
var configManager *CfgManager
|
||||||
|
|
||||||
func TestMain(m *testing.M) {
|
func TestMain(m *testing.M) {
|
||||||
configManager = NewDBCfgManager()
|
configManager = NewDBCfgManager()
|
||||||
dao.InitDatabase(configManager.GetDatabaseCfg())
|
test.InitDatabaseFromEnv()
|
||||||
configManager.UpdateConfig(TestDBConfig)
|
configManager.UpdateConfig(TestDBConfig)
|
||||||
os.Exit(m.Run())
|
os.Exit(m.Run())
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestLoadFromDatabase(t *testing.T) {
|
func TestLoadFromDatabase(t *testing.T) {
|
||||||
|
|
||||||
dao.InitDatabase(configManager.GetDatabaseCfg())
|
|
||||||
configManager.Load()
|
|
||||||
configManager.UpdateConfig(TestDBConfig)
|
configManager.UpdateConfig(TestDBConfig)
|
||||||
|
configManager.Load()
|
||||||
assert.Equal(t, "127.0.0.1", configManager.Get("email_host").GetString())
|
assert.Equal(t, "127.0.0.1", configManager.Get("email_host").GetString())
|
||||||
assert.Equal(t, "http://clair:6060", configManager.Get("clair_url").GetString())
|
assert.Equal(t, "http://clair:6060", configManager.Get("clair_url").GetString())
|
||||||
|
assert.Equal(t, `{"parameter":{"daily_time":0},"type":"daily"}`, configManager.Get("scan_all_policy").GetString())
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestSaveToDatabase(t *testing.T) {
|
func TestSaveToDatabase(t *testing.T) {
|
||||||
dao.InitDatabase(configManager.GetDatabaseCfg())
|
|
||||||
fmt.Printf("database config %#v\n", configManager.GetDatabaseCfg())
|
fmt.Printf("database config %#v\n", configManager.GetDatabaseCfg())
|
||||||
configManager.Load()
|
configManager.Load()
|
||||||
configManager.Set("read_only", "true")
|
configManager.Set("read_only", "true")
|
||||||
configManager.UpdateConfig(TestDBConfig)
|
|
||||||
configManager.Save()
|
configManager.Save()
|
||||||
configManager.Load()
|
configManager.Load()
|
||||||
assert.Equal(t, true, configManager.Get("read_only").GetBool())
|
assert.Equal(t, true, configManager.Get("read_only").GetBool())
|
||||||
@ -55,7 +53,6 @@ func TestUpdateCfg(t *testing.T) {
|
|||||||
"ldap_search_password": "admin",
|
"ldap_search_password": "admin",
|
||||||
"ldap_base_dn": "dc=example,dc=com",
|
"ldap_base_dn": "dc=example,dc=com",
|
||||||
}
|
}
|
||||||
dao.InitDatabase(configManager.GetDatabaseCfg())
|
|
||||||
configManager.Load()
|
configManager.Load()
|
||||||
configManager.UpdateConfig(testConfig)
|
configManager.UpdateConfig(testConfig)
|
||||||
|
|
||||||
@ -111,3 +108,16 @@ func TestNewInMemoryManager(t *testing.T) {
|
|||||||
assert.Equal(t, 5, inMemoryManager.Get("ldap_timeout").GetInt())
|
assert.Equal(t, 5, inMemoryManager.Get("ldap_timeout").GetInt())
|
||||||
assert.Equal(t, true, inMemoryManager.Get("ldap_verify_cert").GetBool())
|
assert.Equal(t, true, inMemoryManager.Get("ldap_verify_cert").GetBool())
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
func TestNewRESTCfgManager(t *testing.T) {
|
||||||
|
restMgr := NewRESTCfgManager("http://10.161.47.13:8080"+common.CoreConfigPath, "0XtgSGFx1amMDTaH")
|
||||||
|
err := restMgr.Load()
|
||||||
|
if err != nil {
|
||||||
|
t.Errorf("Failed with error %v", err)
|
||||||
|
}
|
||||||
|
fmt.Printf("db:%v", restMgr.GetDatabaseCfg().Type)
|
||||||
|
fmt.Printf("host:%#v\n", restMgr.GetDatabaseCfg().PostGreSQL.Host)
|
||||||
|
fmt.Printf("port:%#v\n", restMgr.GetDatabaseCfg().PostGreSQL.Port)
|
||||||
|
|
||||||
|
}*/
|
||||||
|
@ -59,15 +59,15 @@ var (
|
|||||||
// 3. CfgManager.Load()/CfgManager.Save() to load/save from configure storage.
|
// 3. CfgManager.Load()/CfgManager.Save() to load/save from configure storage.
|
||||||
ConfigList = []Item{
|
ConfigList = []Item{
|
||||||
{Name: "admin_initial_password", Scope: SystemScope, Group: BasicGroup, EnvKey: "HARBOR_ADMIN_PASSWORD", DefaultValue: "", ItemType: &PasswordType{}, Editable: true},
|
{Name: "admin_initial_password", Scope: SystemScope, Group: BasicGroup, EnvKey: "HARBOR_ADMIN_PASSWORD", DefaultValue: "", ItemType: &PasswordType{}, Editable: true},
|
||||||
{Name: "admiral_url", Scope: SystemScope, Group: BasicGroup, EnvKey: "ADMIRAL_URL", DefaultValue: "NA", ItemType: &StringType{}, Editable: false},
|
{Name: "admiral_url", Scope: SystemScope, Group: BasicGroup, EnvKey: "ADMIRAL_URL", DefaultValue: "", ItemType: &StringType{}, Editable: false},
|
||||||
{Name: "auth_mode", Scope: UserScope, Group: BasicGroup, EnvKey: "AUTH_MODE", DefaultValue: "db_auth", ItemType: &StringType{}, Editable: false},
|
{Name: "auth_mode", Scope: UserScope, Group: BasicGroup, EnvKey: "AUTH_MODE", DefaultValue: "db_auth", ItemType: &AuthModeType{}, Editable: false},
|
||||||
{Name: "cfg_expiration", Scope: SystemScope, Group: BasicGroup, EnvKey: "CFG_EXPIRATION", DefaultValue: "5", ItemType: &IntType{}, Editable: false},
|
{Name: "cfg_expiration", Scope: SystemScope, Group: BasicGroup, EnvKey: "CFG_EXPIRATION", DefaultValue: "5", ItemType: &IntType{}, Editable: false},
|
||||||
{Name: "chart_repository_url", Scope: SystemScope, Group: BasicGroup, EnvKey: "CHART_REPOSITORY_URL", DefaultValue: "http://chartmuseum:9999", ItemType: &StringType{}, Editable: false},
|
{Name: "chart_repository_url", Scope: SystemScope, Group: BasicGroup, EnvKey: "CHART_REPOSITORY_URL", DefaultValue: "http://chartmuseum:9999", ItemType: &StringType{}, Editable: false},
|
||||||
|
|
||||||
{Name: "clair_db", Scope: SystemScope, Group: ClairGroup, EnvKey: "CLAIR_DB", DefaultValue: "postgres", ItemType: &StringType{}, Editable: false},
|
{Name: "clair_db", Scope: SystemScope, Group: ClairGroup, EnvKey: "CLAIR_DB", DefaultValue: "postgres", ItemType: &StringType{}, Editable: false},
|
||||||
{Name: "clair_db_host", Scope: SystemScope, Group: ClairGroup, EnvKey: "CLAIR_DB_HOST", DefaultValue: "postgresql", ItemType: &StringType{}, Editable: false},
|
{Name: "clair_db_host", Scope: SystemScope, Group: ClairGroup, EnvKey: "CLAIR_DB_HOST", DefaultValue: "postgresql", ItemType: &StringType{}, Editable: false},
|
||||||
{Name: "clair_db_password", Scope: SystemScope, Group: ClairGroup, EnvKey: "CLAIR_DB_PASSWORD", DefaultValue: "root123", ItemType: &PasswordType{}, Editable: false},
|
{Name: "clair_db_password", Scope: SystemScope, Group: ClairGroup, EnvKey: "CLAIR_DB_PASSWORD", DefaultValue: "root123", ItemType: &PasswordType{}, Editable: false},
|
||||||
{Name: "clair_db_port", Scope: SystemScope, Group: ClairGroup, EnvKey: "CLAIR_DB_PORT", DefaultValue: "5432", ItemType: &IntType{}, Editable: false},
|
{Name: "clair_db_port", Scope: SystemScope, Group: ClairGroup, EnvKey: "CLAIR_DB_PORT", DefaultValue: "5432", ItemType: &PortType{}, Editable: false},
|
||||||
{Name: "clair_db_sslmode", Scope: SystemScope, Group: ClairGroup, EnvKey: "CLAIR_DB_SSLMODE", DefaultValue: "disable", ItemType: &StringType{}, Editable: false},
|
{Name: "clair_db_sslmode", Scope: SystemScope, Group: ClairGroup, EnvKey: "CLAIR_DB_SSLMODE", DefaultValue: "disable", ItemType: &StringType{}, Editable: false},
|
||||||
{Name: "clair_db_username", Scope: SystemScope, Group: ClairGroup, EnvKey: "CLAIR_DB_USERNAME", DefaultValue: "postgres", ItemType: &StringType{}, Editable: false},
|
{Name: "clair_db_username", Scope: SystemScope, Group: ClairGroup, EnvKey: "CLAIR_DB_USERNAME", DefaultValue: "postgres", ItemType: &StringType{}, Editable: false},
|
||||||
{Name: "clair_url", Scope: SystemScope, Group: ClairGroup, EnvKey: "CLAIR_URL", DefaultValue: "http://clair:6060", ItemType: &StringType{}, Editable: false},
|
{Name: "clair_url", Scope: SystemScope, Group: ClairGroup, EnvKey: "CLAIR_URL", DefaultValue: "http://clair:6060", ItemType: &StringType{}, Editable: false},
|
||||||
@ -80,39 +80,40 @@ var (
|
|||||||
{Name: "email_identity", Scope: UserScope, Group: EmailGroup, EnvKey: "EMAIL_IDENTITY", DefaultValue: "", ItemType: &StringType{}, Editable: false},
|
{Name: "email_identity", Scope: UserScope, Group: EmailGroup, EnvKey: "EMAIL_IDENTITY", DefaultValue: "", ItemType: &StringType{}, Editable: false},
|
||||||
{Name: "email_insecure", Scope: UserScope, Group: EmailGroup, EnvKey: "EMAIL_INSECURE", DefaultValue: "false", ItemType: &BoolType{}, Editable: false},
|
{Name: "email_insecure", Scope: UserScope, Group: EmailGroup, EnvKey: "EMAIL_INSECURE", DefaultValue: "false", ItemType: &BoolType{}, Editable: false},
|
||||||
{Name: "email_password", Scope: UserScope, Group: EmailGroup, EnvKey: "EMAIL_PWD", DefaultValue: "", ItemType: &PasswordType{}, Editable: false},
|
{Name: "email_password", Scope: UserScope, Group: EmailGroup, EnvKey: "EMAIL_PWD", DefaultValue: "", ItemType: &PasswordType{}, Editable: false},
|
||||||
{Name: "email_port", Scope: UserScope, Group: EmailGroup, EnvKey: "EMAIL_PORT", DefaultValue: "25", ItemType: &IntType{}, Editable: false},
|
{Name: "email_port", Scope: UserScope, Group: EmailGroup, EnvKey: "EMAIL_PORT", DefaultValue: "25", ItemType: &PortType{}, Editable: false},
|
||||||
{Name: "email_ssl", Scope: UserScope, Group: EmailGroup, EnvKey: "EMAIL_SSL", DefaultValue: "false", ItemType: &BoolType{}, Editable: false},
|
{Name: "email_ssl", Scope: UserScope, Group: EmailGroup, EnvKey: "EMAIL_SSL", DefaultValue: "false", ItemType: &BoolType{}, Editable: false},
|
||||||
{Name: "email_username", Scope: UserScope, Group: EmailGroup, EnvKey: "EMAIL_USR", DefaultValue: "sample_admin@mydomain.com", ItemType: &StringType{}, Editable: false},
|
{Name: "email_username", Scope: UserScope, Group: EmailGroup, EnvKey: "EMAIL_USR", DefaultValue: "sample_admin@mydomain.com", ItemType: &StringType{}, Editable: false},
|
||||||
|
|
||||||
{Name: "ext_endpoint", Scope: SystemScope, Group: BasicGroup, EnvKey: "EXT_ENDPOINT", DefaultValue: "https://host01.com", ItemType: &StringType{}, Editable: false},
|
{Name: "ext_endpoint", Scope: SystemScope, Group: BasicGroup, EnvKey: "EXT_ENDPOINT", DefaultValue: "https://host01.com", ItemType: &StringType{}, Editable: false},
|
||||||
{Name: "jobservice_url", Scope: SystemScope, Group: BasicGroup, EnvKey: "JOBSERVICE_URL", DefaultValue: "http://jobservice:8080", ItemType: &StringType{}, Editable: false},
|
{Name: "jobservice_url", Scope: SystemScope, Group: BasicGroup, EnvKey: "JOBSERVICE_URL", DefaultValue: "http://jobservice:8080", ItemType: &StringType{}, Editable: false},
|
||||||
|
|
||||||
{Name: "ldap_base_dn", Scope: UserScope, Group: LdapBasicGroup, EnvKey: "LDAP_BASE_DN", DefaultValue: "", ItemType: &StringType{}, Editable: false},
|
{Name: "ldap_base_dn", Scope: UserScope, Group: LdapBasicGroup, EnvKey: "LDAP_BASE_DN", DefaultValue: "", ItemType: &NonEmptyStringType{}, Editable: false},
|
||||||
{Name: "ldap_filter", Scope: UserScope, Group: LdapBasicGroup, EnvKey: "LDAP_FILTER", DefaultValue: "", ItemType: &StringType{}, Editable: false},
|
{Name: "ldap_filter", Scope: UserScope, Group: LdapBasicGroup, EnvKey: "LDAP_FILTER", DefaultValue: "", ItemType: &StringType{}, Editable: false},
|
||||||
{Name: "ldap_group_base_dn", Scope: UserScope, Group: LdapGroupGroup, EnvKey: "LDAP_GROUP_BASE_DN", DefaultValue: "", ItemType: &StringType{}, Editable: false},
|
{Name: "ldap_group_base_dn", Scope: UserScope, Group: LdapGroupGroup, EnvKey: "LDAP_GROUP_BASE_DN", DefaultValue: "", ItemType: &StringType{}, Editable: false},
|
||||||
{Name: "ldap_group_admin_dn", Scope: UserScope, Group: LdapGroupGroup, EnvKey: "LDAP_GROUP_ADMIN_DN", DefaultValue: "", ItemType: &StringType{}, Editable: false},
|
{Name: "ldap_group_admin_dn", Scope: UserScope, Group: LdapGroupGroup, EnvKey: "LDAP_GROUP_ADMIN_DN", DefaultValue: "", ItemType: &StringType{}, Editable: false},
|
||||||
{Name: "ldap_group_attribute_name", Scope: UserScope, Group: LdapGroupGroup, EnvKey: "LDAP_GROUP_GID", DefaultValue: "", ItemType: &StringType{}, Editable: false},
|
{Name: "ldap_group_attribute_name", Scope: UserScope, Group: LdapGroupGroup, EnvKey: "LDAP_GROUP_GID", DefaultValue: "", ItemType: &StringType{}, Editable: false},
|
||||||
{Name: "ldap_group_search_filter", Scope: UserScope, Group: LdapGroupGroup, EnvKey: "LDAP_GROUP_FILTER", DefaultValue: "", ItemType: &StringType{}, Editable: false},
|
{Name: "ldap_group_search_filter", Scope: UserScope, Group: LdapGroupGroup, EnvKey: "LDAP_GROUP_FILTER", DefaultValue: "", ItemType: &StringType{}, Editable: false},
|
||||||
{Name: "ldap_group_search_scope", Scope: UserScope, Group: LdapGroupGroup, EnvKey: "LDAP_GROUP_SCOPE", DefaultValue: "2", ItemType: &IntType{}, Editable: false},
|
{Name: "ldap_group_search_scope", Scope: UserScope, Group: LdapGroupGroup, EnvKey: "LDAP_GROUP_SCOPE", DefaultValue: "2", ItemType: &LdapScopeType{}, Editable: false},
|
||||||
{Name: "ldap_scope", Scope: UserScope, Group: LdapBasicGroup, EnvKey: "LDAP_SCOPE", DefaultValue: "2", ItemType: &IntType{}, Editable: true},
|
{Name: "ldap_scope", Scope: UserScope, Group: LdapBasicGroup, EnvKey: "LDAP_SCOPE", DefaultValue: "2", ItemType: &LdapScopeType{}, Editable: false},
|
||||||
{Name: "ldap_search_dn", Scope: UserScope, Group: LdapBasicGroup, EnvKey: "LDAP_SEARCH_DN", DefaultValue: "", ItemType: &StringType{}, Editable: false},
|
{Name: "ldap_search_dn", Scope: UserScope, Group: LdapBasicGroup, EnvKey: "LDAP_SEARCH_DN", DefaultValue: "", ItemType: &StringType{}, Editable: false},
|
||||||
{Name: "ldap_search_password", Scope: UserScope, Group: LdapBasicGroup, EnvKey: "LDAP_SEARCH_PWD", DefaultValue: "", ItemType: &PasswordType{}, Editable: false},
|
{Name: "ldap_search_password", Scope: UserScope, Group: LdapBasicGroup, EnvKey: "LDAP_SEARCH_PWD", DefaultValue: "", ItemType: &PasswordType{}, Editable: false},
|
||||||
{Name: "ldap_timeout", Scope: UserScope, Group: LdapBasicGroup, EnvKey: "LDAP_TIMEOUT", DefaultValue: "5", ItemType: &IntType{}, Editable: false},
|
{Name: "ldap_timeout", Scope: UserScope, Group: LdapBasicGroup, EnvKey: "LDAP_TIMEOUT", DefaultValue: "5", ItemType: &IntType{}, Editable: false},
|
||||||
{Name: "ldap_uid", Scope: UserScope, Group: LdapBasicGroup, EnvKey: "LDAP_UID", DefaultValue: "cn", ItemType: &StringType{}, Editable: true},
|
{Name: "ldap_uid", Scope: UserScope, Group: LdapBasicGroup, EnvKey: "LDAP_UID", DefaultValue: "cn", ItemType: &NonEmptyStringType{}, Editable: false},
|
||||||
{Name: "ldap_url", Scope: UserScope, Group: LdapBasicGroup, EnvKey: "LDAP_URL", DefaultValue: "", ItemType: &StringType{}, Editable: true},
|
{Name: "ldap_url", Scope: UserScope, Group: LdapBasicGroup, EnvKey: "LDAP_URL", DefaultValue: "", ItemType: &NonEmptyStringType{}, Editable: false},
|
||||||
{Name: "ldap_verify_cert", Scope: UserScope, Group: LdapBasicGroup, EnvKey: "LDAP_VERIFY_CERT", DefaultValue: "true", ItemType: &BoolType{}, Editable: false},
|
{Name: "ldap_verify_cert", Scope: UserScope, Group: LdapBasicGroup, EnvKey: "LDAP_VERIFY_CERT", DefaultValue: "true", ItemType: &BoolType{}, Editable: false},
|
||||||
|
|
||||||
{Name: "max_job_workers", Scope: SystemScope, Group: BasicGroup, EnvKey: "MAX_JOB_WORKERS", DefaultValue: "10", ItemType: &IntType{}, Editable: false},
|
{Name: "max_job_workers", Scope: SystemScope, Group: BasicGroup, EnvKey: "MAX_JOB_WORKERS", DefaultValue: "10", ItemType: &IntType{}, Editable: false},
|
||||||
{Name: "notary_url", Scope: SystemScope, Group: BasicGroup, EnvKey: "NOTARY_URL", DefaultValue: "http://notary-server:4443", ItemType: &StringType{}, Editable: false},
|
{Name: "notary_url", Scope: SystemScope, Group: BasicGroup, EnvKey: "NOTARY_URL", DefaultValue: "http://notary-server:4443", ItemType: &StringType{}, Editable: false},
|
||||||
|
{Name: "scan_all_policy", Scope: UserScope, Group: BasicGroup, EnvKey: "", DefaultValue: "", ItemType: &MapType{}, Editable: false},
|
||||||
|
|
||||||
{Name: "postgresql_database", Scope: SystemScope, Group: DatabaseGroup, EnvKey: "POSTGRESQL_DATABASE", DefaultValue: "registry", ItemType: &StringType{}, Editable: false},
|
{Name: "postgresql_database", Scope: SystemScope, Group: DatabaseGroup, EnvKey: "POSTGRESQL_DATABASE", DefaultValue: "registry", ItemType: &StringType{}, Editable: false},
|
||||||
{Name: "postgresql_host", Scope: SystemScope, Group: DatabaseGroup, EnvKey: "POSTGRESQL_HOST", DefaultValue: "postgresql", ItemType: &StringType{}, Editable: false},
|
{Name: "postgresql_host", Scope: SystemScope, Group: DatabaseGroup, EnvKey: "POSTGRESQL_HOST", DefaultValue: "postgresql", ItemType: &StringType{}, Editable: false},
|
||||||
{Name: "postgresql_password", Scope: SystemScope, Group: DatabaseGroup, EnvKey: "POSTGRESQL_PASSWORD", DefaultValue: "root123", ItemType: &PasswordType{}, Editable: false},
|
{Name: "postgresql_password", Scope: SystemScope, Group: DatabaseGroup, EnvKey: "POSTGRESQL_PASSWORD", DefaultValue: "root123", ItemType: &PasswordType{}, Editable: false},
|
||||||
{Name: "postgresql_port", Scope: SystemScope, Group: DatabaseGroup, EnvKey: "POSTGRESQL_PORT", DefaultValue: "5432", ItemType: &IntType{}, Editable: false},
|
{Name: "postgresql_port", Scope: SystemScope, Group: DatabaseGroup, EnvKey: "POSTGRESQL_PORT", DefaultValue: "5432", ItemType: &PortType{}, Editable: false},
|
||||||
{Name: "postgresql_sslmode", Scope: SystemScope, Group: DatabaseGroup, EnvKey: "POSTGRESQL_SSLMODE", DefaultValue: "disable", ItemType: &StringType{}, Editable: false},
|
{Name: "postgresql_sslmode", Scope: SystemScope, Group: DatabaseGroup, EnvKey: "POSTGRESQL_SSLMODE", DefaultValue: "disable", ItemType: &StringType{}, Editable: false},
|
||||||
{Name: "postgresql_username", Scope: SystemScope, Group: DatabaseGroup, EnvKey: "POSTGRESQL_USERNAME", DefaultValue: "postgres", ItemType: &StringType{}, Editable: false},
|
{Name: "postgresql_username", Scope: SystemScope, Group: DatabaseGroup, EnvKey: "POSTGRESQL_USERNAME", DefaultValue: "postgres", ItemType: &StringType{}, Editable: false},
|
||||||
|
|
||||||
{Name: "project_creation_restriction", Scope: UserScope, Group: BasicGroup, EnvKey: "PROJECT_CREATION_RESTRICTION", DefaultValue: common.ProCrtRestrEveryone, ItemType: &StringType{}, Editable: false},
|
{Name: "project_creation_restriction", Scope: UserScope, Group: BasicGroup, EnvKey: "PROJECT_CREATION_RESTRICTION", DefaultValue: common.ProCrtRestrEveryone, ItemType: &ProjectCreationRestrictionType{}, Editable: false},
|
||||||
{Name: "read_only", Scope: UserScope, Group: BasicGroup, EnvKey: "READ_ONLY", DefaultValue: "false", ItemType: &BoolType{}, Editable: false},
|
{Name: "read_only", Scope: UserScope, Group: BasicGroup, EnvKey: "READ_ONLY", DefaultValue: "false", ItemType: &BoolType{}, Editable: false},
|
||||||
|
|
||||||
{Name: "registry_storage_provider_name", Scope: SystemScope, Group: BasicGroup, EnvKey: "REGISTRY_STORAGE_PROVIDER_NAME", DefaultValue: "filesystem", ItemType: &StringType{}, Editable: false},
|
{Name: "registry_storage_provider_name", Scope: SystemScope, Group: BasicGroup, EnvKey: "REGISTRY_STORAGE_PROVIDER_NAME", DefaultValue: "filesystem", ItemType: &StringType{}, Editable: false},
|
||||||
@ -120,7 +121,7 @@ var (
|
|||||||
{Name: "registry_controller_url", Scope: SystemScope, Group: BasicGroup, EnvKey: "REGISTRY_CONTROLLER_URL", DefaultValue: "http://registryctl:8080", ItemType: &StringType{}, Editable: false},
|
{Name: "registry_controller_url", Scope: SystemScope, Group: BasicGroup, EnvKey: "REGISTRY_CONTROLLER_URL", DefaultValue: "http://registryctl:8080", ItemType: &StringType{}, Editable: false},
|
||||||
{Name: "self_registration", Scope: UserScope, Group: BasicGroup, EnvKey: "SELF_REGISTRATION", DefaultValue: "true", ItemType: &BoolType{}, Editable: false},
|
{Name: "self_registration", Scope: UserScope, Group: BasicGroup, EnvKey: "SELF_REGISTRATION", DefaultValue: "true", ItemType: &BoolType{}, Editable: false},
|
||||||
{Name: "token_expiration", Scope: UserScope, Group: BasicGroup, EnvKey: "TOKEN_EXPIRATION", DefaultValue: "30", ItemType: &IntType{}, Editable: false},
|
{Name: "token_expiration", Scope: UserScope, Group: BasicGroup, EnvKey: "TOKEN_EXPIRATION", DefaultValue: "30", ItemType: &IntType{}, Editable: false},
|
||||||
{Name: "token_service_url", Scope: SystemScope, Group: BasicGroup, EnvKey: "TOKEN_SERVICE_URL", DefaultValue: "", ItemType: &StringType{}, Editable: false},
|
{Name: "token_service_url", Scope: SystemScope, Group: BasicGroup, EnvKey: "TOKEN_SERVICE_URL", DefaultValue: "http://core:8080/service/token", ItemType: &StringType{}, Editable: false},
|
||||||
|
|
||||||
{Name: "uaa_client_id", Scope: UserScope, Group: UAAGroup, EnvKey: "UAA_CLIENTID", DefaultValue: "", ItemType: &StringType{}, Editable: false},
|
{Name: "uaa_client_id", Scope: UserScope, Group: UAAGroup, EnvKey: "UAA_CLIENTID", DefaultValue: "", ItemType: &StringType{}, Editable: false},
|
||||||
{Name: "uaa_client_secret", Scope: UserScope, Group: UAAGroup, EnvKey: "UAA_CLIENTSECRET", DefaultValue: "", ItemType: &StringType{}, Editable: false},
|
{Name: "uaa_client_secret", Scope: UserScope, Group: UAAGroup, EnvKey: "UAA_CLIENTSECRET", DefaultValue: "", ItemType: &StringType{}, Editable: false},
|
||||||
@ -128,7 +129,7 @@ var (
|
|||||||
{Name: "uaa_verify_cert", Scope: UserScope, Group: UAAGroup, EnvKey: "UAA_VERIFY_CERT", DefaultValue: "false", ItemType: &BoolType{}, Editable: false},
|
{Name: "uaa_verify_cert", Scope: UserScope, Group: UAAGroup, EnvKey: "UAA_VERIFY_CERT", DefaultValue: "false", ItemType: &BoolType{}, Editable: false},
|
||||||
|
|
||||||
{Name: "with_chartmuseum", Scope: SystemScope, Group: BasicGroup, EnvKey: "WITH_CHARTMUSEUM", DefaultValue: "false", ItemType: &BoolType{}, Editable: true},
|
{Name: "with_chartmuseum", Scope: SystemScope, Group: BasicGroup, EnvKey: "WITH_CHARTMUSEUM", DefaultValue: "false", ItemType: &BoolType{}, Editable: true},
|
||||||
{Name: "with_clair", Scope: SystemScope, Group: BasicGroup, EnvKey: "WITH_CLAIR", DefaultValue: "true", ItemType: &BoolType{}, Editable: true},
|
{Name: "with_clair", Scope: SystemScope, Group: BasicGroup, EnvKey: "WITH_CLAIR", DefaultValue: "false", ItemType: &BoolType{}, Editable: true},
|
||||||
{Name: "with_notary", Scope: SystemScope, Group: BasicGroup, EnvKey: "WITH_NOTARY", DefaultValue: "false", ItemType: &BoolType{}, Editable: true},
|
{Name: "with_notary", Scope: SystemScope, Group: BasicGroup, EnvKey: "WITH_NOTARY", DefaultValue: "false", ItemType: &BoolType{}, Editable: true},
|
||||||
}
|
}
|
||||||
)
|
)
|
||||||
|
@ -12,11 +12,15 @@
|
|||||||
// See the License for the specific language governing permissions and
|
// See the License for the specific language governing permissions and
|
||||||
// limitations under the License.
|
// limitations under the License.
|
||||||
|
|
||||||
|
// Package metadata define config related metadata
|
||||||
package metadata
|
package metadata
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"encoding/json"
|
"encoding/json"
|
||||||
|
"fmt"
|
||||||
|
"github.com/goharbor/harbor/src/common"
|
||||||
"strconv"
|
"strconv"
|
||||||
|
"strings"
|
||||||
)
|
)
|
||||||
|
|
||||||
// Type - Use this interface to define and encapsulate the behavior of validation and transformation
|
// Type - Use this interface to define and encapsulate the behavior of validation and transformation
|
||||||
@ -39,6 +43,46 @@ func (t *StringType) get(str string) (interface{}, error) {
|
|||||||
return str, nil
|
return str, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// NonEmptyStringType ...
|
||||||
|
type NonEmptyStringType struct {
|
||||||
|
StringType
|
||||||
|
}
|
||||||
|
|
||||||
|
func (t *NonEmptyStringType) validate(str string) error {
|
||||||
|
if len(strings.TrimSpace(str)) == 0 {
|
||||||
|
return ErrStringValueIsEmpty
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// AuthModeType ...
|
||||||
|
type AuthModeType struct {
|
||||||
|
StringType
|
||||||
|
}
|
||||||
|
|
||||||
|
func (t *AuthModeType) validate(str string) error {
|
||||||
|
if str == common.LDAPAuth || str == common.DBAuth || str == common.UAAAuth {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
return fmt.Errorf("invalid %s, shoud be one of %s, %s, %s",
|
||||||
|
common.AUTHMode, common.DBAuth, common.LDAPAuth, common.UAAAuth)
|
||||||
|
}
|
||||||
|
|
||||||
|
// ProjectCreationRestrictionType ...
|
||||||
|
type ProjectCreationRestrictionType struct {
|
||||||
|
StringType
|
||||||
|
}
|
||||||
|
|
||||||
|
func (t *ProjectCreationRestrictionType) validate(str string) error {
|
||||||
|
if !(str == common.ProCrtRestrAdmOnly || str == common.ProCrtRestrEveryone) {
|
||||||
|
return fmt.Errorf("invalid %s, should be %s or %s",
|
||||||
|
common.ProjectCreationRestriction,
|
||||||
|
common.ProCrtRestrAdmOnly,
|
||||||
|
common.ProCrtRestrEveryone)
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
// IntType ..
|
// IntType ..
|
||||||
type IntType struct {
|
type IntType struct {
|
||||||
}
|
}
|
||||||
@ -48,22 +92,45 @@ func (t *IntType) validate(str string) error {
|
|||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
// GetInt ...
|
|
||||||
func (t *IntType) get(str string) (interface{}, error) {
|
func (t *IntType) get(str string) (interface{}, error) {
|
||||||
return strconv.Atoi(str)
|
return strconv.Atoi(str)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// PortType ...
|
||||||
|
type PortType struct {
|
||||||
|
IntType
|
||||||
|
}
|
||||||
|
|
||||||
|
func (t *PortType) validate(str string) error {
|
||||||
|
val, err := strconv.Atoi(str)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
if val < 0 {
|
||||||
|
return fmt.Errorf("network port should be greater than 0")
|
||||||
|
}
|
||||||
|
|
||||||
|
if val > 65535 {
|
||||||
|
return fmt.Errorf("network port should be less than 65535")
|
||||||
|
}
|
||||||
|
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
// LdapScopeType - The LDAP scope is a int type, but its is limit to 0, 1, 2
|
// LdapScopeType - The LDAP scope is a int type, but its is limit to 0, 1, 2
|
||||||
type LdapScopeType struct {
|
type LdapScopeType struct {
|
||||||
IntType
|
IntType
|
||||||
}
|
}
|
||||||
|
|
||||||
// Validate - Verify the range is limited
|
// validate - Verify the range is limited
|
||||||
func (t *LdapScopeType) validate(str string) error {
|
func (t *LdapScopeType) validate(str string) error {
|
||||||
if str == "0" || str == "1" || str == "2" {
|
if str == "0" || str == "1" || str == "2" {
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
return ErrInvalidData
|
return fmt.Errorf("invalid scope, should be %d, %d or %d",
|
||||||
|
common.LDAPScopeBase,
|
||||||
|
common.LDAPScopeOnelevel,
|
||||||
|
common.LDAPScopeSubtree)
|
||||||
}
|
}
|
||||||
|
|
||||||
// Int64Type ...
|
// Int64Type ...
|
||||||
@ -75,7 +142,6 @@ func (t *Int64Type) validate(str string) error {
|
|||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
// GetInt64 ...
|
|
||||||
func (t *Int64Type) get(str string) (interface{}, error) {
|
func (t *Int64Type) get(str string) (interface{}, error) {
|
||||||
return strconv.ParseInt(str, 10, 64)
|
return strconv.ParseInt(str, 10, 64)
|
||||||
}
|
}
|
||||||
@ -116,7 +182,7 @@ func (t *MapType) validate(str string) error {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func (t *MapType) get(str string) (interface{}, error) {
|
func (t *MapType) get(str string) (interface{}, error) {
|
||||||
result := map[string]string{}
|
result := map[string]interface{}{}
|
||||||
err := json.Unmarshal([]byte(str), &result)
|
err := json.Unmarshal([]byte(str), &result)
|
||||||
return result, err
|
return result, err
|
||||||
}
|
}
|
||||||
|
@ -94,5 +94,5 @@ func TestMapType_validate(t *testing.T) {
|
|||||||
func TestMapType_get(t *testing.T) {
|
func TestMapType_get(t *testing.T) {
|
||||||
test := &MapType{}
|
test := &MapType{}
|
||||||
result, _ := test.get(`{"sample":"abc", "another":"welcome"}`)
|
result, _ := test.get(`{"sample":"abc", "another":"welcome"}`)
|
||||||
assert.Equal(t, result, map[string]string{"sample": "abc", "another": "welcome"})
|
assert.Equal(t, map[string]interface{}{"sample": "abc", "another": "welcome"}, result)
|
||||||
}
|
}
|
||||||
|
@ -29,6 +29,8 @@ var (
|
|||||||
ErrInvalidData = errors.New("the data provided is invalid")
|
ErrInvalidData = errors.New("the data provided is invalid")
|
||||||
// ErrValueNotSet ...
|
// ErrValueNotSet ...
|
||||||
ErrValueNotSet = errors.New("the configure value is not set")
|
ErrValueNotSet = errors.New("the configure value is not set")
|
||||||
|
// ErrStringValueIsEmpty ...
|
||||||
|
ErrStringValueIsEmpty = errors.New("the configure value can not be empty")
|
||||||
)
|
)
|
||||||
|
|
||||||
// ConfigureValue - struct to hold a actual value, also include the name of config metadata.
|
// ConfigureValue - struct to hold a actual value, also include the name of config metadata.
|
||||||
@ -126,6 +128,14 @@ func (c *ConfigureValue) GetStringToStringMap() map[string]string {
|
|||||||
return result
|
return result
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// GetAnyType get the interface{} of current value
|
||||||
|
func (c *ConfigureValue) GetAnyType() (interface{}, error) {
|
||||||
|
if item, ok := Instance().GetByName(c.Name); ok {
|
||||||
|
return item.ItemType.get(c.Value)
|
||||||
|
}
|
||||||
|
return nil, ErrNotDefined
|
||||||
|
}
|
||||||
|
|
||||||
// Validate - to validate configure items, if passed, return nil, else return error
|
// Validate - to validate configure items, if passed, return nil, else return error
|
||||||
func (c *ConfigureValue) Validate() error {
|
func (c *ConfigureValue) Validate() error {
|
||||||
if item, ok := Instance().GetByName(c.Name); ok {
|
if item, ok := Instance().GetByName(c.Name); ok {
|
||||||
|
@ -26,6 +26,7 @@ var testingMetaDataArray = []Item{
|
|||||||
{Name: "ulimit", ItemType: &Int64Type{}, Scope: "user", Group: "ldapbasic"},
|
{Name: "ulimit", ItemType: &Int64Type{}, Scope: "user", Group: "ldapbasic"},
|
||||||
{Name: "ldap_verify_cert", ItemType: &BoolType{}, Scope: "user", Group: "ldapbasic"},
|
{Name: "ldap_verify_cert", ItemType: &BoolType{}, Scope: "user", Group: "ldapbasic"},
|
||||||
{Name: "sample_map_setting", ItemType: &MapType{}, Scope: "user", Group: "ldapbasic"},
|
{Name: "sample_map_setting", ItemType: &MapType{}, Scope: "user", Group: "ldapbasic"},
|
||||||
|
{Name: "scan_all_policy", ItemType: &MapType{}, Scope: "user", Group: "basic"},
|
||||||
}
|
}
|
||||||
|
|
||||||
// createCfgValue ... Create a ConfigureValue object, only used in test
|
// createCfgValue ... Create a ConfigureValue object, only used in test
|
||||||
@ -50,7 +51,11 @@ func TestConfigureValue_GetString(t *testing.T) {
|
|||||||
|
|
||||||
func TestConfigureValue_GetStringToStringMap(t *testing.T) {
|
func TestConfigureValue_GetStringToStringMap(t *testing.T) {
|
||||||
Instance().initFromArray(testingMetaDataArray)
|
Instance().initFromArray(testingMetaDataArray)
|
||||||
assert.Equal(t, createCfgValue("sample_map_setting", `{"sample":"abc"}`).GetStringToStringMap(), map[string]string{"sample": "abc"})
|
val, err := createCfgValue("sample_map_setting", `{"sample":"abc"}`).GetAnyType()
|
||||||
|
if err != nil {
|
||||||
|
t.Error(err)
|
||||||
|
}
|
||||||
|
assert.Equal(t, val, map[string]interface{}{"sample": "abc"})
|
||||||
Instance().init()
|
Instance().init()
|
||||||
}
|
}
|
||||||
func TestConfigureValue_GetInt(t *testing.T) {
|
func TestConfigureValue_GetInt(t *testing.T) {
|
||||||
@ -61,3 +66,12 @@ func TestConfigureValue_GetInt64(t *testing.T) {
|
|||||||
Instance().initFromArray(testingMetaDataArray)
|
Instance().initFromArray(testingMetaDataArray)
|
||||||
assert.Equal(t, createCfgValue("ulimit", "99999").GetInt64(), int64(99999))
|
assert.Equal(t, createCfgValue("ulimit", "99999").GetInt64(), int64(99999))
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func TestNewScanAllPolicy(t *testing.T) {
|
||||||
|
Instance().initFromArray(testingMetaDataArray)
|
||||||
|
value, err := NewCfgValue("scan_all_policy", `{"parameter":{"daily_time":0},"type":"daily"}`)
|
||||||
|
if err != nil {
|
||||||
|
t.Errorf("Can not create scan all policy err: %v", err)
|
||||||
|
}
|
||||||
|
fmt.Printf("value %v\n", value.GetString())
|
||||||
|
}
|
||||||
|
@ -15,12 +15,13 @@
|
|||||||
package driver
|
package driver
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"fmt"
|
|
||||||
"github.com/goharbor/harbor/src/common/config/encrypt"
|
"github.com/goharbor/harbor/src/common/config/encrypt"
|
||||||
"github.com/goharbor/harbor/src/common/config/metadata"
|
"github.com/goharbor/harbor/src/common/config/metadata"
|
||||||
"github.com/goharbor/harbor/src/common/dao"
|
"github.com/goharbor/harbor/src/common/dao"
|
||||||
"github.com/goharbor/harbor/src/common/models"
|
"github.com/goharbor/harbor/src/common/models"
|
||||||
|
"github.com/goharbor/harbor/src/common/utils"
|
||||||
"github.com/goharbor/harbor/src/common/utils/log"
|
"github.com/goharbor/harbor/src/common/utils/log"
|
||||||
|
"os"
|
||||||
)
|
)
|
||||||
|
|
||||||
// Database - Used to load/save configuration in database
|
// Database - Used to load/save configuration in database
|
||||||
@ -61,11 +62,11 @@ func (d *Database) Save(cfgs map[string]interface{}) error {
|
|||||||
var configEntries []models.ConfigEntry
|
var configEntries []models.ConfigEntry
|
||||||
for key, value := range cfgs {
|
for key, value := range cfgs {
|
||||||
if item, ok := metadata.Instance().GetByName(key); ok {
|
if item, ok := metadata.Instance().GetByName(key); ok {
|
||||||
if item.Scope == metadata.SystemScope {
|
if os.Getenv("UTTEST") != "true" && item.Scope == metadata.SystemScope {
|
||||||
log.Errorf("system setting can not updated, key %v", key)
|
log.Errorf("system setting can not updated, key %v", key)
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
strValue := fmt.Sprintf("%v", value)
|
strValue := utils.GetStrValueOfAnyType(value)
|
||||||
entry := &models.ConfigEntry{Key: key, Value: strValue}
|
entry := &models.ConfigEntry{Key: key, Value: strValue}
|
||||||
if _, ok := item.ItemType.(*metadata.PasswordType); ok {
|
if _, ok := item.ItemType.(*metadata.PasswordType); ok {
|
||||||
if encryptPassword, err := encrypt.Instance().Encrypt(strValue); err == nil {
|
if encryptPassword, err := encrypt.Instance().Encrypt(strValue); err == nil {
|
||||||
|
@ -14,6 +14,7 @@
|
|||||||
package driver
|
package driver
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"github.com/goharbor/harbor/src/common"
|
||||||
"github.com/goharbor/harbor/src/common/dao"
|
"github.com/goharbor/harbor/src/common/dao"
|
||||||
"github.com/stretchr/testify/assert"
|
"github.com/stretchr/testify/assert"
|
||||||
"os"
|
"os"
|
||||||
@ -26,18 +27,22 @@ func TestMain(m *testing.M) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func TestDatabase_Load(t *testing.T) {
|
func TestDatabase_Load(t *testing.T) {
|
||||||
|
|
||||||
|
cfgs := map[string]interface{}{
|
||||||
|
common.AUTHMode: "db_auth",
|
||||||
|
common.LDAPURL: "ldap://ldap.vmware.com",
|
||||||
|
}
|
||||||
driver := Database{}
|
driver := Database{}
|
||||||
|
driver.Save(cfgs)
|
||||||
cfgMap, err := driver.Load()
|
cfgMap, err := driver.Load()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
t.Errorf("failed to load, error %v", err)
|
t.Errorf("failed to load, error %v", err)
|
||||||
}
|
}
|
||||||
|
assert.True(t, len(cfgMap) >= 1)
|
||||||
assert.True(t, len(cfgMap) > 10)
|
|
||||||
|
|
||||||
if _, ok := cfgMap["ldap_url"]; !ok {
|
if _, ok := cfgMap["ldap_url"]; !ok {
|
||||||
t.Error("Can not find ldap_url")
|
t.Error("Can not find ldap_url")
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestDatabase_Save(t *testing.T) {
|
func TestDatabase_Save(t *testing.T) {
|
||||||
|
@ -1,29 +1,38 @@
|
|||||||
package driver
|
package driver
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"errors"
|
||||||
"github.com/goharbor/harbor/src/common/http"
|
"github.com/goharbor/harbor/src/common/http"
|
||||||
"github.com/goharbor/harbor/src/common/http/modifier"
|
"github.com/goharbor/harbor/src/common/http/modifier"
|
||||||
|
"github.com/goharbor/harbor/src/common/utils/log"
|
||||||
)
|
)
|
||||||
|
|
||||||
// RESTDriver - config store driver based on REST API
|
// RESTDriver - config store driver based on REST API
|
||||||
type RESTDriver struct {
|
type RESTDriver struct {
|
||||||
coreURL string
|
configRESTURL string
|
||||||
client *http.Client
|
client *http.Client
|
||||||
}
|
}
|
||||||
|
|
||||||
// NewRESTDriver - Create RESTDriver
|
// NewRESTDriver - Create RESTDriver
|
||||||
func NewRESTDriver(coreURL string, modifiers ...modifier.Modifier) *RESTDriver {
|
func NewRESTDriver(configRESTURL string, modifiers ...modifier.Modifier) *RESTDriver {
|
||||||
return &RESTDriver{coreURL: coreURL, client: http.NewClient(nil, modifiers...)}
|
return &RESTDriver{configRESTURL: configRESTURL, client: http.NewClient(nil, modifiers...)}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Load - load config data from REST server
|
// Load - load config data from REST server
|
||||||
func (h *RESTDriver) Load() (map[string]interface{}, error) {
|
func (h *RESTDriver) Load() (map[string]interface{}, error) {
|
||||||
cfgMap := map[string]interface{}{}
|
cfgMap := map[string]interface{}{}
|
||||||
err := h.client.Get(h.coreURL, &cfgMap)
|
log.Infof("get configuration from url: %+v", h.configRESTURL)
|
||||||
|
err := h.client.Get(h.configRESTURL, &cfgMap)
|
||||||
|
if err != nil {
|
||||||
|
log.Errorf("Failed on load rest config err:%v, url:%v", err, h.configRESTURL)
|
||||||
|
}
|
||||||
|
if len(cfgMap) < 1 {
|
||||||
|
return cfgMap, errors.New("failed to load rest config")
|
||||||
|
}
|
||||||
return cfgMap, err
|
return cfgMap, err
|
||||||
}
|
}
|
||||||
|
|
||||||
// Save - Save config data to REST server by PUT method
|
// Save - Save config data to REST server by PUT method
|
||||||
func (h *RESTDriver) Save(cfgMap map[string]interface{}) error {
|
func (h *RESTDriver) Save(cfgMap map[string]interface{}) error {
|
||||||
return h.client.Put(h.coreURL, cfgMap)
|
return h.client.Put(h.configRESTURL, cfgMap)
|
||||||
}
|
}
|
||||||
|
@ -6,6 +6,7 @@ import (
|
|||||||
"fmt"
|
"fmt"
|
||||||
"github.com/goharbor/harbor/src/common/config/metadata"
|
"github.com/goharbor/harbor/src/common/config/metadata"
|
||||||
"github.com/goharbor/harbor/src/common/config/store/driver"
|
"github.com/goharbor/harbor/src/common/config/store/driver"
|
||||||
|
"github.com/goharbor/harbor/src/common/utils"
|
||||||
"github.com/goharbor/harbor/src/common/utils/log"
|
"github.com/goharbor/harbor/src/common/utils/log"
|
||||||
"sync"
|
"sync"
|
||||||
)
|
)
|
||||||
@ -30,7 +31,17 @@ func (c *ConfigStore) Get(key string) (*metadata.ConfigureValue, error) {
|
|||||||
return nil, errors.New("data in config store is not a ConfigureValue type")
|
return nil, errors.New("data in config store is not a ConfigureValue type")
|
||||||
}
|
}
|
||||||
return nil, metadata.ErrValueNotSet
|
return nil, metadata.ErrValueNotSet
|
||||||
|
}
|
||||||
|
|
||||||
|
// GetAnyType get interface{} type for config items
|
||||||
|
func (c *ConfigStore) GetAnyType(key string) (interface{}, error) {
|
||||||
|
if value, ok := c.cfgValues.Load(key); ok {
|
||||||
|
if result, ok := value.(metadata.ConfigureValue); ok {
|
||||||
|
return result.GetAnyType()
|
||||||
|
}
|
||||||
|
return nil, errors.New("data in config store is not a ConfigureValue type")
|
||||||
|
}
|
||||||
|
return nil, metadata.ErrValueNotSet
|
||||||
}
|
}
|
||||||
|
|
||||||
// Set - Set configure value in store, not saved to config driver
|
// Set - Set configure value in store, not saved to config driver
|
||||||
@ -71,7 +82,6 @@ func (c *ConfigStore) Save() error {
|
|||||||
if _, ok := metadata.Instance().GetByName(keyStr); ok {
|
if _, ok := metadata.Instance().GetByName(keyStr); ok {
|
||||||
cfgMap[keyStr] = valueStr
|
cfgMap[keyStr] = valueStr
|
||||||
} else {
|
} else {
|
||||||
|
|
||||||
log.Errorf("failed to get metadata for key %v", keyStr)
|
log.Errorf("failed to get metadata for key %v", keyStr)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -89,7 +99,7 @@ func (c *ConfigStore) Save() error {
|
|||||||
func (c *ConfigStore) Update(cfgMap map[string]interface{}) error {
|
func (c *ConfigStore) Update(cfgMap map[string]interface{}) error {
|
||||||
// Update to store
|
// Update to store
|
||||||
for key, value := range cfgMap {
|
for key, value := range cfgMap {
|
||||||
configValue, err := metadata.NewCfgValue(key, fmt.Sprintf("%v", value))
|
configValue, err := metadata.NewCfgValue(key, utils.GetStrValueOfAnyType(value))
|
||||||
if err != nil {
|
if err != nil {
|
||||||
log.Warningf("error %v, skip to update configure item, key:%v ", err, key)
|
log.Warningf("error %v, skip to update configure item, key:%v ", err, key)
|
||||||
delete(cfgMap, key)
|
delete(cfgMap, key)
|
||||||
|
@ -107,7 +107,6 @@ const (
|
|||||||
ClairURL = "clair_url"
|
ClairURL = "clair_url"
|
||||||
NotaryURL = "notary_url"
|
NotaryURL = "notary_url"
|
||||||
DefaultAdminserverEndpoint = "http://adminserver:8080"
|
DefaultAdminserverEndpoint = "http://adminserver:8080"
|
||||||
DefaultJobserviceEndpoint = "http://jobservice:8080"
|
|
||||||
DefaultCoreEndpoint = "http://core:8080"
|
DefaultCoreEndpoint = "http://core:8080"
|
||||||
DefaultNotaryEndpoint = "http://notary-server:4443"
|
DefaultNotaryEndpoint = "http://notary-server:4443"
|
||||||
LdapGroupType = 1
|
LdapGroupType = 1
|
||||||
@ -121,47 +120,13 @@ const (
|
|||||||
DefaultRegistryCtlURL = "http://registryctl:8080"
|
DefaultRegistryCtlURL = "http://registryctl:8080"
|
||||||
DefaultClairHealthCheckServerURL = "http://clair:6061"
|
DefaultClairHealthCheckServerURL = "http://clair:6061"
|
||||||
// Use this prefix to distinguish harbor user, the prefix contains a special character($), so it cannot be registered as a harbor user.
|
// Use this prefix to distinguish harbor user, the prefix contains a special character($), so it cannot be registered as a harbor user.
|
||||||
RobotPrefix = "robot$"
|
RobotPrefix = "robot$"
|
||||||
|
CoreConfigPath = "/api/internal/configurations"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
// TODO remove with adminserver
|
||||||
// Shared variable, not allowed to modify
|
// Shared variable, not allowed to modify
|
||||||
var (
|
var (
|
||||||
// the keys of configurations which user can modify in PUT method and user can
|
|
||||||
// get in GET method
|
|
||||||
HarborValidKeys = []string{
|
|
||||||
AUTHMode,
|
|
||||||
SelfRegistration,
|
|
||||||
LDAPURL,
|
|
||||||
LDAPSearchDN,
|
|
||||||
LDAPSearchPwd,
|
|
||||||
LDAPBaseDN,
|
|
||||||
LDAPUID,
|
|
||||||
LDAPFilter,
|
|
||||||
LDAPScope,
|
|
||||||
LDAPTimeout,
|
|
||||||
LDAPVerifyCert,
|
|
||||||
LDAPGroupAttributeName,
|
|
||||||
LDAPGroupBaseDN,
|
|
||||||
LDAPGroupSearchFilter,
|
|
||||||
LDAPGroupSearchScope,
|
|
||||||
LdapGroupAdminDn,
|
|
||||||
EmailHost,
|
|
||||||
EmailPort,
|
|
||||||
EmailUsername,
|
|
||||||
EmailPassword,
|
|
||||||
EmailFrom,
|
|
||||||
EmailSSL,
|
|
||||||
EmailIdentity,
|
|
||||||
EmailInsecure,
|
|
||||||
ProjectCreationRestriction,
|
|
||||||
TokenExpiration,
|
|
||||||
ScanAllPolicy,
|
|
||||||
UAAClientID,
|
|
||||||
UAAClientSecret,
|
|
||||||
UAAEndpoint,
|
|
||||||
UAAVerifyCert,
|
|
||||||
ReadOnly,
|
|
||||||
}
|
|
||||||
|
|
||||||
// value is default value
|
// value is default value
|
||||||
HarborStringKeysMap = map[string]string{
|
HarborStringKeysMap = map[string]string{
|
||||||
@ -202,10 +167,4 @@ var (
|
|||||||
UAAVerifyCert: true,
|
UAAVerifyCert: true,
|
||||||
ReadOnly: false,
|
ReadOnly: false,
|
||||||
}
|
}
|
||||||
|
|
||||||
HarborPasswordKeys = []string{
|
|
||||||
EmailPassword,
|
|
||||||
LDAPSearchPwd,
|
|
||||||
UAAClientSecret,
|
|
||||||
}
|
|
||||||
)
|
)
|
||||||
|
@ -90,6 +90,20 @@ func InitDatabase(database *models.Database) error {
|
|||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// InitAndUpgradeDatabase - init database and upgrade when required
|
||||||
|
func InitAndUpgradeDatabase(database *models.Database) error {
|
||||||
|
if err := InitDatabase(database); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
if err := UpgradeSchema(database); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
if err := CheckSchemaVersion(); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
// CheckSchemaVersion checks that whether the schema version matches with the expected one
|
// CheckSchemaVersion checks that whether the schema version matches with the expected one
|
||||||
func CheckSchemaVersion() error {
|
func CheckSchemaVersion() error {
|
||||||
version, err := GetSchemaVersion()
|
version, err := GetSchemaVersion()
|
||||||
|
@ -142,6 +142,7 @@ func TestMain(m *testing.M) {
|
|||||||
switch database {
|
switch database {
|
||||||
case "postgresql":
|
case "postgresql":
|
||||||
PrepareTestForPostgresSQL()
|
PrepareTestForPostgresSQL()
|
||||||
|
PrepareTestData([]string{"delete from admin_job"}, []string{})
|
||||||
default:
|
default:
|
||||||
log.Fatalf("invalid database: %s", database)
|
log.Fatalf("invalid database: %s", database)
|
||||||
}
|
}
|
||||||
|
@ -16,7 +16,6 @@ package local
|
|||||||
|
|
||||||
import (
|
import (
|
||||||
"os"
|
"os"
|
||||||
"strconv"
|
|
||||||
"testing"
|
"testing"
|
||||||
|
|
||||||
"github.com/goharbor/harbor/src/common"
|
"github.com/goharbor/harbor/src/common"
|
||||||
@ -25,6 +24,7 @@ import (
|
|||||||
"github.com/goharbor/harbor/src/common/models"
|
"github.com/goharbor/harbor/src/common/models"
|
||||||
"github.com/goharbor/harbor/src/common/rbac"
|
"github.com/goharbor/harbor/src/common/rbac"
|
||||||
"github.com/goharbor/harbor/src/common/utils/log"
|
"github.com/goharbor/harbor/src/common/utils/log"
|
||||||
|
"github.com/goharbor/harbor/src/common/utils/test"
|
||||||
"github.com/goharbor/harbor/src/core/promgr"
|
"github.com/goharbor/harbor/src/core/promgr"
|
||||||
"github.com/goharbor/harbor/src/core/promgr/pmsdriver/local"
|
"github.com/goharbor/harbor/src/core/promgr/pmsdriver/local"
|
||||||
"github.com/stretchr/testify/assert"
|
"github.com/stretchr/testify/assert"
|
||||||
@ -54,45 +54,8 @@ var (
|
|||||||
)
|
)
|
||||||
|
|
||||||
func TestMain(m *testing.M) {
|
func TestMain(m *testing.M) {
|
||||||
dbHost := os.Getenv("POSTGRESQL_HOST")
|
|
||||||
if len(dbHost) == 0 {
|
|
||||||
log.Fatalf("environment variable POSTGRES_HOST is not set")
|
|
||||||
}
|
|
||||||
dbUser := os.Getenv("POSTGRESQL_USR")
|
|
||||||
if len(dbUser) == 0 {
|
|
||||||
log.Fatalf("environment variable POSTGRES_USR is not set")
|
|
||||||
}
|
|
||||||
dbPortStr := os.Getenv("POSTGRESQL_PORT")
|
|
||||||
if len(dbPortStr) == 0 {
|
|
||||||
log.Fatalf("environment variable POSTGRES_PORT is not set")
|
|
||||||
}
|
|
||||||
dbPort, err := strconv.Atoi(dbPortStr)
|
|
||||||
if err != nil {
|
|
||||||
log.Fatalf("invalid POSTGRESQL_PORT: %v", err)
|
|
||||||
}
|
|
||||||
|
|
||||||
dbPassword := os.Getenv("POSTGRESQL_PWD")
|
test.InitDatabaseFromEnv()
|
||||||
dbDatabase := os.Getenv("POSTGRESQL_DATABASE")
|
|
||||||
if len(dbDatabase) == 0 {
|
|
||||||
log.Fatalf("environment variable POSTGRESQL_DATABASE is not set")
|
|
||||||
}
|
|
||||||
|
|
||||||
database := &models.Database{
|
|
||||||
Type: "postgresql",
|
|
||||||
PostGreSQL: &models.PostGreSQL{
|
|
||||||
Host: dbHost,
|
|
||||||
Port: dbPort,
|
|
||||||
Username: dbUser,
|
|
||||||
Password: dbPassword,
|
|
||||||
Database: dbDatabase,
|
|
||||||
},
|
|
||||||
}
|
|
||||||
|
|
||||||
log.Infof("POSTGRES_HOST: %s, POSTGRES_USR: %s, POSTGRES_PORT: %d, POSTGRES_PWD: %s\n", dbHost, dbUser, dbPort, dbPassword)
|
|
||||||
|
|
||||||
if err := dao.InitDatabase(database); err != nil {
|
|
||||||
log.Fatalf("failed to initialize database: %v", err)
|
|
||||||
}
|
|
||||||
|
|
||||||
// regiser users
|
// regiser users
|
||||||
id, err := dao.Register(*projectAdminUser)
|
id, err := dao.Register(*projectAdminUser)
|
||||||
|
@ -6,7 +6,6 @@ import (
|
|||||||
"testing"
|
"testing"
|
||||||
|
|
||||||
"github.com/goharbor/harbor/src/common"
|
"github.com/goharbor/harbor/src/common"
|
||||||
"github.com/goharbor/harbor/src/common/dao"
|
|
||||||
"github.com/goharbor/harbor/src/common/models"
|
"github.com/goharbor/harbor/src/common/models"
|
||||||
"github.com/goharbor/harbor/src/common/utils/log"
|
"github.com/goharbor/harbor/src/common/utils/log"
|
||||||
"github.com/goharbor/harbor/src/common/utils/test"
|
"github.com/goharbor/harbor/src/common/utils/test"
|
||||||
@ -75,18 +74,9 @@ var adminServerDefaultConfigWithVerifyCert = map[string]interface{}{
|
|||||||
}
|
}
|
||||||
|
|
||||||
func TestMain(m *testing.M) {
|
func TestMain(m *testing.M) {
|
||||||
server, err := test.NewAdminserver(adminServerLdapTestConfig)
|
test.InitDatabaseFromEnv()
|
||||||
if err != nil {
|
|
||||||
log.Fatalf("failed to create a mock admin server: %v", err)
|
|
||||||
}
|
|
||||||
defer server.Close()
|
|
||||||
|
|
||||||
if err := os.Setenv("ADMINSERVER_URL", server.URL); err != nil {
|
|
||||||
log.Fatalf("failed to set env %s: %v", "ADMINSERVER_URL", err)
|
|
||||||
}
|
|
||||||
|
|
||||||
secretKeyPath := "/tmp/secretkey"
|
secretKeyPath := "/tmp/secretkey"
|
||||||
_, err = test.GenerateKey(secretKeyPath)
|
_, err := test.GenerateKey(secretKeyPath)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
log.Errorf("failed to generate secret key: %v", err)
|
log.Errorf("failed to generate secret key: %v", err)
|
||||||
return
|
return
|
||||||
@ -101,14 +91,7 @@ func TestMain(m *testing.M) {
|
|||||||
log.Fatalf("failed to initialize configurations: %v", err)
|
log.Fatalf("failed to initialize configurations: %v", err)
|
||||||
}
|
}
|
||||||
|
|
||||||
database, err := uiConfig.Database()
|
uiConfig.Upload(adminServerLdapTestConfig)
|
||||||
if err != nil {
|
|
||||||
log.Fatalf("failed to get database configuration: %v", err)
|
|
||||||
}
|
|
||||||
|
|
||||||
if err := dao.InitDatabase(database); err != nil {
|
|
||||||
log.Fatalf("failed to initialize database: %v", err)
|
|
||||||
}
|
|
||||||
|
|
||||||
os.Exit(m.Run())
|
os.Exit(m.Run())
|
||||||
|
|
||||||
|
@ -17,9 +17,8 @@ import (
|
|||||||
"encoding/json"
|
"encoding/json"
|
||||||
"fmt"
|
"fmt"
|
||||||
|
|
||||||
"github.com/goharbor/harbor/src/common"
|
|
||||||
notarytest "github.com/goharbor/harbor/src/common/utils/notary/test"
|
notarytest "github.com/goharbor/harbor/src/common/utils/notary/test"
|
||||||
utilstest "github.com/goharbor/harbor/src/common/utils/test"
|
"github.com/goharbor/harbor/src/common/utils/test"
|
||||||
"github.com/goharbor/harbor/src/core/config"
|
"github.com/goharbor/harbor/src/core/config"
|
||||||
"github.com/stretchr/testify/assert"
|
"github.com/stretchr/testify/assert"
|
||||||
|
|
||||||
@ -27,11 +26,13 @@ import (
|
|||||||
"os"
|
"os"
|
||||||
"path"
|
"path"
|
||||||
"testing"
|
"testing"
|
||||||
|
|
||||||
|
"github.com/goharbor/harbor/src/common"
|
||||||
|
"github.com/goharbor/harbor/src/common/utils/log"
|
||||||
)
|
)
|
||||||
|
|
||||||
var endpoint = "10.117.4.142"
|
var endpoint = "10.117.4.142"
|
||||||
var notaryServer *httptest.Server
|
var notaryServer *httptest.Server
|
||||||
var adminServer *httptest.Server
|
|
||||||
|
|
||||||
func TestMain(m *testing.M) {
|
func TestMain(m *testing.M) {
|
||||||
notaryServer = notarytest.NewNotaryServer(endpoint)
|
notaryServer = notarytest.NewNotaryServer(endpoint)
|
||||||
@ -42,17 +43,12 @@ func TestMain(m *testing.M) {
|
|||||||
common.CfgExpiration: 5,
|
common.CfgExpiration: 5,
|
||||||
common.TokenExpiration: 30,
|
common.TokenExpiration: 30,
|
||||||
}
|
}
|
||||||
adminServer, err := utilstest.NewAdminserver(defaultConfig)
|
|
||||||
if err != nil {
|
|
||||||
panic(err)
|
|
||||||
}
|
|
||||||
defer adminServer.Close()
|
|
||||||
if err := os.Setenv("ADMINSERVER_URL", adminServer.URL); err != nil {
|
|
||||||
panic(err)
|
|
||||||
}
|
|
||||||
if err := config.Init(); err != nil {
|
if err := config.Init(); err != nil {
|
||||||
panic(err)
|
log.Fatalf("failed to initialize config: %v", err)
|
||||||
}
|
}
|
||||||
|
test.InitDatabaseFromEnv()
|
||||||
|
config.Upload(defaultConfig)
|
||||||
notaryCachePath = "/tmp/notary"
|
notaryCachePath = "/tmp/notary"
|
||||||
result := m.Run()
|
result := m.Run()
|
||||||
if result != 0 {
|
if result != 0 {
|
||||||
|
@ -15,11 +15,13 @@
|
|||||||
package test
|
package test
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"fmt"
|
||||||
"os"
|
"os"
|
||||||
"strconv"
|
"strconv"
|
||||||
|
|
||||||
"github.com/goharbor/harbor/src/common/dao"
|
"github.com/goharbor/harbor/src/common/dao"
|
||||||
"github.com/goharbor/harbor/src/common/models"
|
"github.com/goharbor/harbor/src/common/models"
|
||||||
|
"github.com/goharbor/harbor/src/common/utils"
|
||||||
"github.com/goharbor/harbor/src/common/utils/log"
|
"github.com/goharbor/harbor/src/common/utils/log"
|
||||||
)
|
)
|
||||||
|
|
||||||
@ -44,6 +46,7 @@ func InitDatabaseFromEnv() {
|
|||||||
|
|
||||||
dbPassword := os.Getenv("POSTGRESQL_PWD")
|
dbPassword := os.Getenv("POSTGRESQL_PWD")
|
||||||
dbDatabase := os.Getenv("POSTGRESQL_DATABASE")
|
dbDatabase := os.Getenv("POSTGRESQL_DATABASE")
|
||||||
|
adminPwd := os.Getenv("HARBOR_ADMIN_PASSWD")
|
||||||
if len(dbDatabase) == 0 {
|
if len(dbDatabase) == 0 {
|
||||||
log.Fatalf("environment variable POSTGRESQL_DATABASE is not set")
|
log.Fatalf("environment variable POSTGRESQL_DATABASE is not set")
|
||||||
}
|
}
|
||||||
@ -61,7 +64,32 @@ func InitDatabaseFromEnv() {
|
|||||||
|
|
||||||
log.Infof("POSTGRES_HOST: %s, POSTGRES_USR: %s, POSTGRES_PORT: %d, POSTGRES_PWD: %s\n", dbHost, dbUser, dbPort, dbPassword)
|
log.Infof("POSTGRES_HOST: %s, POSTGRES_USR: %s, POSTGRES_PORT: %d, POSTGRES_PWD: %s\n", dbHost, dbUser, dbPort, dbPassword)
|
||||||
|
|
||||||
if err := dao.InitDatabase(database); err != nil {
|
if err := dao.InitAndUpgradeDatabase(database); err != nil {
|
||||||
log.Fatalf("failed to initialize database: %v", err)
|
log.Fatalf("failed to init and upgrade database : %v", err)
|
||||||
}
|
}
|
||||||
|
if err := updateUserInitialPassword(1, adminPwd); err != nil {
|
||||||
|
log.Fatalf("failed to init password for admin: %v", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
func updateUserInitialPassword(userID int, password string) error {
|
||||||
|
queryUser := models.User{UserID: userID}
|
||||||
|
user, err := dao.GetUser(queryUser)
|
||||||
|
if err != nil {
|
||||||
|
return fmt.Errorf("Failed to get user, userID: %d %v", userID, err)
|
||||||
|
}
|
||||||
|
if user == nil {
|
||||||
|
return fmt.Errorf("user id: %d does not exist", userID)
|
||||||
|
}
|
||||||
|
if user.Salt == "" {
|
||||||
|
user.Salt = utils.GenerateRandomString()
|
||||||
|
user.Password = password
|
||||||
|
err = dao.ChangeUserPassword(*user)
|
||||||
|
if err != nil {
|
||||||
|
return fmt.Errorf("Failed to update user encrypted password, userID: %d, err: %v", userID, err)
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
}
|
||||||
|
return nil
|
||||||
}
|
}
|
||||||
|
@ -21,7 +21,11 @@ import (
|
|||||||
"net/http/httptest"
|
"net/http/httptest"
|
||||||
"strings"
|
"strings"
|
||||||
|
|
||||||
|
"fmt"
|
||||||
|
"github.com/goharbor/harbor/src/common"
|
||||||
"github.com/gorilla/mux"
|
"github.com/gorilla/mux"
|
||||||
|
"os"
|
||||||
|
"sort"
|
||||||
)
|
)
|
||||||
|
|
||||||
// RequestHandlerMapping is a mapping between request and its handler
|
// RequestHandlerMapping is a mapping between request and its handler
|
||||||
@ -87,3 +91,54 @@ func NewServer(mappings ...*RequestHandlerMapping) *httptest.Server {
|
|||||||
|
|
||||||
return httptest.NewServer(r)
|
return httptest.NewServer(r)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// GetUnitTestConfig ...
|
||||||
|
func GetUnitTestConfig() map[string]interface{} {
|
||||||
|
ipAddress := os.Getenv("IP")
|
||||||
|
return map[string]interface{}{
|
||||||
|
common.ExtEndpoint: fmt.Sprintf("https://%s", ipAddress),
|
||||||
|
common.AUTHMode: "db_auth",
|
||||||
|
common.DatabaseType: "postgresql",
|
||||||
|
common.PostGreSQLHOST: ipAddress,
|
||||||
|
common.PostGreSQLPort: 5432,
|
||||||
|
common.PostGreSQLUsername: "postgres",
|
||||||
|
common.PostGreSQLPassword: "root123",
|
||||||
|
common.PostGreSQLDatabase: "registry",
|
||||||
|
common.LDAPURL: "ldap://ldap.vmware.com",
|
||||||
|
common.LDAPSearchDN: "cn=admin,dc=example,dc=com",
|
||||||
|
common.LDAPSearchPwd: "admin",
|
||||||
|
common.LDAPBaseDN: "dc=example,dc=com",
|
||||||
|
common.LDAPUID: "uid",
|
||||||
|
common.LDAPFilter: "",
|
||||||
|
common.LDAPScope: 2,
|
||||||
|
common.LDAPTimeout: 30,
|
||||||
|
common.LDAPVerifyCert: true,
|
||||||
|
common.UAAVerifyCert: true,
|
||||||
|
common.ClairDBHost: "postgresql",
|
||||||
|
common.CfgExpiration: 5,
|
||||||
|
common.AdminInitialPassword: "Harbor12345",
|
||||||
|
common.LDAPGroupSearchFilter: "objectclass=groupOfNames",
|
||||||
|
common.LDAPGroupBaseDN: "dc=example,dc=com",
|
||||||
|
common.LDAPGroupAttributeName: "cn",
|
||||||
|
common.LDAPGroupSearchScope: 2,
|
||||||
|
common.LdapGroupAdminDn: "cn=harbor_users,ou=groups,dc=example,dc=com",
|
||||||
|
common.WithNotary: "false",
|
||||||
|
common.WithChartMuseum: "false",
|
||||||
|
common.SelfRegistration: "true",
|
||||||
|
common.WithClair: "false",
|
||||||
|
common.TokenServiceURL: "http://core:8080/service/token",
|
||||||
|
common.RegistryURL: fmt.Sprintf("http://%s:5000", ipAddress),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// TraceCfgMap ...
|
||||||
|
func TraceCfgMap(cfgs map[string]interface{}) {
|
||||||
|
var keys []string
|
||||||
|
for k := range cfgs {
|
||||||
|
keys = append(keys, k)
|
||||||
|
}
|
||||||
|
sort.Strings(keys)
|
||||||
|
for _, k := range keys {
|
||||||
|
fmt.Printf("%v=%v\n", k, cfgs[k])
|
||||||
|
}
|
||||||
|
}
|
||||||
|
@ -218,3 +218,19 @@ func ParseOfftime(offtime int64) (hour, minite, second int) {
|
|||||||
func TrimLower(str string) string {
|
func TrimLower(str string) string {
|
||||||
return strings.TrimSpace(strings.ToLower(str))
|
return strings.TrimSpace(strings.ToLower(str))
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// GetStrValueOfAnyType return string format of any value, for map, need to convert to json
|
||||||
|
func GetStrValueOfAnyType(value interface{}) string {
|
||||||
|
var strVal string
|
||||||
|
if _, ok := value.(map[string]interface{}); ok {
|
||||||
|
b, err := json.Marshal(value)
|
||||||
|
if err != nil {
|
||||||
|
log.Errorf("can not marshal json object, error %v", err)
|
||||||
|
return ""
|
||||||
|
}
|
||||||
|
strVal = string(b)
|
||||||
|
} else {
|
||||||
|
strVal = fmt.Sprintf("%v", value)
|
||||||
|
}
|
||||||
|
return strVal
|
||||||
|
}
|
||||||
|
@ -17,31 +17,47 @@ package api
|
|||||||
import (
|
import (
|
||||||
"fmt"
|
"fmt"
|
||||||
"net/http"
|
"net/http"
|
||||||
"reflect"
|
"strings"
|
||||||
|
|
||||||
"github.com/goharbor/harbor/src/common"
|
"github.com/goharbor/harbor/src/common"
|
||||||
|
"github.com/goharbor/harbor/src/common/config"
|
||||||
|
"github.com/goharbor/harbor/src/common/config/metadata"
|
||||||
"github.com/goharbor/harbor/src/common/dao"
|
"github.com/goharbor/harbor/src/common/dao"
|
||||||
"github.com/goharbor/harbor/src/common/models"
|
"github.com/goharbor/harbor/src/common/models"
|
||||||
|
"github.com/goharbor/harbor/src/common/security/secret"
|
||||||
"github.com/goharbor/harbor/src/common/utils/log"
|
"github.com/goharbor/harbor/src/common/utils/log"
|
||||||
"github.com/goharbor/harbor/src/core/config"
|
corecfg "github.com/goharbor/harbor/src/core/config"
|
||||||
|
"github.com/goharbor/harbor/src/core/filter"
|
||||||
)
|
)
|
||||||
|
|
||||||
// ConfigAPI ...
|
// ConfigAPI ...
|
||||||
type ConfigAPI struct {
|
type ConfigAPI struct {
|
||||||
BaseController
|
BaseController
|
||||||
|
cfgManager *config.CfgManager
|
||||||
}
|
}
|
||||||
|
|
||||||
// Prepare validates the user
|
// Prepare validates the user
|
||||||
func (c *ConfigAPI) Prepare() {
|
func (c *ConfigAPI) Prepare() {
|
||||||
c.BaseController.Prepare()
|
c.BaseController.Prepare()
|
||||||
|
c.cfgManager = corecfg.GetCfgManager()
|
||||||
if !c.SecurityCtx.IsAuthenticated() {
|
if !c.SecurityCtx.IsAuthenticated() {
|
||||||
c.HandleUnauthorized()
|
c.HandleUnauthorized()
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Only internal container can access /api/internal/configurations
|
||||||
|
if strings.EqualFold(c.Ctx.Request.RequestURI, "/api/internal/configurations") {
|
||||||
|
if _, ok := c.Ctx.Request.Context().Value(filter.SecurCtxKey).(*secret.SecurityContext); !ok {
|
||||||
|
c.HandleUnauthorized()
|
||||||
|
return
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
if !c.SecurityCtx.IsSysAdmin() && !c.SecurityCtx.IsSolutionUser() {
|
if !c.SecurityCtx.IsSysAdmin() && !c.SecurityCtx.IsSolutionUser() {
|
||||||
c.HandleForbidden(c.SecurityCtx.GetUsername())
|
c.HandleForbidden(c.SecurityCtx.GetUsername())
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
type value struct {
|
type value struct {
|
||||||
@ -51,20 +67,8 @@ type value struct {
|
|||||||
|
|
||||||
// Get returns configurations
|
// Get returns configurations
|
||||||
func (c *ConfigAPI) Get() {
|
func (c *ConfigAPI) Get() {
|
||||||
configs, err := config.GetSystemCfg()
|
configs := c.cfgManager.GetUserCfgs()
|
||||||
if err != nil {
|
m, err := convertForGet(configs)
|
||||||
log.Errorf("failed to get configurations: %v", err)
|
|
||||||
c.CustomAbort(http.StatusInternalServerError, http.StatusText(http.StatusInternalServerError))
|
|
||||||
}
|
|
||||||
|
|
||||||
cfgs := map[string]interface{}{}
|
|
||||||
for _, k := range common.HarborValidKeys {
|
|
||||||
if v, ok := configs[k]; ok {
|
|
||||||
cfgs[k] = v
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
m, err := convertForGet(cfgs)
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
log.Errorf("failed to convert configurations: %v", err)
|
log.Errorf("failed to convert configurations: %v", err)
|
||||||
c.CustomAbort(http.StatusInternalServerError, http.StatusText(http.StatusInternalServerError))
|
c.CustomAbort(http.StatusInternalServerError, http.StatusText(http.StatusInternalServerError))
|
||||||
@ -76,11 +80,8 @@ func (c *ConfigAPI) Get() {
|
|||||||
|
|
||||||
// GetInternalConfig returns internal configurations
|
// GetInternalConfig returns internal configurations
|
||||||
func (c *ConfigAPI) GetInternalConfig() {
|
func (c *ConfigAPI) GetInternalConfig() {
|
||||||
configs, err := config.GetSystemCfg()
|
|
||||||
if err != nil {
|
configs := c.cfgManager.GetAll()
|
||||||
log.Errorf("failed to get configurations: %v", err)
|
|
||||||
c.CustomAbort(http.StatusInternalServerError, http.StatusText(http.StatusInternalServerError))
|
|
||||||
}
|
|
||||||
c.Data["json"] = configs
|
c.Data["json"] = configs
|
||||||
c.ServeJSON()
|
c.ServeJSON()
|
||||||
}
|
}
|
||||||
@ -89,16 +90,12 @@ func (c *ConfigAPI) GetInternalConfig() {
|
|||||||
func (c *ConfigAPI) Put() {
|
func (c *ConfigAPI) Put() {
|
||||||
m := map[string]interface{}{}
|
m := map[string]interface{}{}
|
||||||
c.DecodeJSONReq(&m)
|
c.DecodeJSONReq(&m)
|
||||||
|
err := c.cfgManager.Load()
|
||||||
cfg := map[string]interface{}{}
|
if err != nil {
|
||||||
for _, k := range common.HarborValidKeys {
|
log.Errorf("failed to get configurations: %v", err)
|
||||||
if v, ok := m[k]; ok {
|
c.CustomAbort(http.StatusInternalServerError, http.StatusText(http.StatusInternalServerError))
|
||||||
cfg[k] = v
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
isSysErr, err := c.validateCfg(m)
|
||||||
isSysErr, err := validateCfg(cfg)
|
|
||||||
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
if isSysErr {
|
if isSysErr {
|
||||||
log.Errorf("failed to validate configurations: %v", err)
|
log.Errorf("failed to validate configurations: %v", err)
|
||||||
@ -108,146 +105,26 @@ func (c *ConfigAPI) Put() {
|
|||||||
c.CustomAbort(http.StatusBadRequest, err.Error())
|
c.CustomAbort(http.StatusBadRequest, err.Error())
|
||||||
}
|
}
|
||||||
|
|
||||||
if err := config.Upload(cfg); err != nil {
|
if err := c.cfgManager.UpdateConfig(m); err != nil {
|
||||||
log.Errorf("failed to upload configurations: %v", err)
|
log.Errorf("failed to upload configurations: %v", err)
|
||||||
c.CustomAbort(http.StatusInternalServerError, http.StatusText(http.StatusInternalServerError))
|
c.CustomAbort(http.StatusInternalServerError, http.StatusText(http.StatusInternalServerError))
|
||||||
}
|
}
|
||||||
|
|
||||||
if err := config.Load(); err != nil {
|
|
||||||
log.Errorf("failed to load configurations: %v", err)
|
|
||||||
c.CustomAbort(http.StatusInternalServerError, http.StatusText(http.StatusInternalServerError))
|
|
||||||
}
|
|
||||||
|
|
||||||
// Everything is ok, detect the configurations to confirm if the option we are caring is changed.
|
|
||||||
if err := watchConfigChanges(cfg); err != nil {
|
|
||||||
log.Errorf("Failed to watch configuration change with error: %s\n", err)
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Reset system configurations
|
func (c *ConfigAPI) validateCfg(cfgs map[string]interface{}) (bool, error) {
|
||||||
func (c *ConfigAPI) Reset() {
|
mode := c.cfgManager.Get("auth_mode").GetString()
|
||||||
if err := config.Reset(); err != nil {
|
if value, ok := cfgs[common.AUTHMode]; ok {
|
||||||
log.Errorf("failed to reset configurations: %v", err)
|
|
||||||
c.CustomAbort(http.StatusInternalServerError, http.StatusText(http.StatusInternalServerError))
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func validateCfg(c map[string]interface{}) (bool, error) {
|
|
||||||
strMap := map[string]string{}
|
|
||||||
for k := range common.HarborStringKeysMap {
|
|
||||||
if _, ok := c[k]; !ok {
|
|
||||||
continue
|
|
||||||
}
|
|
||||||
if _, ok := c[k].(string); !ok {
|
|
||||||
return false, fmt.Errorf("Invalid value type, expected string, key: %s, value: %v, type: %v", k, c[k], reflect.TypeOf(c[k]))
|
|
||||||
}
|
|
||||||
strMap[k] = c[k].(string)
|
|
||||||
}
|
|
||||||
numMap := map[string]int{}
|
|
||||||
for k := range common.HarborNumKeysMap {
|
|
||||||
if _, ok := c[k]; !ok {
|
|
||||||
continue
|
|
||||||
}
|
|
||||||
if _, ok := c[k].(float64); !ok {
|
|
||||||
return false, fmt.Errorf("Invalid value type, expected float64, key: %s, value: %v, type: %v", k, c[k], reflect.TypeOf(c[k]))
|
|
||||||
}
|
|
||||||
numMap[k] = int(c[k].(float64))
|
|
||||||
}
|
|
||||||
boolMap := map[string]bool{}
|
|
||||||
for k := range common.HarborBoolKeysMap {
|
|
||||||
if _, ok := c[k]; !ok {
|
|
||||||
continue
|
|
||||||
}
|
|
||||||
if _, ok := c[k].(bool); !ok {
|
|
||||||
return false, fmt.Errorf("Invalid value type, expected bool, key: %s, value: %v, type: %v", k, c[k], reflect.TypeOf(c[k]))
|
|
||||||
}
|
|
||||||
boolMap[k] = c[k].(bool)
|
|
||||||
}
|
|
||||||
|
|
||||||
mode, err := config.AuthMode()
|
|
||||||
if err != nil {
|
|
||||||
return true, err
|
|
||||||
}
|
|
||||||
|
|
||||||
if value, ok := strMap[common.AUTHMode]; ok {
|
|
||||||
if value != common.DBAuth && value != common.LDAPAuth && value != common.UAAAuth {
|
|
||||||
return false, fmt.Errorf("invalid %s, shoud be one of %s, %s, %s", common.AUTHMode, common.DBAuth, common.LDAPAuth, common.UAAAuth)
|
|
||||||
}
|
|
||||||
flag, err := authModeCanBeModified()
|
flag, err := authModeCanBeModified()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return true, err
|
return true, err
|
||||||
}
|
}
|
||||||
if mode != value && !flag {
|
if mode != fmt.Sprintf("%v", value) && !flag {
|
||||||
return false, fmt.Errorf("%s can not be modified as new users have been inserted into database", common.AUTHMode)
|
return false, fmt.Errorf("%s can not be modified as new users have been inserted into database", common.AUTHMode)
|
||||||
}
|
}
|
||||||
mode = value
|
|
||||||
}
|
}
|
||||||
|
err := c.cfgManager.ValidateCfg(cfgs)
|
||||||
if mode == common.LDAPAuth {
|
if err != nil {
|
||||||
ldapConf, err := config.LDAPConf()
|
return false, err
|
||||||
if err != nil {
|
|
||||||
return true, err
|
|
||||||
}
|
|
||||||
|
|
||||||
if len(ldapConf.LdapURL) == 0 {
|
|
||||||
if _, ok := strMap[common.LDAPURL]; !ok {
|
|
||||||
return false, fmt.Errorf("%s is missing", common.LDAPURL)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if len(ldapConf.LdapBaseDn) == 0 {
|
|
||||||
if _, ok := strMap[common.LDAPBaseDN]; !ok {
|
|
||||||
return false, fmt.Errorf("%s is missing", common.LDAPBaseDN)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if len(ldapConf.LdapUID) == 0 {
|
|
||||||
if _, ok := strMap[common.LDAPUID]; !ok {
|
|
||||||
return false, fmt.Errorf("%s is missing", common.LDAPUID)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if ldapConf.LdapScope == 0 {
|
|
||||||
if _, ok := numMap[common.LDAPScope]; !ok {
|
|
||||||
return false, fmt.Errorf("%s is missing", common.LDAPScope)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if ldapURL, ok := strMap[common.LDAPURL]; ok && len(ldapURL) == 0 {
|
|
||||||
return false, fmt.Errorf("%s is empty", common.LDAPURL)
|
|
||||||
}
|
|
||||||
if baseDN, ok := strMap[common.LDAPBaseDN]; ok && len(baseDN) == 0 {
|
|
||||||
return false, fmt.Errorf("%s is empty", common.LDAPBaseDN)
|
|
||||||
}
|
|
||||||
if uID, ok := strMap[common.LDAPUID]; ok && len(uID) == 0 {
|
|
||||||
return false, fmt.Errorf("%s is empty", common.LDAPUID)
|
|
||||||
}
|
|
||||||
if scope, ok := numMap[common.LDAPScope]; ok &&
|
|
||||||
scope != common.LDAPScopeBase &&
|
|
||||||
scope != common.LDAPScopeOnelevel &&
|
|
||||||
scope != common.LDAPScopeSubtree {
|
|
||||||
return false, fmt.Errorf("invalid %s, should be %d, %d or %d",
|
|
||||||
common.LDAPScope,
|
|
||||||
common.LDAPScopeBase,
|
|
||||||
common.LDAPScopeOnelevel,
|
|
||||||
common.LDAPScopeSubtree)
|
|
||||||
}
|
|
||||||
for k, n := range numMap {
|
|
||||||
if n < 0 {
|
|
||||||
return false, fmt.Errorf("invalid %s: %d", k, n)
|
|
||||||
}
|
|
||||||
if (k == common.EmailPort ||
|
|
||||||
k == common.PostGreSQLPort) && n > 65535 {
|
|
||||||
return false, fmt.Errorf("invalid %s: %d", k, n)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if crt, ok := strMap[common.ProjectCreationRestriction]; ok &&
|
|
||||||
crt != common.ProCrtRestrEveryone &&
|
|
||||||
crt != common.ProCrtRestrAdmOnly {
|
|
||||||
return false, fmt.Errorf("invalid %s, should be %s or %s",
|
|
||||||
common.ProjectCreationRestriction,
|
|
||||||
common.ProCrtRestrAdmOnly,
|
|
||||||
common.ProCrtRestrEveryone)
|
|
||||||
}
|
}
|
||||||
return false, nil
|
return false, nil
|
||||||
}
|
}
|
||||||
@ -256,9 +133,11 @@ func validateCfg(c map[string]interface{}) (bool, error) {
|
|||||||
func convertForGet(cfg map[string]interface{}) (map[string]*value, error) {
|
func convertForGet(cfg map[string]interface{}) (map[string]*value, error) {
|
||||||
result := map[string]*value{}
|
result := map[string]*value{}
|
||||||
|
|
||||||
for _, k := range common.HarborPasswordKeys {
|
mList := metadata.Instance().GetAll()
|
||||||
if _, ok := cfg[k]; ok {
|
|
||||||
delete(cfg, k)
|
for _, item := range mList {
|
||||||
|
if _, ok := item.ItemType.(*metadata.PasswordType); ok {
|
||||||
|
delete(cfg, item.Name)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -45,7 +45,7 @@ func TestGetConfig(t *testing.T) {
|
|||||||
if !assert.Equal(200, code, "the status code of getting configurations with admin user should be 200") {
|
if !assert.Equal(200, code, "the status code of getting configurations with admin user should be 200") {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
t.Logf("cfg: %+v", cfg)
|
||||||
mode := cfg[common.AUTHMode].Value.(string)
|
mode := cfg[common.AUTHMode].Value.(string)
|
||||||
assert.Equal(common.DBAuth, mode, fmt.Sprintf("the auth mode should be %s", common.DBAuth))
|
assert.Equal(common.DBAuth, mode, fmt.Sprintf("the auth mode should be %s", common.DBAuth))
|
||||||
ccc, err := config.GetSystemCfg()
|
ccc, err := config.GetSystemCfg()
|
||||||
@ -103,46 +103,6 @@ func TestPutConfig(t *testing.T) {
|
|||||||
t.Logf("%v", ccc)
|
t.Logf("%v", ccc)
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestResetConfig(t *testing.T) {
|
|
||||||
fmt.Println("Testing resetting configurations")
|
|
||||||
assert := assert.New(t)
|
|
||||||
apiTest := newHarborAPI()
|
|
||||||
|
|
||||||
code, err := apiTest.ResetConfig(*admin)
|
|
||||||
if err != nil {
|
|
||||||
t.Errorf("failed to get configurations: %v", err)
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
if !assert.Equal(200, code, "unexpected response code") {
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
code, cfgs, err := apiTest.GetConfig(*admin)
|
|
||||||
if err != nil {
|
|
||||||
t.Errorf("failed to get configurations: %v", err)
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
if !assert.Equal(200, code, "unexpected response code") {
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
value, ok := cfgs[common.TokenExpiration]
|
|
||||||
if !ok {
|
|
||||||
t.Errorf("%s not found", common.TokenExpiration)
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
assert.Equal(int(value.Value.(float64)), 30, "unexpected 30")
|
|
||||||
|
|
||||||
ccc, err := config.GetSystemCfg()
|
|
||||||
if err != nil {
|
|
||||||
t.Logf("failed to get system configurations: %v", err)
|
|
||||||
}
|
|
||||||
t.Logf("%v", ccc)
|
|
||||||
}
|
|
||||||
|
|
||||||
func TestPutConfigMaxLength(t *testing.T) {
|
func TestPutConfigMaxLength(t *testing.T) {
|
||||||
fmt.Println("Testing modifying configurations with max length.")
|
fmt.Println("Testing modifying configurations with max length.")
|
||||||
assert := assert.New(t)
|
assert := assert.New(t)
|
||||||
|
@ -106,7 +106,7 @@ func CommonDelTarget() {
|
|||||||
|
|
||||||
func CommonAddRepository() {
|
func CommonAddRepository() {
|
||||||
commonRepository := &models.RepoRecord{
|
commonRepository := &models.RepoRecord{
|
||||||
RepositoryID: 1,
|
RepositoryID: 31,
|
||||||
Name: TestRepoName,
|
Name: TestRepoName,
|
||||||
ProjectID: 1,
|
ProjectID: 1,
|
||||||
PullCount: 1,
|
PullCount: 1,
|
||||||
|
@ -27,10 +27,9 @@ import (
|
|||||||
"runtime"
|
"runtime"
|
||||||
"strconv"
|
"strconv"
|
||||||
|
|
||||||
"github.com/goharbor/harbor/src/common/dao"
|
|
||||||
"github.com/goharbor/harbor/src/common/job/test"
|
"github.com/goharbor/harbor/src/common/job/test"
|
||||||
"github.com/goharbor/harbor/src/common/models"
|
"github.com/goharbor/harbor/src/common/models"
|
||||||
"github.com/goharbor/harbor/src/common/utils"
|
testutils "github.com/goharbor/harbor/src/common/utils/test"
|
||||||
"github.com/goharbor/harbor/src/core/config"
|
"github.com/goharbor/harbor/src/core/config"
|
||||||
"github.com/goharbor/harbor/src/core/filter"
|
"github.com/goharbor/harbor/src/core/filter"
|
||||||
"github.com/goharbor/harbor/tests/apitests/apilib"
|
"github.com/goharbor/harbor/tests/apitests/apilib"
|
||||||
@ -41,7 +40,7 @@ import (
|
|||||||
"github.com/astaxie/beego"
|
"github.com/astaxie/beego"
|
||||||
"github.com/dghubble/sling"
|
"github.com/dghubble/sling"
|
||||||
|
|
||||||
// for test env prepare
|
"github.com/goharbor/harbor/src/common/dao"
|
||||||
_ "github.com/goharbor/harbor/src/core/auth/db"
|
_ "github.com/goharbor/harbor/src/core/auth/db"
|
||||||
_ "github.com/goharbor/harbor/src/core/auth/ldap"
|
_ "github.com/goharbor/harbor/src/core/auth/ldap"
|
||||||
"github.com/goharbor/harbor/src/replication/core"
|
"github.com/goharbor/harbor/src/replication/core"
|
||||||
@ -79,14 +78,14 @@ type usrInfo struct {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func init() {
|
func init() {
|
||||||
if err := config.Init(); err != nil {
|
config.Init()
|
||||||
log.Fatalf("failed to initialize configurations: %v", err)
|
testutils.InitDatabaseFromEnv()
|
||||||
}
|
dao.PrepareTestData([]string{"delete from harbor_user where user_id >2", "delete from project where owner_id >2"}, []string{})
|
||||||
database, err := config.Database()
|
config.Upload(testutils.GetUnitTestConfig())
|
||||||
if err != nil {
|
|
||||||
log.Fatalf("failed to get database configurations: %v", err)
|
allCfgs, _ := config.GetSystemCfg()
|
||||||
}
|
testutils.TraceCfgMap(allCfgs)
|
||||||
dao.InitDatabase(database)
|
|
||||||
_, file, _, _ := runtime.Caller(0)
|
_, file, _, _ := runtime.Caller(0)
|
||||||
dir := filepath.Dir(file)
|
dir := filepath.Dir(file)
|
||||||
dir = filepath.Join(dir, "..")
|
dir = filepath.Join(dir, "..")
|
||||||
@ -143,7 +142,6 @@ func init() {
|
|||||||
beego.Router("/api/ldap/groups/search", &LdapAPI{}, "get:SearchGroup")
|
beego.Router("/api/ldap/groups/search", &LdapAPI{}, "get:SearchGroup")
|
||||||
beego.Router("/api/ldap/users/import", &LdapAPI{}, "post:ImportUser")
|
beego.Router("/api/ldap/users/import", &LdapAPI{}, "post:ImportUser")
|
||||||
beego.Router("/api/configurations", &ConfigAPI{})
|
beego.Router("/api/configurations", &ConfigAPI{})
|
||||||
beego.Router("/api/configurations/reset", &ConfigAPI{}, "post:Reset")
|
|
||||||
beego.Router("/api/configs", &ConfigAPI{}, "get:GetInternalConfig")
|
beego.Router("/api/configs", &ConfigAPI{}, "get:GetInternalConfig")
|
||||||
beego.Router("/api/email/ping", &EmailAPI{}, "post:Ping")
|
beego.Router("/api/email/ping", &EmailAPI{}, "post:Ping")
|
||||||
beego.Router("/api/replications", &ReplicationAPI{})
|
beego.Router("/api/replications", &ReplicationAPI{})
|
||||||
@ -179,8 +177,6 @@ func init() {
|
|||||||
beego.Router("/api/chartrepo/:repo/charts/:name/:version/labels", chartLabelAPIType, "get:GetLabels;post:MarkLabel")
|
beego.Router("/api/chartrepo/:repo/charts/:name/:version/labels", chartLabelAPIType, "get:GetLabels;post:MarkLabel")
|
||||||
beego.Router("/api/chartrepo/:repo/charts/:name/:version/labels/:id([0-9]+)", chartLabelAPIType, "delete:RemoveLabel")
|
beego.Router("/api/chartrepo/:repo/charts/:name/:version/labels/:id([0-9]+)", chartLabelAPIType, "delete:RemoveLabel")
|
||||||
|
|
||||||
_ = updateInitPassword(1, "Harbor12345")
|
|
||||||
|
|
||||||
if err := core.Init(); err != nil {
|
if err := core.Init(); err != nil {
|
||||||
log.Fatalf("failed to initialize GlobalController: %v", err)
|
log.Fatalf("failed to initialize GlobalController: %v", err)
|
||||||
}
|
}
|
||||||
@ -1024,27 +1020,6 @@ func (a testapi) UsersDelete(userID int, authInfo usrInfo) (int, error) {
|
|||||||
httpStatusCode, _, err := request(_sling, jsonAcceptHeader, authInfo)
|
httpStatusCode, _, err := request(_sling, jsonAcceptHeader, authInfo)
|
||||||
return httpStatusCode, err
|
return httpStatusCode, err
|
||||||
}
|
}
|
||||||
func updateInitPassword(userID int, password string) error {
|
|
||||||
queryUser := models.User{UserID: userID}
|
|
||||||
user, err := dao.GetUser(queryUser)
|
|
||||||
if err != nil {
|
|
||||||
return fmt.Errorf("Failed to get user, userID: %d %v", userID, err)
|
|
||||||
}
|
|
||||||
if user == nil {
|
|
||||||
return fmt.Errorf("user id: %d does not exist", userID)
|
|
||||||
}
|
|
||||||
if user.Salt == "" {
|
|
||||||
user.Salt = utils.GenerateRandomString()
|
|
||||||
user.Password = password
|
|
||||||
err = dao.ChangeUserPassword(*user)
|
|
||||||
if err != nil {
|
|
||||||
return fmt.Errorf("Failed to update user encrypted password, userID: %d, err: %v", userID, err)
|
|
||||||
}
|
|
||||||
|
|
||||||
} else {
|
|
||||||
}
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
// Get system volume info
|
// Get system volume info
|
||||||
func (a testapi) VolumeInfoGet(authInfo usrInfo) (int, apilib.SystemInfo, error) {
|
func (a testapi) VolumeInfoGet(authInfo usrInfo) (int, apilib.SystemInfo, error) {
|
||||||
|
@ -71,6 +71,9 @@ type GCRep struct {
|
|||||||
|
|
||||||
// Valid validates the gc request
|
// Valid validates the gc request
|
||||||
func (gr *GCReq) Valid(v *validation.Validation) {
|
func (gr *GCReq) Valid(v *validation.Validation) {
|
||||||
|
if gr.Schedule == nil {
|
||||||
|
return
|
||||||
|
}
|
||||||
switch gr.Schedule.Type {
|
switch gr.Schedule.Type {
|
||||||
case ScheduleDaily, ScheduleWeekly:
|
case ScheduleDaily, ScheduleWeekly:
|
||||||
if gr.Schedule.Offtime < 0 || gr.Schedule.Offtime > 3600*24 {
|
if gr.Schedule.Offtime < 0 || gr.Schedule.Offtime > 3600*24 {
|
||||||
|
@ -14,7 +14,6 @@
|
|||||||
package models
|
package models
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"log"
|
|
||||||
"testing"
|
"testing"
|
||||||
|
|
||||||
"github.com/stretchr/testify/assert"
|
"github.com/stretchr/testify/assert"
|
||||||
@ -22,6 +21,9 @@ import (
|
|||||||
"github.com/goharbor/harbor/src/common"
|
"github.com/goharbor/harbor/src/common"
|
||||||
common_job "github.com/goharbor/harbor/src/common/job"
|
common_job "github.com/goharbor/harbor/src/common/job"
|
||||||
"github.com/goharbor/harbor/src/common/utils/test"
|
"github.com/goharbor/harbor/src/common/utils/test"
|
||||||
|
"github.com/goharbor/harbor/src/core/config"
|
||||||
|
"os"
|
||||||
|
"strings"
|
||||||
)
|
)
|
||||||
|
|
||||||
var adminServerTestConfig = map[string]interface{}{
|
var adminServerTestConfig = map[string]interface{}{
|
||||||
@ -29,11 +31,11 @@ var adminServerTestConfig = map[string]interface{}{
|
|||||||
}
|
}
|
||||||
|
|
||||||
func TestMain(m *testing.M) {
|
func TestMain(m *testing.M) {
|
||||||
server, err := test.NewAdminserver(adminServerTestConfig)
|
|
||||||
if err != nil {
|
test.InitDatabaseFromEnv()
|
||||||
log.Fatalf("failed to create a mock admin server: %v", err)
|
config.Init()
|
||||||
}
|
config.Upload(adminServerTestConfig)
|
||||||
defer server.Close()
|
os.Exit(m.Run())
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -126,5 +128,5 @@ func TestCronString(t *testing.T) {
|
|||||||
Schedule: schedule,
|
Schedule: schedule,
|
||||||
}
|
}
|
||||||
cronStr := adminjob.CronString()
|
cronStr := adminjob.CronString()
|
||||||
assert.Equal(t, cronStr, "{\"type\":\"Daily\",\"Weekday\":0,\"Offtime\":102}")
|
assert.True(t, strings.EqualFold(cronStr, "{\"type\":\"Daily\",\"Weekday\":0,\"Offtime\":102}"))
|
||||||
}
|
}
|
||||||
|
@ -16,14 +16,12 @@ package api
|
|||||||
import (
|
import (
|
||||||
"fmt"
|
"fmt"
|
||||||
"net/http"
|
"net/http"
|
||||||
"os"
|
|
||||||
"testing"
|
"testing"
|
||||||
|
|
||||||
"github.com/goharbor/harbor/src/core/config"
|
"github.com/goharbor/harbor/src/core/config"
|
||||||
|
|
||||||
"github.com/goharbor/harbor/src/common"
|
"github.com/goharbor/harbor/src/common"
|
||||||
"github.com/goharbor/harbor/src/common/models"
|
"github.com/goharbor/harbor/src/common/models"
|
||||||
"github.com/goharbor/harbor/src/common/utils/test"
|
|
||||||
"k8s.io/helm/cmd/helm/search"
|
"k8s.io/helm/cmd/helm/search"
|
||||||
|
|
||||||
"github.com/goharbor/harbor/src/common/dao"
|
"github.com/goharbor/harbor/src/common/dao"
|
||||||
@ -182,43 +180,30 @@ func TestSearch(t *testing.T) {
|
|||||||
_, exist = repositories["search-2/hello-world"]
|
_, exist = repositories["search-2/hello-world"]
|
||||||
assert.True(t, exist)
|
assert.True(t, exist)
|
||||||
|
|
||||||
currentAdminServerURL, ok := os.LookupEnv("ADMINSERVER_URL")
|
chartSettings := map[string]interface{}{
|
||||||
if ok {
|
common.WithChartMuseum: true,
|
||||||
chartSettings := map[string]interface{}{
|
|
||||||
common.WithChartMuseum: true,
|
|
||||||
}
|
|
||||||
adminServer, err := test.NewAdminserver(chartSettings)
|
|
||||||
if err != nil {
|
|
||||||
t.Fatal(nil)
|
|
||||||
}
|
|
||||||
defer adminServer.Close()
|
|
||||||
|
|
||||||
if err := config.InitByURL(adminServer.URL); err != nil {
|
|
||||||
t.Fatal(err)
|
|
||||||
}
|
|
||||||
defer func() {
|
|
||||||
// reset config
|
|
||||||
if err := config.InitByURL(currentAdminServerURL); err != nil {
|
|
||||||
t.Error(err)
|
|
||||||
}
|
|
||||||
}()
|
|
||||||
|
|
||||||
// Search chart
|
|
||||||
err = handleAndParse(&testingRequest{
|
|
||||||
method: http.MethodGet,
|
|
||||||
url: "/api/search",
|
|
||||||
queryStruct: struct {
|
|
||||||
Keyword string `url:"q"`
|
|
||||||
}{
|
|
||||||
Keyword: "harbor",
|
|
||||||
},
|
|
||||||
credential: sysAdmin,
|
|
||||||
}, result)
|
|
||||||
require.Nil(t, err)
|
|
||||||
require.Equal(t, 1, len(result.Chart))
|
|
||||||
require.Equal(t, "library/harbor", result.Chart[0].Name)
|
|
||||||
|
|
||||||
// Restore chart search handler
|
|
||||||
searchHandler = nil
|
|
||||||
}
|
}
|
||||||
|
config.InitWithSettings(chartSettings)
|
||||||
|
defer func() {
|
||||||
|
// reset config
|
||||||
|
config.Init()
|
||||||
|
}()
|
||||||
|
|
||||||
|
// Search chart
|
||||||
|
err = handleAndParse(&testingRequest{
|
||||||
|
method: http.MethodGet,
|
||||||
|
url: "/api/search",
|
||||||
|
queryStruct: struct {
|
||||||
|
Keyword string `url:"q"`
|
||||||
|
}{
|
||||||
|
Keyword: "harbor",
|
||||||
|
},
|
||||||
|
credential: sysAdmin,
|
||||||
|
}, result)
|
||||||
|
require.Nil(t, err)
|
||||||
|
require.Equal(t, 1, len(result.Chart))
|
||||||
|
require.Equal(t, "library/harbor", result.Chart[0].Name)
|
||||||
|
|
||||||
|
// Restore chart search handler
|
||||||
|
searchHandler = nil
|
||||||
}
|
}
|
||||||
|
@ -16,9 +16,10 @@ package api
|
|||||||
import (
|
import (
|
||||||
"encoding/json"
|
"encoding/json"
|
||||||
"fmt"
|
"fmt"
|
||||||
"testing"
|
"github.com/goharbor/harbor/src/common"
|
||||||
|
"github.com/goharbor/harbor/src/core/config"
|
||||||
"github.com/stretchr/testify/assert"
|
"github.com/stretchr/testify/assert"
|
||||||
|
"testing"
|
||||||
)
|
)
|
||||||
|
|
||||||
func TestGetVolumeInfo(t *testing.T) {
|
func TestGetVolumeInfo(t *testing.T) {
|
||||||
@ -53,6 +54,9 @@ func TestGetVolumeInfo(t *testing.T) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func TestGetGeneralInfo(t *testing.T) {
|
func TestGetGeneralInfo(t *testing.T) {
|
||||||
|
config.Upload(map[string]interface{}{
|
||||||
|
common.ReadOnly: false,
|
||||||
|
})
|
||||||
apiTest := newHarborAPI()
|
apiTest := newHarborAPI()
|
||||||
code, body, err := apiTest.GetGeneralInfo()
|
code, body, err := apiTest.GetGeneralInfo()
|
||||||
assert := assert.New(t)
|
assert := assert.New(t)
|
||||||
|
@ -27,6 +27,8 @@ import (
|
|||||||
"github.com/stretchr/testify/assert"
|
"github.com/stretchr/testify/assert"
|
||||||
|
|
||||||
"github.com/astaxie/beego"
|
"github.com/astaxie/beego"
|
||||||
|
"github.com/goharbor/harbor/src/common"
|
||||||
|
"github.com/goharbor/harbor/src/core/config"
|
||||||
)
|
)
|
||||||
|
|
||||||
var testUser0002ID, testUser0003ID int
|
var testUser0002ID, testUser0003ID int
|
||||||
@ -39,7 +41,9 @@ func TestUsersPost(t *testing.T) {
|
|||||||
|
|
||||||
assert := assert.New(t)
|
assert := assert.New(t)
|
||||||
apiTest := newHarborAPI()
|
apiTest := newHarborAPI()
|
||||||
|
config.Upload(map[string]interface{}{
|
||||||
|
common.AUTHMode: "db_auth",
|
||||||
|
})
|
||||||
// case 1: register a new user without admin auth, expect 400, because self registration is on
|
// case 1: register a new user without admin auth, expect 400, because self registration is on
|
||||||
fmt.Println("Register user without admin auth")
|
fmt.Println("Register user without admin auth")
|
||||||
code, err := apiTest.UsersPost(testUser0002)
|
code, err := apiTest.UsersPost(testUser0002)
|
||||||
|
@ -17,34 +17,12 @@ import (
|
|||||||
"testing"
|
"testing"
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
"github.com/goharbor/harbor/src/common"
|
|
||||||
"github.com/goharbor/harbor/src/common/models"
|
"github.com/goharbor/harbor/src/common/models"
|
||||||
"github.com/stretchr/testify/assert"
|
"github.com/stretchr/testify/assert"
|
||||||
)
|
)
|
||||||
|
|
||||||
var l = NewUserLock(2 * time.Second)
|
var l = NewUserLock(2 * time.Second)
|
||||||
|
|
||||||
var adminServerLdapTestConfig = map[string]interface{}{
|
|
||||||
common.ExtEndpoint: "host01.com",
|
|
||||||
common.AUTHMode: "ldap_auth",
|
|
||||||
common.DatabaseType: "postgresql",
|
|
||||||
common.PostGreSQLHOST: "127.0.0.1",
|
|
||||||
common.PostGreSQLPort: 5432,
|
|
||||||
common.PostGreSQLUsername: "postgres",
|
|
||||||
common.PostGreSQLPassword: "root123",
|
|
||||||
common.PostGreSQLDatabase: "registry",
|
|
||||||
common.LDAPURL: "ldap://127.0.0.1",
|
|
||||||
common.LDAPSearchDN: "cn=admin,dc=example,dc=com",
|
|
||||||
common.LDAPSearchPwd: "admin",
|
|
||||||
common.LDAPBaseDN: "dc=example,dc=com",
|
|
||||||
common.LDAPUID: "uid",
|
|
||||||
common.LDAPFilter: "",
|
|
||||||
common.LDAPScope: 3,
|
|
||||||
common.LDAPTimeout: 30,
|
|
||||||
common.CfgExpiration: 5,
|
|
||||||
common.AdminInitialPassword: "password",
|
|
||||||
}
|
|
||||||
|
|
||||||
func TestLock(t *testing.T) {
|
func TestLock(t *testing.T) {
|
||||||
t.Log("Locking john")
|
t.Log("Locking john")
|
||||||
l.Lock("john")
|
l.Lock("john")
|
||||||
|
@ -25,6 +25,7 @@ import (
|
|||||||
"github.com/goharbor/harbor/src/common/models"
|
"github.com/goharbor/harbor/src/common/models"
|
||||||
"github.com/goharbor/harbor/src/common/utils/ldap"
|
"github.com/goharbor/harbor/src/common/utils/ldap"
|
||||||
"github.com/goharbor/harbor/src/core/auth"
|
"github.com/goharbor/harbor/src/core/auth"
|
||||||
|
"github.com/goharbor/harbor/src/core/config"
|
||||||
coreConfig "github.com/goharbor/harbor/src/core/config"
|
coreConfig "github.com/goharbor/harbor/src/core/config"
|
||||||
)
|
)
|
||||||
|
|
||||||
@ -65,18 +66,9 @@ var adminServerTestConfig = map[string]interface{}{
|
|||||||
}
|
}
|
||||||
|
|
||||||
func TestMain(m *testing.M) {
|
func TestMain(m *testing.M) {
|
||||||
server, err := test.NewAdminserver(adminServerTestConfig)
|
test.InitDatabaseFromEnv()
|
||||||
if err != nil {
|
|
||||||
log.Fatalf("failed to create a mock admin server: %v", err)
|
|
||||||
}
|
|
||||||
defer server.Close()
|
|
||||||
|
|
||||||
if err := os.Setenv("ADMINSERVER_URL", server.URL); err != nil {
|
|
||||||
log.Fatalf("failed to set env %s: %v", "ADMINSERVER_URL", err)
|
|
||||||
}
|
|
||||||
|
|
||||||
secretKeyPath := "/tmp/secretkey"
|
secretKeyPath := "/tmp/secretkey"
|
||||||
_, err = test.GenerateKey(secretKeyPath)
|
_, err := test.GenerateKey(secretKeyPath)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
log.Fatalf("failed to generate secret key: %v", err)
|
log.Fatalf("failed to generate secret key: %v", err)
|
||||||
return
|
return
|
||||||
@ -91,14 +83,9 @@ func TestMain(m *testing.M) {
|
|||||||
log.Fatalf("failed to initialize configurations: %v", err)
|
log.Fatalf("failed to initialize configurations: %v", err)
|
||||||
}
|
}
|
||||||
|
|
||||||
database, err := coreConfig.Database()
|
config.Upload(adminServerTestConfig)
|
||||||
if err != nil {
|
retCode := m.Run()
|
||||||
log.Fatalf("failed to get database configuration: %v", err)
|
os.Exit(retCode)
|
||||||
}
|
|
||||||
|
|
||||||
if err := dao.InitDatabase(database); err != nil {
|
|
||||||
log.Fatalf("failed to initialize database: %v", err)
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestSearchUser(t *testing.T) {
|
func TestSearchUser(t *testing.T) {
|
||||||
|
@ -74,18 +74,11 @@ var adminServerLdapTestConfig = map[string]interface{}{
|
|||||||
}
|
}
|
||||||
|
|
||||||
func TestMain(m *testing.M) {
|
func TestMain(m *testing.M) {
|
||||||
server, err := test.NewAdminserver(adminServerLdapTestConfig)
|
test.InitDatabaseFromEnv()
|
||||||
if err != nil {
|
coreConfig.InitWithSettings(adminServerLdapTestConfig)
|
||||||
log.Fatalf("failed to create a mock admin server: %v", err)
|
|
||||||
}
|
|
||||||
defer server.Close()
|
|
||||||
|
|
||||||
if err := os.Setenv("ADMINSERVER_URL", server.URL); err != nil {
|
|
||||||
log.Fatalf("failed to set env %s: %v", "ADMINSERVER_URL", err)
|
|
||||||
}
|
|
||||||
|
|
||||||
secretKeyPath := "/tmp/secretkey"
|
secretKeyPath := "/tmp/secretkey"
|
||||||
_, err = test.GenerateKey(secretKeyPath)
|
_, err := test.GenerateKey(secretKeyPath)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
log.Errorf("failed to generate secret key: %v", err)
|
log.Errorf("failed to generate secret key: %v", err)
|
||||||
return
|
return
|
||||||
@ -96,19 +89,6 @@ func TestMain(m *testing.M) {
|
|||||||
log.Fatalf("failed to set env %s: %v", "KEY_PATH", err)
|
log.Fatalf("failed to set env %s: %v", "KEY_PATH", err)
|
||||||
}
|
}
|
||||||
|
|
||||||
if err := coreConfig.Init(); err != nil {
|
|
||||||
log.Fatalf("failed to initialize configurations: %v", err)
|
|
||||||
}
|
|
||||||
|
|
||||||
database, err := coreConfig.Database()
|
|
||||||
if err != nil {
|
|
||||||
log.Fatalf("failed to get database configuration: %v", err)
|
|
||||||
}
|
|
||||||
|
|
||||||
if err := dao.InitDatabase(database); err != nil {
|
|
||||||
log.Fatalf("failed to initialize database: %v", err)
|
|
||||||
}
|
|
||||||
|
|
||||||
// Extract to test utils
|
// Extract to test utils
|
||||||
initSqls := []string{
|
initSqls := []string{
|
||||||
"insert into harbor_user (username, email, password, realname) values ('member_test_01', 'member_test_01@example.com', '123456', 'member_test_01')",
|
"insert into harbor_user (username, email, password, realname) values ('member_test_01', 'member_test_01@example.com', '123456', 'member_test_01')",
|
||||||
|
@ -21,7 +21,6 @@ import (
|
|||||||
"github.com/goharbor/harbor/src/common/dao"
|
"github.com/goharbor/harbor/src/common/dao"
|
||||||
"github.com/goharbor/harbor/src/common/models"
|
"github.com/goharbor/harbor/src/common/models"
|
||||||
"github.com/goharbor/harbor/src/common/utils/test"
|
"github.com/goharbor/harbor/src/common/utils/test"
|
||||||
utilstest "github.com/goharbor/harbor/src/common/utils/test"
|
|
||||||
"github.com/goharbor/harbor/src/common/utils/uaa"
|
"github.com/goharbor/harbor/src/common/utils/uaa"
|
||||||
"github.com/goharbor/harbor/src/core/config"
|
"github.com/goharbor/harbor/src/core/config"
|
||||||
"github.com/stretchr/testify/assert"
|
"github.com/stretchr/testify/assert"
|
||||||
@ -29,16 +28,7 @@ import (
|
|||||||
|
|
||||||
func TestMain(m *testing.M) {
|
func TestMain(m *testing.M) {
|
||||||
test.InitDatabaseFromEnv()
|
test.InitDatabaseFromEnv()
|
||||||
server, err := utilstest.NewAdminserver(nil)
|
err := config.Init()
|
||||||
if err != nil {
|
|
||||||
panic(err)
|
|
||||||
}
|
|
||||||
defer server.Close()
|
|
||||||
|
|
||||||
if err := os.Setenv("ADMINSERVER_URL", server.URL); err != nil {
|
|
||||||
panic(err)
|
|
||||||
}
|
|
||||||
err = config.Init()
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
panic(err)
|
panic(err)
|
||||||
}
|
}
|
||||||
|
@ -12,6 +12,9 @@
|
|||||||
// See the License for the specific language governing permissions and
|
// See the License for the specific language governing permissions and
|
||||||
// limitations under the License.
|
// limitations under the License.
|
||||||
|
|
||||||
|
// Package config provide config for core api and other modules
|
||||||
|
// Before accessing user settings, need to call Load()
|
||||||
|
// For system settings, no need to call Load()
|
||||||
package config
|
package config
|
||||||
|
|
||||||
import (
|
import (
|
||||||
@ -23,15 +26,12 @@ import (
|
|||||||
"io/ioutil"
|
"io/ioutil"
|
||||||
"net/http"
|
"net/http"
|
||||||
"os"
|
"os"
|
||||||
"strconv"
|
|
||||||
"strings"
|
"strings"
|
||||||
|
|
||||||
"github.com/goharbor/harbor/src/adminserver/client"
|
|
||||||
"github.com/goharbor/harbor/src/common"
|
"github.com/goharbor/harbor/src/common"
|
||||||
comcfg "github.com/goharbor/harbor/src/common/config"
|
comcfg "github.com/goharbor/harbor/src/common/config"
|
||||||
"github.com/goharbor/harbor/src/common/models"
|
"github.com/goharbor/harbor/src/common/models"
|
||||||
"github.com/goharbor/harbor/src/common/secret"
|
"github.com/goharbor/harbor/src/common/secret"
|
||||||
"github.com/goharbor/harbor/src/common/utils"
|
|
||||||
"github.com/goharbor/harbor/src/common/utils/log"
|
"github.com/goharbor/harbor/src/common/utils/log"
|
||||||
"github.com/goharbor/harbor/src/core/promgr"
|
"github.com/goharbor/harbor/src/core/promgr"
|
||||||
"github.com/goharbor/harbor/src/core/promgr/pmsdriver"
|
"github.com/goharbor/harbor/src/core/promgr/pmsdriver"
|
||||||
@ -48,11 +48,8 @@ const (
|
|||||||
var (
|
var (
|
||||||
// SecretStore manages secrets
|
// SecretStore manages secrets
|
||||||
SecretStore *secret.Store
|
SecretStore *secret.Store
|
||||||
// AdminserverClient is a client for adminserver
|
|
||||||
AdminserverClient client.Client
|
|
||||||
// GlobalProjectMgr is initialized based on the deploy mode
|
// GlobalProjectMgr is initialized based on the deploy mode
|
||||||
GlobalProjectMgr promgr.ProjectManager
|
GlobalProjectMgr promgr.ProjectManager
|
||||||
mg *comcfg.Manager
|
|
||||||
keyProvider comcfg.KeyProvider
|
keyProvider comcfg.KeyProvider
|
||||||
// AdmiralClient is initialized only under integration deploy mode
|
// AdmiralClient is initialized only under integration deploy mode
|
||||||
// and can be passed to project manager as a parameter
|
// and can be passed to project manager as a parameter
|
||||||
@ -61,50 +58,35 @@ var (
|
|||||||
TokenReader admiral.TokenReader
|
TokenReader admiral.TokenReader
|
||||||
// defined as a var for testing.
|
// defined as a var for testing.
|
||||||
defaultCACertPath = "/etc/core/ca/ca.crt"
|
defaultCACertPath = "/etc/core/ca/ca.crt"
|
||||||
|
cfgMgr *comcfg.CfgManager
|
||||||
)
|
)
|
||||||
|
|
||||||
// Init configurations
|
// Init configurations
|
||||||
func Init() error {
|
func Init() error {
|
||||||
// init key provider
|
// init key provider
|
||||||
initKeyProvider()
|
initKeyProvider()
|
||||||
adminServerURL := os.Getenv("ADMINSERVER_URL")
|
|
||||||
if len(adminServerURL) == 0 {
|
|
||||||
adminServerURL = common.DefaultAdminserverEndpoint
|
|
||||||
}
|
|
||||||
|
|
||||||
return InitByURL(adminServerURL)
|
cfgMgr = comcfg.NewDBCfgManager()
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
// InitByURL Init configurations with given url
|
|
||||||
func InitByURL(adminServerURL string) error {
|
|
||||||
log.Infof("initializing client for adminserver %s ...", adminServerURL)
|
|
||||||
cfg := &client.Config{
|
|
||||||
Secret: CoreSecret(),
|
|
||||||
}
|
|
||||||
AdminserverClient = client.NewClient(adminServerURL, cfg)
|
|
||||||
if err := AdminserverClient.Ping(); err != nil {
|
|
||||||
return fmt.Errorf("failed to ping adminserver: %v", err)
|
|
||||||
}
|
|
||||||
|
|
||||||
mg = comcfg.NewManager(AdminserverClient, true)
|
|
||||||
|
|
||||||
if err := Load(); err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
|
|
||||||
|
log.Info("init secret store")
|
||||||
// init secret store
|
// init secret store
|
||||||
initSecretStore()
|
initSecretStore()
|
||||||
|
log.Info("init project manager based on deploy mode")
|
||||||
// init project manager based on deploy mode
|
// init project manager based on deploy mode
|
||||||
if err := initProjectManager(); err != nil {
|
if err := initProjectManager(); err != nil {
|
||||||
log.Errorf("Failed to initialise project manager, error: %v", err)
|
log.Errorf("Failed to initialise project manager, error: %v", err)
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// InitWithSettings init config with predefined configs
|
||||||
|
func InitWithSettings(cfgs map[string]interface{}) {
|
||||||
|
Init()
|
||||||
|
cfgMgr = comcfg.NewInMemoryManager()
|
||||||
|
cfgMgr.UpdateConfig(cfgs)
|
||||||
|
}
|
||||||
|
|
||||||
func initKeyProvider() {
|
func initKeyProvider() {
|
||||||
path := os.Getenv("KEY_PATH")
|
path := os.Getenv("KEY_PATH")
|
||||||
if len(path) == 0 {
|
if len(path) == 0 {
|
||||||
@ -163,34 +145,36 @@ func initProjectManager() error {
|
|||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// GetCfgManager return the current config manager
|
||||||
|
func GetCfgManager() *comcfg.CfgManager {
|
||||||
|
if cfgMgr == nil {
|
||||||
|
return comcfg.NewDBCfgManager()
|
||||||
|
}
|
||||||
|
return cfgMgr
|
||||||
|
}
|
||||||
|
|
||||||
// Load configurations
|
// Load configurations
|
||||||
func Load() error {
|
func Load() error {
|
||||||
_, err := mg.Load()
|
return cfgMgr.Load()
|
||||||
return err
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Reset configurations
|
// Upload save all system configurations
|
||||||
func Reset() error {
|
|
||||||
return mg.Reset()
|
|
||||||
}
|
|
||||||
|
|
||||||
// Upload uploads all system configurations to admin server
|
|
||||||
func Upload(cfg map[string]interface{}) error {
|
func Upload(cfg map[string]interface{}) error {
|
||||||
return mg.Upload(cfg)
|
return cfgMgr.UpdateConfig(cfg)
|
||||||
}
|
}
|
||||||
|
|
||||||
// GetSystemCfg returns the system configurations
|
// GetSystemCfg returns the system configurations
|
||||||
func GetSystemCfg() (map[string]interface{}, error) {
|
func GetSystemCfg() (map[string]interface{}, error) {
|
||||||
return mg.Load()
|
return cfgMgr.GetAll(), nil
|
||||||
}
|
}
|
||||||
|
|
||||||
// AuthMode ...
|
// AuthMode ...
|
||||||
func AuthMode() (string, error) {
|
func AuthMode() (string, error) {
|
||||||
cfg, err := mg.Get()
|
err := cfgMgr.Load()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return "", err
|
log.Errorf("failed to load config, error %v", err)
|
||||||
}
|
}
|
||||||
return utils.SafeCastString(cfg[common.AUTHMode]), nil
|
return cfgMgr.Get(common.AUTHMode).GetString(), nil
|
||||||
}
|
}
|
||||||
|
|
||||||
// TokenPrivateKeyPath returns the path to the key for signing token for registry
|
// TokenPrivateKeyPath returns the path to the key for signing token for registry
|
||||||
@ -204,84 +188,53 @@ func TokenPrivateKeyPath() string {
|
|||||||
|
|
||||||
// LDAPConf returns the setting of ldap server
|
// LDAPConf returns the setting of ldap server
|
||||||
func LDAPConf() (*models.LdapConf, error) {
|
func LDAPConf() (*models.LdapConf, error) {
|
||||||
cfg, err := mg.Get()
|
err := cfgMgr.Load()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
ldapConf := &models.LdapConf{}
|
return &models.LdapConf{
|
||||||
ldapConf.LdapURL = utils.SafeCastString(cfg[common.LDAPURL])
|
LdapURL: cfgMgr.Get(common.LDAPURL).GetString(),
|
||||||
ldapConf.LdapSearchDn = utils.SafeCastString(cfg[common.LDAPSearchDN])
|
LdapSearchDn: cfgMgr.Get(common.LDAPSearchDN).GetString(),
|
||||||
ldapConf.LdapSearchPassword = utils.SafeCastString(cfg[common.LDAPSearchPwd])
|
LdapSearchPassword: cfgMgr.Get(common.LDAPSearchPwd).GetString(),
|
||||||
ldapConf.LdapBaseDn = utils.SafeCastString(cfg[common.LDAPBaseDN])
|
LdapBaseDn: cfgMgr.Get(common.LDAPBaseDN).GetString(),
|
||||||
ldapConf.LdapUID = utils.SafeCastString(cfg[common.LDAPUID])
|
LdapUID: cfgMgr.Get(common.LDAPUID).GetString(),
|
||||||
ldapConf.LdapFilter = utils.SafeCastString(cfg[common.LDAPFilter])
|
LdapFilter: cfgMgr.Get(common.LDAPFilter).GetString(),
|
||||||
ldapConf.LdapScope = int(utils.SafeCastFloat64(cfg[common.LDAPScope]))
|
LdapScope: cfgMgr.Get(common.LDAPScope).GetInt(),
|
||||||
ldapConf.LdapConnectionTimeout = int(utils.SafeCastFloat64(cfg[common.LDAPTimeout]))
|
LdapConnectionTimeout: cfgMgr.Get(common.LDAPTimeout).GetInt(),
|
||||||
if cfg[common.LDAPVerifyCert] != nil {
|
LdapVerifyCert: cfgMgr.Get(common.LDAPVerifyCert).GetBool(),
|
||||||
ldapConf.LdapVerifyCert = cfg[common.LDAPVerifyCert].(bool)
|
}, nil
|
||||||
} else {
|
|
||||||
ldapConf.LdapVerifyCert = true
|
|
||||||
}
|
|
||||||
|
|
||||||
return ldapConf, nil
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// LDAPGroupConf returns the setting of ldap group search
|
// LDAPGroupConf returns the setting of ldap group search
|
||||||
func LDAPGroupConf() (*models.LdapGroupConf, error) {
|
func LDAPGroupConf() (*models.LdapGroupConf, error) {
|
||||||
|
err := cfgMgr.Load()
|
||||||
cfg, err := mg.Get()
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
return &models.LdapGroupConf{
|
||||||
ldapGroupConf := &models.LdapGroupConf{LdapGroupSearchScope: 2}
|
LdapGroupBaseDN: cfgMgr.Get(common.LDAPGroupBaseDN).GetString(),
|
||||||
if _, ok := cfg[common.LDAPGroupBaseDN]; ok {
|
LdapGroupFilter: cfgMgr.Get(common.LDAPGroupSearchFilter).GetString(),
|
||||||
ldapGroupConf.LdapGroupBaseDN = utils.SafeCastString(cfg[common.LDAPGroupBaseDN])
|
LdapGroupNameAttribute: cfgMgr.Get(common.LDAPGroupAttributeName).GetString(),
|
||||||
}
|
LdapGroupSearchScope: cfgMgr.Get(common.LDAPGroupSearchScope).GetInt(),
|
||||||
if _, ok := cfg[common.LDAPGroupSearchFilter]; ok {
|
LdapGroupAdminDN: cfgMgr.Get(common.LdapGroupAdminDn).GetString(),
|
||||||
ldapGroupConf.LdapGroupFilter = utils.SafeCastString(cfg[common.LDAPGroupSearchFilter])
|
}, nil
|
||||||
}
|
|
||||||
if _, ok := cfg[common.LDAPGroupAttributeName]; ok {
|
|
||||||
ldapGroupConf.LdapGroupNameAttribute = utils.SafeCastString(cfg[common.LDAPGroupAttributeName])
|
|
||||||
}
|
|
||||||
if _, ok := cfg[common.LDAPGroupSearchScope]; ok {
|
|
||||||
if scopeStr, ok := cfg[common.LDAPGroupSearchScope].(string); ok {
|
|
||||||
ldapGroupConf.LdapGroupSearchScope, err = strconv.Atoi(scopeStr)
|
|
||||||
}
|
|
||||||
if scopeFloat, ok := cfg[common.LDAPGroupSearchScope].(float64); ok {
|
|
||||||
ldapGroupConf.LdapGroupSearchScope = int(scopeFloat)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if _, ok := cfg[common.LdapGroupAdminDn]; ok {
|
|
||||||
ldapGroupConf.LdapGroupAdminDN = cfg[common.LdapGroupAdminDn].(string)
|
|
||||||
}
|
|
||||||
return ldapGroupConf, nil
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// TokenExpiration returns the token expiration time (in minute)
|
// TokenExpiration returns the token expiration time (in minute)
|
||||||
func TokenExpiration() (int, error) {
|
func TokenExpiration() (int, error) {
|
||||||
cfg, err := mg.Get()
|
return cfgMgr.Get(common.TokenExpiration).GetInt(), nil
|
||||||
if err != nil {
|
|
||||||
return 0, err
|
|
||||||
}
|
|
||||||
|
|
||||||
return int(utils.SafeCastFloat64(cfg[common.TokenExpiration])), nil
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// ExtEndpoint returns the external URL of Harbor: protocol://host:port
|
// ExtEndpoint returns the external URL of Harbor: protocol://host:port
|
||||||
func ExtEndpoint() (string, error) {
|
func ExtEndpoint() (string, error) {
|
||||||
cfg, err := mg.Get()
|
return cfgMgr.Get(common.ExtEndpoint).GetString(), nil
|
||||||
if err != nil {
|
|
||||||
return "", err
|
|
||||||
}
|
|
||||||
return utils.SafeCastString(cfg[common.ExtEndpoint]), nil
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// ExtURL returns the external URL: host:port
|
// ExtURL returns the external URL: host:port
|
||||||
func ExtURL() (string, error) {
|
func ExtURL() (string, error) {
|
||||||
endpoint, err := ExtEndpoint()
|
endpoint, err := ExtEndpoint()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return "", err
|
log.Errorf("failed to load config, error %v", err)
|
||||||
}
|
}
|
||||||
l := strings.Split(endpoint, "://")
|
l := strings.Split(endpoint, "://")
|
||||||
if len(l) > 0 {
|
if len(l) > 0 {
|
||||||
@ -297,44 +250,22 @@ func SecretKey() (string, error) {
|
|||||||
|
|
||||||
// SelfRegistration returns the enablement of self registration
|
// SelfRegistration returns the enablement of self registration
|
||||||
func SelfRegistration() (bool, error) {
|
func SelfRegistration() (bool, error) {
|
||||||
cfg, err := mg.Get()
|
return cfgMgr.Get(common.SelfRegistration).GetBool(), nil
|
||||||
if err != nil {
|
|
||||||
return false, err
|
|
||||||
}
|
|
||||||
return utils.SafeCastBool(cfg[common.SelfRegistration]), nil
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// RegistryURL ...
|
// RegistryURL ...
|
||||||
func RegistryURL() (string, error) {
|
func RegistryURL() (string, error) {
|
||||||
cfg, err := mg.Get()
|
return cfgMgr.Get(common.RegistryURL).GetString(), nil
|
||||||
if err != nil {
|
|
||||||
return "", err
|
|
||||||
}
|
|
||||||
return utils.SafeCastString(cfg[common.RegistryURL]), nil
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// InternalJobServiceURL returns jobservice URL for internal communication between Harbor containers
|
// InternalJobServiceURL returns jobservice URL for internal communication between Harbor containers
|
||||||
func InternalJobServiceURL() string {
|
func InternalJobServiceURL() string {
|
||||||
cfg, err := mg.Get()
|
return strings.TrimSuffix(cfgMgr.Get(common.JobServiceURL).GetString(), "/")
|
||||||
if err != nil {
|
|
||||||
log.Warningf("Failed to Get job service URL from backend, error: %v, will return default value.", err)
|
|
||||||
return common.DefaultJobserviceEndpoint
|
|
||||||
}
|
|
||||||
|
|
||||||
if cfg[common.JobServiceURL] == nil {
|
|
||||||
return common.DefaultJobserviceEndpoint
|
|
||||||
}
|
|
||||||
return strings.TrimSuffix(utils.SafeCastString(cfg[common.JobServiceURL]), "/")
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// InternalCoreURL returns the local harbor core url
|
// InternalCoreURL returns the local harbor core url
|
||||||
func InternalCoreURL() string {
|
func InternalCoreURL() string {
|
||||||
cfg, err := mg.Get()
|
return strings.TrimSuffix(cfgMgr.Get(common.CoreURL).GetString(), "/")
|
||||||
if err != nil {
|
|
||||||
log.Warningf("Failed to Get job service Core URL from backend, error: %v, will return default value.", err)
|
|
||||||
return common.DefaultCoreEndpoint
|
|
||||||
}
|
|
||||||
return strings.TrimSuffix(utils.SafeCastString(cfg[common.CoreURL]), "/")
|
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -346,72 +277,49 @@ func InternalTokenServiceEndpoint() string {
|
|||||||
// InternalNotaryEndpoint returns notary server endpoint for internal communication between Harbor containers
|
// InternalNotaryEndpoint returns notary server endpoint for internal communication between Harbor containers
|
||||||
// This is currently a conventional value and can be unaccessible when Harbor is not deployed with Notary.
|
// This is currently a conventional value and can be unaccessible when Harbor is not deployed with Notary.
|
||||||
func InternalNotaryEndpoint() string {
|
func InternalNotaryEndpoint() string {
|
||||||
cfg, err := mg.Get()
|
return cfgMgr.Get(common.NotaryURL).GetString()
|
||||||
if err != nil {
|
|
||||||
log.Warningf("Failed to get Notary endpoint from backend, error: %v, will use default value.", err)
|
|
||||||
return common.DefaultNotaryEndpoint
|
|
||||||
}
|
|
||||||
if cfg[common.NotaryURL] == nil {
|
|
||||||
return common.DefaultNotaryEndpoint
|
|
||||||
}
|
|
||||||
return utils.SafeCastString(cfg[common.NotaryURL])
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// InitialAdminPassword returns the initial password for administrator
|
// InitialAdminPassword returns the initial password for administrator
|
||||||
func InitialAdminPassword() (string, error) {
|
func InitialAdminPassword() (string, error) {
|
||||||
cfg, err := mg.Get()
|
return cfgMgr.Get(common.AdminInitialPassword).GetString(), nil
|
||||||
if err != nil {
|
|
||||||
return "", err
|
|
||||||
}
|
|
||||||
return utils.SafeCastString(cfg[common.AdminInitialPassword]), nil
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// OnlyAdminCreateProject returns the flag to restrict that only sys admin can create project
|
// OnlyAdminCreateProject returns the flag to restrict that only sys admin can create project
|
||||||
func OnlyAdminCreateProject() (bool, error) {
|
func OnlyAdminCreateProject() (bool, error) {
|
||||||
cfg, err := mg.Get()
|
return cfgMgr.Get(common.ProjectCreationRestriction).GetString() == common.ProCrtRestrAdmOnly, nil
|
||||||
if err != nil {
|
|
||||||
return true, err
|
|
||||||
}
|
|
||||||
return utils.SafeCastString(cfg[common.ProjectCreationRestriction]) == common.ProCrtRestrAdmOnly, nil
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Email returns email server settings
|
// Email returns email server settings
|
||||||
func Email() (*models.Email, error) {
|
func Email() (*models.Email, error) {
|
||||||
cfg, err := mg.Get()
|
err := cfgMgr.Load()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
return &models.Email{
|
||||||
email := &models.Email{}
|
Host: cfgMgr.Get(common.EmailHost).GetString(),
|
||||||
email.Host = utils.SafeCastString(cfg[common.EmailHost])
|
Port: cfgMgr.Get(common.EmailPort).GetInt(),
|
||||||
email.Port = int(utils.SafeCastFloat64(cfg[common.EmailPort]))
|
Username: cfgMgr.Get(common.EmailUsername).GetString(),
|
||||||
email.Username = utils.SafeCastString(cfg[common.EmailUsername])
|
Password: cfgMgr.Get(common.EmailPassword).GetString(),
|
||||||
email.Password = utils.SafeCastString(cfg[common.EmailPassword])
|
SSL: cfgMgr.Get(common.EmailSSL).GetBool(),
|
||||||
email.SSL = utils.SafeCastBool(cfg[common.EmailSSL])
|
From: cfgMgr.Get(common.EmailFrom).GetString(),
|
||||||
email.From = utils.SafeCastString(cfg[common.EmailFrom])
|
Identity: cfgMgr.Get(common.EmailIdentity).GetString(),
|
||||||
email.Identity = utils.SafeCastString(cfg[common.EmailIdentity])
|
Insecure: cfgMgr.Get(common.EmailInsecure).GetBool(),
|
||||||
email.Insecure = utils.SafeCastBool(cfg[common.EmailInsecure])
|
}, nil
|
||||||
|
|
||||||
return email, nil
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Database returns database settings
|
// Database returns database settings
|
||||||
func Database() (*models.Database, error) {
|
func Database() (*models.Database, error) {
|
||||||
cfg, err := mg.Get()
|
|
||||||
if err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
|
|
||||||
database := &models.Database{}
|
database := &models.Database{}
|
||||||
database.Type = cfg[common.DatabaseType].(string)
|
database.Type = cfgMgr.Get(common.DatabaseType).GetString()
|
||||||
|
postgresql := &models.PostGreSQL{
|
||||||
postgresql := &models.PostGreSQL{}
|
Host: cfgMgr.Get(common.PostGreSQLHOST).GetString(),
|
||||||
postgresql.Host = utils.SafeCastString(cfg[common.PostGreSQLHOST])
|
Port: cfgMgr.Get(common.PostGreSQLPort).GetInt(),
|
||||||
postgresql.Port = int(utils.SafeCastFloat64(cfg[common.PostGreSQLPort]))
|
Username: cfgMgr.Get(common.PostGreSQLUsername).GetString(),
|
||||||
postgresql.Username = utils.SafeCastString(cfg[common.PostGreSQLUsername])
|
Password: cfgMgr.Get(common.PostGreSQLPassword).GetString(),
|
||||||
postgresql.Password = utils.SafeCastString(cfg[common.PostGreSQLPassword])
|
Database: cfgMgr.Get(common.PostGreSQLDatabase).GetString(),
|
||||||
postgresql.Database = utils.SafeCastString(cfg[common.PostGreSQLDatabase])
|
SSLMode: cfgMgr.Get(common.PostGreSQLSSLMode).GetString(),
|
||||||
postgresql.SSLMode = utils.SafeCastString(cfg[common.PostGreSQLSSLMode])
|
}
|
||||||
database.PostGreSQL = postgresql
|
database.PostGreSQL = postgresql
|
||||||
|
|
||||||
return database, nil
|
return database, nil
|
||||||
@ -432,83 +340,45 @@ func JobserviceSecret() string {
|
|||||||
|
|
||||||
// WithNotary returns a bool value to indicate if Harbor's deployed with Notary
|
// WithNotary returns a bool value to indicate if Harbor's deployed with Notary
|
||||||
func WithNotary() bool {
|
func WithNotary() bool {
|
||||||
cfg, err := mg.Get()
|
return cfgMgr.Get(common.WithNotary).GetBool()
|
||||||
if err != nil {
|
|
||||||
log.Warningf("Failed to get configuration, will return WithNotary == false")
|
|
||||||
return false
|
|
||||||
}
|
|
||||||
return utils.SafeCastBool(cfg[common.WithNotary])
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// WithClair returns a bool value to indicate if Harbor's deployed with Clair
|
// WithClair returns a bool value to indicate if Harbor's deployed with Clair
|
||||||
func WithClair() bool {
|
func WithClair() bool {
|
||||||
cfg, err := mg.Get()
|
return cfgMgr.Get(common.WithClair).GetBool()
|
||||||
if err != nil {
|
|
||||||
log.Errorf("Failed to get configuration, will return WithClair == false")
|
|
||||||
return false
|
|
||||||
}
|
|
||||||
return utils.SafeCastBool(cfg[common.WithClair])
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// ClairEndpoint returns the end point of clair instance, by default it's the one deployed within Harbor.
|
// ClairEndpoint returns the end point of clair instance, by default it's the one deployed within Harbor.
|
||||||
func ClairEndpoint() string {
|
func ClairEndpoint() string {
|
||||||
cfg, err := mg.Get()
|
return cfgMgr.Get(common.ClairURL).GetString()
|
||||||
if err != nil {
|
|
||||||
log.Errorf("Failed to get configuration, use default clair endpoint")
|
|
||||||
return common.DefaultClairEndpoint
|
|
||||||
}
|
|
||||||
return utils.SafeCastString(cfg[common.ClairURL])
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// ClairDB return Clair db info
|
// ClairDB return Clair db info
|
||||||
func ClairDB() (*models.PostGreSQL, error) {
|
func ClairDB() (*models.PostGreSQL, error) {
|
||||||
cfg, err := mg.Get()
|
clairDB := &models.PostGreSQL{
|
||||||
if err != nil {
|
Host: cfgMgr.Get(common.ClairDBHost).GetString(),
|
||||||
log.Errorf("Failed to get configuration of Clair DB, Error detail %v", err)
|
Port: cfgMgr.Get(common.ClairDBPort).GetInt(),
|
||||||
return nil, err
|
Username: cfgMgr.Get(common.ClairDBUsername).GetString(),
|
||||||
|
Password: cfgMgr.Get(common.ClairDBPassword).GetString(),
|
||||||
|
Database: cfgMgr.Get(common.ClairDB).GetString(),
|
||||||
|
SSLMode: cfgMgr.Get(common.ClairDBSSLMode).GetString(),
|
||||||
}
|
}
|
||||||
clairDB := &models.PostGreSQL{}
|
|
||||||
clairDB.Host = utils.SafeCastString(cfg[common.ClairDBHost])
|
|
||||||
clairDB.Port = int(utils.SafeCastFloat64(cfg[common.ClairDBPort]))
|
|
||||||
clairDB.Username = utils.SafeCastString(cfg[common.ClairDBUsername])
|
|
||||||
clairDB.Password = utils.SafeCastString(cfg[common.ClairDBPassword])
|
|
||||||
clairDB.Database = utils.SafeCastString(cfg[common.ClairDB])
|
|
||||||
clairDB.SSLMode = utils.SafeCastString(cfg[common.ClairDBSSLMode])
|
|
||||||
return clairDB, nil
|
return clairDB, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
// 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.
|
||||||
func AdmiralEndpoint() string {
|
func AdmiralEndpoint() string {
|
||||||
cfg, err := mg.Get()
|
if cfgMgr.Get(common.AdmiralEndpoint).GetString() == "NA" {
|
||||||
if err != nil {
|
|
||||||
log.Errorf("Failed to get configuration, will return empty string as admiral's endpoint, error: %v", err)
|
|
||||||
return ""
|
return ""
|
||||||
}
|
}
|
||||||
|
return cfgMgr.Get(common.AdmiralEndpoint).GetString()
|
||||||
if e, ok := cfg[common.AdmiralEndpoint].(string); !ok || e == "NA" {
|
|
||||||
return ""
|
|
||||||
}
|
|
||||||
return utils.SafeCastString(cfg[common.AdmiralEndpoint])
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// ScanAllPolicy returns the policy which controls the scan all.
|
// ScanAllPolicy returns the policy which controls the scan all.
|
||||||
func ScanAllPolicy() models.ScanAllPolicy {
|
func ScanAllPolicy() models.ScanAllPolicy {
|
||||||
var res models.ScanAllPolicy
|
var res models.ScanAllPolicy
|
||||||
cfg, err := mg.Get()
|
log.Infof("Scan all policy %v", cfgMgr.Get(common.ScanAllPolicy).GetString())
|
||||||
if err != nil {
|
if err := json.Unmarshal([]byte(cfgMgr.Get(common.ScanAllPolicy).GetString()), &res); err != nil {
|
||||||
log.Errorf("Failed to get configuration, will return default scan all policy, error: %v", err)
|
|
||||||
return models.DefaultScanAllPolicy
|
|
||||||
}
|
|
||||||
v, ok := cfg[common.ScanAllPolicy]
|
|
||||||
if !ok {
|
|
||||||
return models.DefaultScanAllPolicy
|
|
||||||
}
|
|
||||||
b, err := json.Marshal(v)
|
|
||||||
if err != nil {
|
|
||||||
log.Errorf("Failed to Marshal the value in configuration for Scan All policy, error: %v, returning the default policy", err)
|
|
||||||
return models.DefaultScanAllPolicy
|
|
||||||
}
|
|
||||||
if err := json.Unmarshal(b, &res); err != nil {
|
|
||||||
log.Errorf("Failed to unmarshal the value in configuration for Scan All policy, error: %v, returning the default policy", err)
|
log.Errorf("Failed to unmarshal the value in configuration for Scan All policy, error: %v, returning the default policy", err)
|
||||||
return models.DefaultScanAllPolicy
|
return models.DefaultScanAllPolicy
|
||||||
}
|
}
|
||||||
@ -522,54 +392,36 @@ func WithAdmiral() bool {
|
|||||||
|
|
||||||
// UAASettings returns the UAASettings to access UAA service.
|
// UAASettings returns the UAASettings to access UAA service.
|
||||||
func UAASettings() (*models.UAASettings, error) {
|
func UAASettings() (*models.UAASettings, error) {
|
||||||
cfg, err := mg.Get()
|
err := cfgMgr.Load()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
us := &models.UAASettings{
|
us := &models.UAASettings{
|
||||||
Endpoint: utils.SafeCastString(cfg[common.UAAEndpoint]),
|
Endpoint: cfgMgr.Get(common.UAAEndpoint).GetString(),
|
||||||
ClientID: utils.SafeCastString(cfg[common.UAAClientID]),
|
ClientID: cfgMgr.Get(common.UAAClientID).GetString(),
|
||||||
ClientSecret: utils.SafeCastString(cfg[common.UAAClientSecret]),
|
ClientSecret: cfgMgr.Get(common.UAAClientSecret).GetString(),
|
||||||
VerifyCert: utils.SafeCastBool(cfg[common.UAAVerifyCert]),
|
VerifyCert: cfgMgr.Get(common.UAAVerifyCert).GetBool(),
|
||||||
}
|
}
|
||||||
return us, nil
|
return us, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
// ReadOnly returns a bool to indicates if Harbor is in read only mode.
|
// ReadOnly returns a bool to indicates if Harbor is in read only mode.
|
||||||
func ReadOnly() bool {
|
func ReadOnly() bool {
|
||||||
cfg, err := mg.Get()
|
return cfgMgr.Get(common.ReadOnly).GetBool()
|
||||||
if err != nil {
|
|
||||||
log.Errorf("Failed to get configuration, will return false as read only, error: %v", err)
|
|
||||||
return false
|
|
||||||
}
|
|
||||||
return utils.SafeCastBool(cfg[common.ReadOnly])
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// WithChartMuseum returns a bool to indicate if chartmuseum is deployed with Harbor.
|
// WithChartMuseum returns a bool to indicate if chartmuseum is deployed with Harbor.
|
||||||
func WithChartMuseum() bool {
|
func WithChartMuseum() bool {
|
||||||
cfg, err := mg.Get()
|
return cfgMgr.Get(common.WithChartMuseum).GetBool()
|
||||||
if err != nil {
|
|
||||||
log.Errorf("Failed to get 'with_chartmuseum' configuration with error: %s; return false as default", err.Error())
|
|
||||||
return false
|
|
||||||
}
|
|
||||||
|
|
||||||
return utils.SafeCastBool(cfg[common.WithChartMuseum])
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// GetChartMuseumEndpoint returns the endpoint of the chartmuseum service
|
// GetChartMuseumEndpoint returns the endpoint of the chartmuseum service
|
||||||
// otherwise an non nil error is returned
|
// otherwise an non nil error is returned
|
||||||
func GetChartMuseumEndpoint() (string, error) {
|
func GetChartMuseumEndpoint() (string, error) {
|
||||||
cfg, err := mg.Get()
|
chartEndpoint := strings.TrimSpace(cfgMgr.Get(common.ChartRepoURL).GetString())
|
||||||
if err != nil {
|
|
||||||
log.Errorf("Failed to get 'chart_repository_url' configuration with error: %s; return false as default", err.Error())
|
|
||||||
return "", err
|
|
||||||
}
|
|
||||||
|
|
||||||
chartEndpoint := strings.TrimSpace(utils.SafeCastString(cfg[common.ChartRepoURL]))
|
|
||||||
if len(chartEndpoint) == 0 {
|
if len(chartEndpoint) == 0 {
|
||||||
return "", errors.New("empty chartmuseum endpoint")
|
return "", errors.New("empty chartmuseum endpoint")
|
||||||
}
|
}
|
||||||
|
|
||||||
return chartEndpoint, nil
|
return chartEndpoint, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -14,35 +14,37 @@
|
|||||||
package config
|
package config
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"encoding/json"
|
||||||
"os"
|
"os"
|
||||||
"path"
|
"path"
|
||||||
"runtime"
|
"runtime"
|
||||||
"testing"
|
"testing"
|
||||||
|
|
||||||
|
"fmt"
|
||||||
"github.com/goharbor/harbor/src/common"
|
"github.com/goharbor/harbor/src/common"
|
||||||
|
"github.com/goharbor/harbor/src/common/dao"
|
||||||
|
"github.com/goharbor/harbor/src/common/models"
|
||||||
"github.com/goharbor/harbor/src/common/utils/test"
|
"github.com/goharbor/harbor/src/common/utils/test"
|
||||||
"github.com/stretchr/testify/assert"
|
"github.com/stretchr/testify/assert"
|
||||||
)
|
)
|
||||||
|
|
||||||
// test functions under package core/config
|
// test functions under package core/config
|
||||||
func TestConfig(t *testing.T) {
|
func TestConfig(t *testing.T) {
|
||||||
|
test.InitDatabaseFromEnv()
|
||||||
|
dao.PrepareTestData([]string{"delete from properties where k='scan_all_policy'"}, []string{})
|
||||||
defaultCACertPath = path.Join(currPath(), "test", "ca.crt")
|
defaultCACertPath = path.Join(currPath(), "test", "ca.crt")
|
||||||
c := map[string]interface{}{
|
c := map[string]interface{}{
|
||||||
common.AdmiralEndpoint: "http://www.vmware.com",
|
common.AdmiralEndpoint: "https://www.vmware.com",
|
||||||
|
common.WithClair: false,
|
||||||
|
common.WithChartMuseum: false,
|
||||||
|
common.WithNotary: false,
|
||||||
}
|
}
|
||||||
server, err := test.NewAdminserver(c)
|
Init()
|
||||||
if err != nil {
|
|
||||||
t.Fatalf("failed to create a mock admin server: %v", err)
|
|
||||||
}
|
|
||||||
defer server.Close()
|
|
||||||
|
|
||||||
if err := os.Setenv("ADMINSERVER_URL", server.URL); err != nil {
|
Upload(c)
|
||||||
t.Fatalf("failed to set env %s: %v", "ADMINSERVER_URL", err)
|
|
||||||
}
|
|
||||||
|
|
||||||
secretKeyPath := "/tmp/secretkey"
|
secretKeyPath := "/tmp/secretkey"
|
||||||
_, err = test.GenerateKey(secretKeyPath)
|
_, err := test.GenerateKey(secretKeyPath)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
t.Errorf("failed to generate secret key: %v", err)
|
t.Errorf("failed to generate secret key: %v", err)
|
||||||
return
|
return
|
||||||
@ -139,12 +141,14 @@ func TestConfig(t *testing.T) {
|
|||||||
if err != nil {
|
if err != nil {
|
||||||
t.Fatalf("failed to get clair DB %v", err)
|
t.Fatalf("failed to get clair DB %v", err)
|
||||||
}
|
}
|
||||||
adminServerDefaultConfig := test.GetDefaultConfigMap()
|
defaultConfig := test.GetDefaultConfigMap()
|
||||||
assert.Equal(adminServerDefaultConfig[common.ClairDB], clairDB.Database)
|
defaultConfig[common.AdmiralEndpoint] = "http://www.vmware.com"
|
||||||
assert.Equal(adminServerDefaultConfig[common.ClairDBUsername], clairDB.Username)
|
Upload(defaultConfig)
|
||||||
assert.Equal(adminServerDefaultConfig[common.ClairDBPassword], clairDB.Password)
|
assert.Equal(defaultConfig[common.ClairDB], clairDB.Database)
|
||||||
assert.Equal(adminServerDefaultConfig[common.ClairDBHost], clairDB.Host)
|
assert.Equal(defaultConfig[common.ClairDBUsername], clairDB.Username)
|
||||||
assert.Equal(adminServerDefaultConfig[common.ClairDBPort], clairDB.Port)
|
assert.Equal(defaultConfig[common.ClairDBPassword], clairDB.Password)
|
||||||
|
assert.Equal(defaultConfig[common.ClairDBHost], clairDB.Host)
|
||||||
|
assert.Equal(defaultConfig[common.ClairDBPort], clairDB.Port)
|
||||||
|
|
||||||
if InternalNotaryEndpoint() != "http://notary-server:4443" {
|
if InternalNotaryEndpoint() != "http://notary-server:4443" {
|
||||||
t.Errorf("Unexpected notary endpoint: %s", InternalNotaryEndpoint())
|
t.Errorf("Unexpected notary endpoint: %s", InternalNotaryEndpoint())
|
||||||
@ -173,11 +177,6 @@ func TestConfig(t *testing.T) {
|
|||||||
t.Errorf(`extURL should be "host01.com".`)
|
t.Errorf(`extURL should be "host01.com".`)
|
||||||
}
|
}
|
||||||
|
|
||||||
// reset configurations
|
|
||||||
if err = Reset(); err != nil {
|
|
||||||
t.Errorf("failed to reset configurations: %v", err)
|
|
||||||
return
|
|
||||||
}
|
|
||||||
mode, err = AuthMode()
|
mode, err = AuthMode()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
t.Fatalf("failed to get auth mode: %v", err)
|
t.Fatalf("failed to get auth mode: %v", err)
|
||||||
@ -214,3 +213,12 @@ func currPath() string {
|
|||||||
}
|
}
|
||||||
return path.Dir(f)
|
return path.Dir(f)
|
||||||
}
|
}
|
||||||
|
func TestConfigureValue_GetMap(t *testing.T) {
|
||||||
|
var policy models.ScanAllPolicy
|
||||||
|
value2 := `{"parameter":{"daily_time":0},"type":"daily"}`
|
||||||
|
err := json.Unmarshal([]byte(value2), &policy)
|
||||||
|
if err != nil {
|
||||||
|
t.Errorf("Failed with error %v", err)
|
||||||
|
}
|
||||||
|
fmt.Printf("%+v\n", policy)
|
||||||
|
}
|
||||||
|
@ -27,26 +27,13 @@ import (
|
|||||||
|
|
||||||
"github.com/astaxie/beego"
|
"github.com/astaxie/beego"
|
||||||
"github.com/goharbor/harbor/src/common"
|
"github.com/goharbor/harbor/src/common"
|
||||||
"github.com/goharbor/harbor/src/common/dao"
|
|
||||||
"github.com/goharbor/harbor/src/common/models"
|
"github.com/goharbor/harbor/src/common/models"
|
||||||
"github.com/goharbor/harbor/src/common/utils/test"
|
utilstest "github.com/goharbor/harbor/src/common/utils/test"
|
||||||
"github.com/goharbor/harbor/src/core/config"
|
"github.com/goharbor/harbor/src/core/config"
|
||||||
"github.com/goharbor/harbor/src/core/proxy"
|
"github.com/goharbor/harbor/src/core/proxy"
|
||||||
"github.com/stretchr/testify/assert"
|
"github.com/stretchr/testify/assert"
|
||||||
)
|
)
|
||||||
|
|
||||||
// const (
|
|
||||||
// adminName = "admin"
|
|
||||||
// adminPwd = "Harbor12345"
|
|
||||||
// )
|
|
||||||
|
|
||||||
// type usrInfo struct {
|
|
||||||
// Name string
|
|
||||||
// Passwd string
|
|
||||||
// }
|
|
||||||
|
|
||||||
// var admin *usrInfo
|
|
||||||
|
|
||||||
func init() {
|
func init() {
|
||||||
_, file, _, _ := runtime.Caller(0)
|
_, file, _, _ := runtime.Caller(0)
|
||||||
dir := filepath.Dir(file)
|
dir := filepath.Dir(file)
|
||||||
@ -67,13 +54,11 @@ func init() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func TestMain(m *testing.M) {
|
func TestMain(m *testing.M) {
|
||||||
|
utilstest.InitDatabaseFromEnv()
|
||||||
rc := m.Run()
|
rc := m.Run()
|
||||||
if rc != 0 {
|
if rc != 0 {
|
||||||
os.Exit(rc)
|
os.Exit(rc)
|
||||||
}
|
}
|
||||||
// Init user Info
|
|
||||||
// admin = &usrInfo{adminName, adminPwd}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// TestUserResettable
|
// TestUserResettable
|
||||||
@ -90,19 +75,7 @@ func TestUserResettable(t *testing.T) {
|
|||||||
common.CfgExpiration: 5,
|
common.CfgExpiration: 5,
|
||||||
common.TokenExpiration: 30,
|
common.TokenExpiration: 30,
|
||||||
}
|
}
|
||||||
DBAuthAdminsvr, err := test.NewAdminserver(DBAuthConfig)
|
config.InitWithSettings(LDAPAuthConfig)
|
||||||
if err != nil {
|
|
||||||
panic(err)
|
|
||||||
}
|
|
||||||
LDAPAuthAdminsvr, err := test.NewAdminserver(LDAPAuthConfig)
|
|
||||||
if err != nil {
|
|
||||||
panic(err)
|
|
||||||
}
|
|
||||||
defer DBAuthAdminsvr.Close()
|
|
||||||
defer LDAPAuthAdminsvr.Close()
|
|
||||||
if err := config.InitByURL(LDAPAuthAdminsvr.URL); err != nil {
|
|
||||||
panic(err)
|
|
||||||
}
|
|
||||||
u1 := &models.User{
|
u1 := &models.User{
|
||||||
UserID: 3,
|
UserID: 3,
|
||||||
Username: "daniel",
|
Username: "daniel",
|
||||||
@ -115,34 +88,16 @@ func TestUserResettable(t *testing.T) {
|
|||||||
}
|
}
|
||||||
assert.False(isUserResetable(u1))
|
assert.False(isUserResetable(u1))
|
||||||
assert.True(isUserResetable(u2))
|
assert.True(isUserResetable(u2))
|
||||||
if err := config.InitByURL(DBAuthAdminsvr.URL); err != nil {
|
config.InitWithSettings(DBAuthConfig)
|
||||||
panic(err)
|
|
||||||
}
|
|
||||||
assert.True(isUserResetable(u1))
|
assert.True(isUserResetable(u1))
|
||||||
}
|
}
|
||||||
|
|
||||||
// TestMain is a sample to run an endpoint test
|
// TestMain is a sample to run an endpoint test
|
||||||
func TestAll(t *testing.T) {
|
func TestAll(t *testing.T) {
|
||||||
if err := config.Init(); err != nil {
|
config.InitWithSettings(utilstest.GetUnitTestConfig())
|
||||||
panic(err)
|
proxy.Init()
|
||||||
}
|
|
||||||
if err := proxy.Init(); err != nil {
|
|
||||||
panic(err)
|
|
||||||
}
|
|
||||||
database, err := config.Database()
|
|
||||||
if err != nil {
|
|
||||||
panic(err)
|
|
||||||
}
|
|
||||||
if err := dao.InitDatabase(database); err != nil {
|
|
||||||
panic(err)
|
|
||||||
}
|
|
||||||
|
|
||||||
assert := assert.New(t)
|
assert := assert.New(t)
|
||||||
|
|
||||||
// v := url.Values{}
|
|
||||||
// v.Set("principal", "admin")
|
|
||||||
// v.Add("password", "Harbor12345")
|
|
||||||
|
|
||||||
r, _ := http.NewRequest("POST", "/c/login", nil)
|
r, _ := http.NewRequest("POST", "/c/login", nil)
|
||||||
w := httptest.NewRecorder()
|
w := httptest.NewRecorder()
|
||||||
beego.BeeApp.Handlers.ServeHTTP(w, r)
|
beego.BeeApp.Handlers.ServeHTTP(w, r)
|
||||||
|
@ -17,11 +17,9 @@ package filter
|
|||||||
import (
|
import (
|
||||||
"net/http"
|
"net/http"
|
||||||
"net/http/httptest"
|
"net/http/httptest"
|
||||||
"os"
|
|
||||||
"testing"
|
"testing"
|
||||||
|
|
||||||
"github.com/goharbor/harbor/src/common"
|
"github.com/goharbor/harbor/src/common"
|
||||||
utilstest "github.com/goharbor/harbor/src/common/utils/test"
|
|
||||||
"github.com/goharbor/harbor/src/core/config"
|
"github.com/goharbor/harbor/src/core/config"
|
||||||
"github.com/stretchr/testify/assert"
|
"github.com/stretchr/testify/assert"
|
||||||
)
|
)
|
||||||
@ -29,29 +27,9 @@ import (
|
|||||||
func TestReadonlyFilter(t *testing.T) {
|
func TestReadonlyFilter(t *testing.T) {
|
||||||
|
|
||||||
var defaultConfig = map[string]interface{}{
|
var defaultConfig = map[string]interface{}{
|
||||||
common.ExtEndpoint: "host01.com",
|
common.ReadOnly: true,
|
||||||
common.AUTHMode: "db_auth",
|
|
||||||
common.CfgExpiration: 5,
|
|
||||||
common.TokenExpiration: 30,
|
|
||||||
common.DatabaseType: "postgresql",
|
|
||||||
common.PostGreSQLDatabase: "registry",
|
|
||||||
common.PostGreSQLHOST: "127.0.0.1",
|
|
||||||
common.PostGreSQLPort: 5432,
|
|
||||||
common.PostGreSQLPassword: "root123",
|
|
||||||
common.PostGreSQLUsername: "postgres",
|
|
||||||
common.ReadOnly: true,
|
|
||||||
}
|
|
||||||
adminServer, err := utilstest.NewAdminserver(defaultConfig)
|
|
||||||
if err != nil {
|
|
||||||
panic(err)
|
|
||||||
}
|
|
||||||
defer adminServer.Close()
|
|
||||||
if err := os.Setenv("ADMINSERVER_URL", adminServer.URL); err != nil {
|
|
||||||
panic(err)
|
|
||||||
}
|
|
||||||
if err := config.Init(); err != nil {
|
|
||||||
panic(err)
|
|
||||||
}
|
}
|
||||||
|
config.Upload(defaultConfig)
|
||||||
|
|
||||||
assert := assert.New(t)
|
assert := assert.New(t)
|
||||||
req1, _ := http.NewRequest("DELETE", "http://127.0.0.1:5000/api/repositories/library/ubuntu", nil)
|
req1, _ := http.NewRequest("DELETE", "http://127.0.0.1:5000/api/repositories/library/ubuntu", nil)
|
||||||
|
@ -287,7 +287,6 @@ func (s *sessionReqCtxModifier) Modify(ctx *beegoctx.Context) bool {
|
|||||||
log.Info("can not get user information from session")
|
log.Info("can not get user information from session")
|
||||||
return false
|
return false
|
||||||
}
|
}
|
||||||
log.Debugf("Getting user %+v", user)
|
|
||||||
log.Debug("using local database project manager")
|
log.Debug("using local database project manager")
|
||||||
pm := config.GlobalProjectMgr
|
pm := config.GlobalProjectMgr
|
||||||
log.Debug("creating local database security context...")
|
log.Debug("creating local database security context...")
|
||||||
|
@ -28,12 +28,12 @@ import (
|
|||||||
"github.com/astaxie/beego"
|
"github.com/astaxie/beego"
|
||||||
beegoctx "github.com/astaxie/beego/context"
|
beegoctx "github.com/astaxie/beego/context"
|
||||||
"github.com/astaxie/beego/session"
|
"github.com/astaxie/beego/session"
|
||||||
"github.com/goharbor/harbor/src/common/dao"
|
|
||||||
"github.com/goharbor/harbor/src/common/models"
|
"github.com/goharbor/harbor/src/common/models"
|
||||||
commonsecret "github.com/goharbor/harbor/src/common/secret"
|
commonsecret "github.com/goharbor/harbor/src/common/secret"
|
||||||
"github.com/goharbor/harbor/src/common/security"
|
"github.com/goharbor/harbor/src/common/security"
|
||||||
"github.com/goharbor/harbor/src/common/security/local"
|
"github.com/goharbor/harbor/src/common/security/local"
|
||||||
"github.com/goharbor/harbor/src/common/security/secret"
|
"github.com/goharbor/harbor/src/common/security/secret"
|
||||||
|
"github.com/goharbor/harbor/src/common/utils/test"
|
||||||
_ "github.com/goharbor/harbor/src/core/auth/db"
|
_ "github.com/goharbor/harbor/src/core/auth/db"
|
||||||
_ "github.com/goharbor/harbor/src/core/auth/ldap"
|
_ "github.com/goharbor/harbor/src/core/auth/ldap"
|
||||||
"github.com/goharbor/harbor/src/core/config"
|
"github.com/goharbor/harbor/src/core/config"
|
||||||
@ -59,18 +59,10 @@ func TestMain(m *testing.M) {
|
|||||||
if err != nil {
|
if err != nil {
|
||||||
log.Fatalf("failed to create session manager: %v", err)
|
log.Fatalf("failed to create session manager: %v", err)
|
||||||
}
|
}
|
||||||
|
config.Init()
|
||||||
|
test.InitDatabaseFromEnv()
|
||||||
|
|
||||||
if err := config.Init(); err != nil {
|
config.Upload(test.GetUnitTestConfig())
|
||||||
log.Fatalf("failed to initialize configurations: %v", err)
|
|
||||||
}
|
|
||||||
database, err := config.Database()
|
|
||||||
if err != nil {
|
|
||||||
log.Fatalf("failed to get database configurations: %v", err)
|
|
||||||
}
|
|
||||||
if err = dao.InitDatabase(database); err != nil {
|
|
||||||
log.Fatalf("failed to initialize database: %v", err)
|
|
||||||
}
|
|
||||||
|
|
||||||
Init()
|
Init()
|
||||||
|
|
||||||
os.Exit(m.Run())
|
os.Exit(m.Run())
|
||||||
|
@ -93,9 +93,12 @@ func main() {
|
|||||||
if err != nil {
|
if err != nil {
|
||||||
log.Fatalf("failed to get database configuration: %v", err)
|
log.Fatalf("failed to get database configuration: %v", err)
|
||||||
}
|
}
|
||||||
if err := dao.InitDatabase(database); err != nil {
|
if err := dao.InitAndUpgradeDatabase(database); err != nil {
|
||||||
log.Fatalf("failed to initialize database: %v", err)
|
log.Fatalf("failed to initialize database: %v", err)
|
||||||
}
|
}
|
||||||
|
if err := config.Load(); err != nil {
|
||||||
|
log.Fatalf("failed to load config: %v", err)
|
||||||
|
}
|
||||||
|
|
||||||
password, err := config.InitialAdminPassword()
|
password, err := config.InitialAdminPassword()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
|
@ -16,11 +16,11 @@ package admiral
|
|||||||
|
|
||||||
import (
|
import (
|
||||||
"io/ioutil"
|
"io/ioutil"
|
||||||
"os"
|
|
||||||
"testing"
|
"testing"
|
||||||
|
|
||||||
"github.com/stretchr/testify/assert"
|
"github.com/stretchr/testify/assert"
|
||||||
"github.com/stretchr/testify/require"
|
"github.com/stretchr/testify/require"
|
||||||
|
"os"
|
||||||
)
|
)
|
||||||
|
|
||||||
func TestRawTokenReader(t *testing.T) {
|
func TestRawTokenReader(t *testing.T) {
|
||||||
@ -46,7 +46,7 @@ func TestFileTokenReader(t *testing.T) {
|
|||||||
|
|
||||||
// file exist
|
// file exist
|
||||||
path = "/tmp/exist_file"
|
path = "/tmp/exist_file"
|
||||||
err = ioutil.WriteFile(path, []byte("token"), 0x0666)
|
err = ioutil.WriteFile(path, []byte("token"), 0x0766)
|
||||||
require.Nil(t, err)
|
require.Nil(t, err)
|
||||||
defer os.Remove(path)
|
defer os.Remove(path)
|
||||||
|
|
||||||
|
@ -1,12 +1,10 @@
|
|||||||
package proxy
|
package proxy
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"github.com/goharbor/harbor/src/adminserver/client"
|
|
||||||
"github.com/goharbor/harbor/src/common"
|
"github.com/goharbor/harbor/src/common"
|
||||||
"github.com/goharbor/harbor/src/common/dao"
|
|
||||||
"github.com/goharbor/harbor/src/common/models"
|
"github.com/goharbor/harbor/src/common/models"
|
||||||
notarytest "github.com/goharbor/harbor/src/common/utils/notary/test"
|
notarytest "github.com/goharbor/harbor/src/common/utils/notary/test"
|
||||||
utilstest "github.com/goharbor/harbor/src/common/utils/test"
|
testutils "github.com/goharbor/harbor/src/common/utils/test"
|
||||||
"github.com/goharbor/harbor/src/core/config"
|
"github.com/goharbor/harbor/src/core/config"
|
||||||
"github.com/stretchr/testify/assert"
|
"github.com/stretchr/testify/assert"
|
||||||
"github.com/stretchr/testify/require"
|
"github.com/stretchr/testify/require"
|
||||||
@ -19,8 +17,6 @@ import (
|
|||||||
|
|
||||||
var endpoint = "10.117.4.142"
|
var endpoint = "10.117.4.142"
|
||||||
var notaryServer *httptest.Server
|
var notaryServer *httptest.Server
|
||||||
var adminServer *httptest.Server
|
|
||||||
var adminserverClient client.Client
|
|
||||||
|
|
||||||
var admiralEndpoint = "http://127.0.0.1:8282"
|
var admiralEndpoint = "http://127.0.0.1:8282"
|
||||||
var token = ""
|
var token = ""
|
||||||
@ -35,18 +31,7 @@ func TestMain(m *testing.M) {
|
|||||||
common.CfgExpiration: 5,
|
common.CfgExpiration: 5,
|
||||||
common.TokenExpiration: 30,
|
common.TokenExpiration: 30,
|
||||||
}
|
}
|
||||||
adminServer, err := utilstest.NewAdminserver(defaultConfig)
|
config.InitWithSettings(defaultConfig)
|
||||||
if err != nil {
|
|
||||||
panic(err)
|
|
||||||
}
|
|
||||||
defer adminServer.Close()
|
|
||||||
if err := os.Setenv("ADMINSERVER_URL", adminServer.URL); err != nil {
|
|
||||||
panic(err)
|
|
||||||
}
|
|
||||||
if err := config.Init(); err != nil {
|
|
||||||
panic(err)
|
|
||||||
}
|
|
||||||
adminserverClient = client.NewClient(adminServer.URL, nil)
|
|
||||||
result := m.Run()
|
result := m.Run()
|
||||||
if result != 0 {
|
if result != 0 {
|
||||||
os.Exit(result)
|
os.Exit(result)
|
||||||
@ -123,24 +108,13 @@ func TestPMSPolicyChecker(t *testing.T) {
|
|||||||
common.PostGreSQLPassword: "root123",
|
common.PostGreSQLPassword: "root123",
|
||||||
common.PostGreSQLDatabase: "registry",
|
common.PostGreSQLDatabase: "registry",
|
||||||
}
|
}
|
||||||
adminServer, err := utilstest.NewAdminserver(defaultConfigAdmiral)
|
|
||||||
if err != nil {
|
|
||||||
panic(err)
|
|
||||||
}
|
|
||||||
defer adminServer.Close()
|
|
||||||
if err := os.Setenv("ADMINSERVER_URL", adminServer.URL); err != nil {
|
|
||||||
panic(err)
|
|
||||||
}
|
|
||||||
if err := config.Init(); err != nil {
|
if err := config.Init(); err != nil {
|
||||||
panic(err)
|
panic(err)
|
||||||
}
|
}
|
||||||
database, err := config.Database()
|
testutils.InitDatabaseFromEnv()
|
||||||
if err != nil {
|
|
||||||
panic(err)
|
config.Upload(defaultConfigAdmiral)
|
||||||
}
|
|
||||||
if err := dao.InitDatabase(database); err != nil {
|
|
||||||
panic(err)
|
|
||||||
}
|
|
||||||
|
|
||||||
name := "project_for_test_get_sev_low"
|
name := "project_for_test_get_sev_low"
|
||||||
id, err := config.GlobalProjectMgr.Create(&models.Project{
|
id, err := config.GlobalProjectMgr.Create(&models.Project{
|
||||||
|
@ -103,9 +103,9 @@ func initRouters() {
|
|||||||
beego.Router("/api/targets/:id([0-9]+)/policies/", &api.TargetAPI{}, "get:ListPolicies")
|
beego.Router("/api/targets/:id([0-9]+)/policies/", &api.TargetAPI{}, "get:ListPolicies")
|
||||||
beego.Router("/api/targets/ping", &api.TargetAPI{}, "post:Ping")
|
beego.Router("/api/targets/ping", &api.TargetAPI{}, "post:Ping")
|
||||||
beego.Router("/api/logs", &api.LogAPI{})
|
beego.Router("/api/logs", &api.LogAPI{})
|
||||||
beego.Router("/api/configs", &api.ConfigAPI{}, "get:GetInternalConfig")
|
|
||||||
beego.Router("/api/configurations", &api.ConfigAPI{})
|
beego.Router("/api/internal/configurations", &api.ConfigAPI{}, "get:GetInternalConfig;put:Put")
|
||||||
beego.Router("/api/configurations/reset", &api.ConfigAPI{}, "post:Reset")
|
beego.Router("/api/configurations", &api.ConfigAPI{}, "get:Get;put:Put")
|
||||||
beego.Router("/api/statistics", &api.StatisticAPI{})
|
beego.Router("/api/statistics", &api.StatisticAPI{})
|
||||||
beego.Router("/api/replications", &api.ReplicationAPI{})
|
beego.Router("/api/replications", &api.ReplicationAPI{})
|
||||||
beego.Router("/api/labels", &api.LabelAPI{}, "post:Post;get:List")
|
beego.Router("/api/labels", &api.LabelAPI{}, "post:Post;get:List")
|
||||||
@ -118,7 +118,6 @@ func initRouters() {
|
|||||||
|
|
||||||
beego.Router("/api/internal/syncregistry", &api.InternalAPI{}, "post:SyncRegistry")
|
beego.Router("/api/internal/syncregistry", &api.InternalAPI{}, "post:SyncRegistry")
|
||||||
beego.Router("/api/internal/renameadmin", &api.InternalAPI{}, "post:RenameAdmin")
|
beego.Router("/api/internal/renameadmin", &api.InternalAPI{}, "post:RenameAdmin")
|
||||||
beego.Router("/api/internal/configurations", &api.ConfigAPI{}, "get:GetInternalConfig")
|
|
||||||
|
|
||||||
// external service that hosted on harbor process:
|
// external service that hosted on harbor process:
|
||||||
beego.Router("/service/notifications", ®istry.NotificationHandler{})
|
beego.Router("/service/notifications", ®istry.NotificationHandler{})
|
||||||
|
@ -128,7 +128,7 @@ func (n *NotificationHandler) Post() {
|
|||||||
if err != nil {
|
if err != nil {
|
||||||
log.Errorf("Failed to get last update from Clair DB, error: %v, the auto scan will be skipped.", err)
|
log.Errorf("Failed to get last update from Clair DB, error: %v, the auto scan will be skipped.", err)
|
||||||
} else if last == 0 {
|
} else if last == 0 {
|
||||||
log.Infof("The Vulnerability data is not ready in Clair DB, the auto scan will be skipped.")
|
log.Infof("The Vulnerability data is not ready in Clair DB, the auto scan will be skipped, error %v", err)
|
||||||
} else if err := coreutils.TriggerImageScan(repository, tag); err != nil {
|
} else if err := coreutils.TriggerImageScan(repository, tag); err != nil {
|
||||||
log.Warningf("Failed to scan image, repository: %s, tag: %s, error: %v", repository, tag, err)
|
log.Warningf("Failed to scan image, repository: %s, tag: %s, error: %v", repository, tag, err)
|
||||||
}
|
}
|
||||||
|
@ -1,21 +0,0 @@
|
|||||||
// Copyright Project Harbor Authors
|
|
||||||
//
|
|
||||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
|
||||||
// you may not use this file except in compliance with the License.
|
|
||||||
// You may obtain a copy of the License at
|
|
||||||
//
|
|
||||||
// http://www.apache.org/licenses/LICENSE-2.0
|
|
||||||
//
|
|
||||||
// Unless required by applicable law or agreed to in writing, software
|
|
||||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
|
||||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
||||||
// See the License for the specific language governing permissions and
|
|
||||||
// limitations under the License.
|
|
||||||
package service
|
|
||||||
|
|
||||||
import (
|
|
||||||
"testing"
|
|
||||||
)
|
|
||||||
|
|
||||||
func TestMain(m *testing.M) {
|
|
||||||
}
|
|
@ -14,7 +14,7 @@
|
|||||||
package token
|
package token
|
||||||
|
|
||||||
import (
|
import (
|
||||||
jwt "github.com/dgrijalva/jwt-go"
|
"github.com/dgrijalva/jwt-go"
|
||||||
"github.com/docker/distribution/registry/auth/token"
|
"github.com/docker/distribution/registry/auth/token"
|
||||||
"github.com/stretchr/testify/assert"
|
"github.com/stretchr/testify/assert"
|
||||||
|
|
||||||
@ -31,20 +31,10 @@ import (
|
|||||||
|
|
||||||
"github.com/goharbor/harbor/src/common/models"
|
"github.com/goharbor/harbor/src/common/models"
|
||||||
"github.com/goharbor/harbor/src/common/rbac"
|
"github.com/goharbor/harbor/src/common/rbac"
|
||||||
"github.com/goharbor/harbor/src/common/utils/test"
|
|
||||||
"github.com/goharbor/harbor/src/core/config"
|
"github.com/goharbor/harbor/src/core/config"
|
||||||
)
|
)
|
||||||
|
|
||||||
func TestMain(m *testing.M) {
|
func TestMain(m *testing.M) {
|
||||||
server, err := test.NewAdminserver(nil)
|
|
||||||
if err != nil {
|
|
||||||
panic(err)
|
|
||||||
}
|
|
||||||
defer server.Close()
|
|
||||||
|
|
||||||
if err := os.Setenv("ADMINSERVER_URL", server.URL); err != nil {
|
|
||||||
panic(err)
|
|
||||||
}
|
|
||||||
if err := config.Init(); err != nil {
|
if err := config.Init(); err != nil {
|
||||||
panic(err)
|
panic(err)
|
||||||
}
|
}
|
||||||
|
@ -129,7 +129,7 @@ func triggerImageScan(repository, tag, digest string, client job.Client) error {
|
|||||||
}
|
}
|
||||||
err = dao.SetScanJobUUID(id, uuid)
|
err = dao.SetScanJobUUID(id, uuid)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
log.Warningf("Failed to set UUID for scan job, ID: %d, repository: %s, tag: %s", id, repository, tag)
|
log.Warningf("Failed to set UUID for scan job, ID: %d, UUID: %v, repository: %s, tag: %s", id, uuid, repository, tag)
|
||||||
}
|
}
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
@ -22,8 +22,8 @@ import (
|
|||||||
"reflect"
|
"reflect"
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
"github.com/goharbor/harbor/src/adminserver/client"
|
|
||||||
"github.com/goharbor/harbor/src/common"
|
"github.com/goharbor/harbor/src/common"
|
||||||
|
comcfg "github.com/goharbor/harbor/src/common/config"
|
||||||
"github.com/goharbor/harbor/src/common/dao"
|
"github.com/goharbor/harbor/src/common/dao"
|
||||||
"github.com/goharbor/harbor/src/common/models"
|
"github.com/goharbor/harbor/src/common/models"
|
||||||
"github.com/goharbor/harbor/src/jobservice/config"
|
"github.com/goharbor/harbor/src/jobservice/config"
|
||||||
@ -59,15 +59,15 @@ type Context struct {
|
|||||||
properties map[string]interface{}
|
properties map[string]interface{}
|
||||||
|
|
||||||
// admin server client
|
// admin server client
|
||||||
adminClient client.Client
|
cfgMgr comcfg.CfgManager
|
||||||
}
|
}
|
||||||
|
|
||||||
// NewContext ...
|
// NewContext ...
|
||||||
func NewContext(sysCtx context.Context, adminClient client.Client) *Context {
|
func NewContext(sysCtx context.Context, cfgMgr *comcfg.CfgManager) *Context {
|
||||||
return &Context{
|
return &Context{
|
||||||
sysContext: sysCtx,
|
sysContext: sysCtx,
|
||||||
adminClient: adminClient,
|
cfgMgr: *cfgMgr,
|
||||||
properties: make(map[string]interface{}),
|
properties: make(map[string]interface{}),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -76,12 +76,11 @@ func (c *Context) Init() error {
|
|||||||
var (
|
var (
|
||||||
counter = 0
|
counter = 0
|
||||||
err error
|
err error
|
||||||
configs map[string]interface{}
|
|
||||||
)
|
)
|
||||||
|
|
||||||
for counter == 0 || err != nil {
|
for counter == 0 || err != nil {
|
||||||
counter++
|
counter++
|
||||||
configs, err = c.adminClient.GetCfgs()
|
err = c.cfgMgr.Load()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
logger.Errorf("Job context initialization error: %s\n", err.Error())
|
logger.Errorf("Job context initialization error: %s\n", err.Error())
|
||||||
if counter < maxRetryTimes {
|
if counter < maxRetryTimes {
|
||||||
@ -94,7 +93,7 @@ func (c *Context) Init() error {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
db := getDBFromConfig(configs)
|
db := c.cfgMgr.GetDatabaseCfg()
|
||||||
|
|
||||||
err = dao.InitDatabase(db)
|
err = dao.InitDatabase(db)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
@ -110,9 +109,9 @@ func (c *Context) Init() error {
|
|||||||
// This func will build the job execution context before running
|
// This func will build the job execution context before running
|
||||||
func (c *Context) Build(dep env.JobData) (env.JobContext, error) {
|
func (c *Context) Build(dep env.JobData) (env.JobContext, error) {
|
||||||
jContext := &Context{
|
jContext := &Context{
|
||||||
sysContext: c.sysContext,
|
sysContext: c.sysContext,
|
||||||
adminClient: c.adminClient,
|
cfgMgr: c.cfgMgr,
|
||||||
properties: make(map[string]interface{}),
|
properties: make(map[string]interface{}),
|
||||||
}
|
}
|
||||||
|
|
||||||
// Copy properties
|
// Copy properties
|
||||||
@ -122,8 +121,9 @@ func (c *Context) Build(dep env.JobData) (env.JobContext, error) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Refresh admin server properties
|
// Refresh config properties
|
||||||
props, err := c.adminClient.GetCfgs()
|
err := c.cfgMgr.Load()
|
||||||
|
props := c.cfgMgr.GetAll()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
@ -16,17 +16,13 @@ package gc
|
|||||||
|
|
||||||
import (
|
import (
|
||||||
"fmt"
|
"fmt"
|
||||||
"net/http"
|
|
||||||
"os"
|
"os"
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
"github.com/garyburd/redigo/redis"
|
"github.com/garyburd/redigo/redis"
|
||||||
"github.com/goharbor/harbor/src/common"
|
"github.com/goharbor/harbor/src/common"
|
||||||
common_http "github.com/goharbor/harbor/src/common/http"
|
"github.com/goharbor/harbor/src/common/config"
|
||||||
"github.com/goharbor/harbor/src/common/http/modifier/auth"
|
|
||||||
"github.com/goharbor/harbor/src/common/registryctl"
|
"github.com/goharbor/harbor/src/common/registryctl"
|
||||||
"github.com/goharbor/harbor/src/common/utils"
|
|
||||||
reg "github.com/goharbor/harbor/src/common/utils/registry"
|
|
||||||
"github.com/goharbor/harbor/src/jobservice/env"
|
"github.com/goharbor/harbor/src/jobservice/env"
|
||||||
"github.com/goharbor/harbor/src/jobservice/logger"
|
"github.com/goharbor/harbor/src/jobservice/logger"
|
||||||
"github.com/goharbor/harbor/src/registryctl/client"
|
"github.com/goharbor/harbor/src/registryctl/client"
|
||||||
@ -44,9 +40,8 @@ const (
|
|||||||
type GarbageCollector struct {
|
type GarbageCollector struct {
|
||||||
registryCtlClient client.Client
|
registryCtlClient client.Client
|
||||||
logger logger.Interface
|
logger logger.Interface
|
||||||
coreclient *common_http.Client
|
cfgMgr *config.CfgManager
|
||||||
CoreURL string
|
CoreURL string
|
||||||
insecure bool
|
|
||||||
redisURL string
|
redisURL string
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -102,40 +97,34 @@ func (gc *GarbageCollector) init(ctx env.JobContext, params map[string]interface
|
|||||||
registryctl.Init()
|
registryctl.Init()
|
||||||
gc.registryCtlClient = registryctl.RegistryCtlClient
|
gc.registryCtlClient = registryctl.RegistryCtlClient
|
||||||
gc.logger = ctx.GetLogger()
|
gc.logger = ctx.GetLogger()
|
||||||
cred := auth.NewSecretAuthorizer(os.Getenv("JOBSERVICE_SECRET"))
|
|
||||||
gc.insecure = false
|
|
||||||
gc.coreclient = common_http.NewClient(&http.Client{
|
|
||||||
Transport: reg.GetHTTPTransport(gc.insecure),
|
|
||||||
}, cred)
|
|
||||||
errTpl := "Failed to get required property: %s"
|
errTpl := "Failed to get required property: %s"
|
||||||
if v, ok := ctx.Get(common.CoreURL); ok && len(v.(string)) > 0 {
|
if v, ok := ctx.Get(common.CoreURL); ok && len(v.(string)) > 0 {
|
||||||
gc.CoreURL = v.(string)
|
gc.CoreURL = v.(string)
|
||||||
} else {
|
} else {
|
||||||
return fmt.Errorf(errTpl, common.CoreURL)
|
return fmt.Errorf(errTpl, common.CoreURL)
|
||||||
}
|
}
|
||||||
|
secret := os.Getenv("JOBSERVICE_SECRET")
|
||||||
|
configURL := gc.CoreURL + common.CoreConfigPath
|
||||||
|
gc.cfgMgr = config.NewRESTCfgManager(configURL, secret)
|
||||||
gc.redisURL = params["redis_url_reg"].(string)
|
gc.redisURL = params["redis_url_reg"].(string)
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func (gc *GarbageCollector) getReadOnly() (bool, error) {
|
func (gc *GarbageCollector) getReadOnly() (bool, error) {
|
||||||
cfgs := map[string]interface{}{}
|
|
||||||
if err := gc.coreclient.Get(fmt.Sprintf("%s/api/configs", gc.CoreURL), &cfgs); err != nil {
|
if err := gc.cfgMgr.Load(); err != nil {
|
||||||
return false, err
|
return false, err
|
||||||
}
|
}
|
||||||
return utils.SafeCastBool(cfgs[common.ReadOnly]), nil
|
return gc.cfgMgr.Get(common.ReadOnly).GetBool(), nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func (gc *GarbageCollector) setReadOnly(switcher bool) error {
|
func (gc *GarbageCollector) setReadOnly(switcher bool) error {
|
||||||
if err := gc.coreclient.Put(fmt.Sprintf("%s/api/configurations", gc.CoreURL), struct {
|
cfg := map[string]interface{}{
|
||||||
ReadOnly bool `json:"read_only"`
|
common.ReadOnly: switcher,
|
||||||
}{
|
|
||||||
ReadOnly: switcher,
|
|
||||||
}); err != nil {
|
|
||||||
gc.logger.Errorf("failed to send readonly request to %s: %v", gc.CoreURL, err)
|
|
||||||
return err
|
|
||||||
}
|
}
|
||||||
gc.logger.Info("the readonly request has been sent successfully")
|
gc.cfgMgr.UpdateConfig(cfg)
|
||||||
return nil
|
return gc.cfgMgr.Save()
|
||||||
}
|
}
|
||||||
|
|
||||||
// cleanCache is to clean the registry cache for GC.
|
// cleanCache is to clean the registry cache for GC.
|
||||||
|
@ -20,13 +20,15 @@ import (
|
|||||||
"flag"
|
"flag"
|
||||||
"fmt"
|
"fmt"
|
||||||
|
|
||||||
"github.com/goharbor/harbor/src/adminserver/client"
|
"github.com/goharbor/harbor/src/common"
|
||||||
|
comcfg "github.com/goharbor/harbor/src/common/config"
|
||||||
"github.com/goharbor/harbor/src/jobservice/config"
|
"github.com/goharbor/harbor/src/jobservice/config"
|
||||||
"github.com/goharbor/harbor/src/jobservice/env"
|
"github.com/goharbor/harbor/src/jobservice/env"
|
||||||
"github.com/goharbor/harbor/src/jobservice/job/impl"
|
"github.com/goharbor/harbor/src/jobservice/job/impl"
|
||||||
"github.com/goharbor/harbor/src/jobservice/logger"
|
"github.com/goharbor/harbor/src/jobservice/logger"
|
||||||
"github.com/goharbor/harbor/src/jobservice/runtime"
|
"github.com/goharbor/harbor/src/jobservice/runtime"
|
||||||
"github.com/goharbor/harbor/src/jobservice/utils"
|
"github.com/goharbor/harbor/src/jobservice/utils"
|
||||||
|
"os"
|
||||||
)
|
)
|
||||||
|
|
||||||
func main() {
|
func main() {
|
||||||
@ -60,9 +62,10 @@ func main() {
|
|||||||
if utils.IsEmptyStr(secret) {
|
if utils.IsEmptyStr(secret) {
|
||||||
return nil, errors.New("empty auth secret")
|
return nil, errors.New("empty auth secret")
|
||||||
}
|
}
|
||||||
|
coreURL := os.Getenv("CORE_URL")
|
||||||
adminClient := client.NewClient(config.GetAdminServerEndpoint(), &client.Config{Secret: secret})
|
configURL := coreURL + common.CoreConfigPath
|
||||||
jobCtx := impl.NewContext(ctx.SystemContext, adminClient)
|
cfgMgr := comcfg.NewRESTCfgManager(configURL, secret)
|
||||||
|
jobCtx := impl.NewContext(ctx.SystemContext, cfgMgr)
|
||||||
|
|
||||||
if err := jobCtx.Init(); err != nil {
|
if err := jobCtx.Init(); err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
|
@ -7,13 +7,27 @@ import { SharedModule } from "../../shared/shared.module";
|
|||||||
import { ErrorHandler } from '../../error-handler/error-handler';
|
import { ErrorHandler } from '../../error-handler/error-handler';
|
||||||
import { GcViewModelFactory } from './gc.viewmodel.factory';
|
import { GcViewModelFactory } from './gc.viewmodel.factory';
|
||||||
import { GcUtility } from './gc.utility';
|
import { GcUtility } from './gc.utility';
|
||||||
|
import { of } from 'rxjs';
|
||||||
|
|
||||||
describe('GcComponent', () => {
|
describe('GcComponent', () => {
|
||||||
let component: GcComponent;
|
let component: GcComponent;
|
||||||
let fixture: ComponentFixture<GcComponent>;
|
let fixture: ComponentFixture<GcComponent>;
|
||||||
|
let gcRepoService: GcRepoService;
|
||||||
let config: IServiceConfig = {
|
let config: IServiceConfig = {
|
||||||
systemInfoEndpoint: "/api/system/gc"
|
systemInfoEndpoint: "/api/system/gc"
|
||||||
};
|
};
|
||||||
|
let mockSchedule = [];
|
||||||
|
let mockJobs = [
|
||||||
|
{
|
||||||
|
id: 22222,
|
||||||
|
schedule: null,
|
||||||
|
job_status: 'string',
|
||||||
|
creation_time: new Date(),
|
||||||
|
update_time: new Date(),
|
||||||
|
}
|
||||||
|
];
|
||||||
|
let spySchedule: jasmine.Spy;
|
||||||
|
let spyJobs: jasmine.Spy;
|
||||||
beforeEach(async(() => {
|
beforeEach(async(() => {
|
||||||
TestBed.configureTestingModule({
|
TestBed.configureTestingModule({
|
||||||
imports: [
|
imports: [
|
||||||
@ -35,9 +49,12 @@ describe('GcComponent', () => {
|
|||||||
beforeEach(() => {
|
beforeEach(() => {
|
||||||
fixture = TestBed.createComponent(GcComponent);
|
fixture = TestBed.createComponent(GcComponent);
|
||||||
component = fixture.componentInstance;
|
component = fixture.componentInstance;
|
||||||
|
|
||||||
|
gcRepoService = fixture.debugElement.injector.get(GcRepoService);
|
||||||
|
spySchedule = spyOn(gcRepoService, "getSchedule").and.returnValues(of(mockSchedule));
|
||||||
|
spyJobs = spyOn(gcRepoService, "getJobs").and.returnValues(of(mockJobs));
|
||||||
fixture.detectChanges();
|
fixture.detectChanges();
|
||||||
});
|
});
|
||||||
|
|
||||||
it('should create', () => {
|
it('should create', () => {
|
||||||
expect(component).toBeTruthy();
|
expect(component).toBeTruthy();
|
||||||
});
|
});
|
||||||
|
@ -31,7 +31,7 @@ $size60:60px;
|
|||||||
}
|
}
|
||||||
|
|
||||||
.card-container {
|
.card-container {
|
||||||
margin-top: 21px;
|
margin-top: 40px;
|
||||||
.chart-card {
|
.chart-card {
|
||||||
width: 200px;
|
width: 200px;
|
||||||
margin: 10px;
|
margin: 10px;
|
||||||
|
@ -144,7 +144,7 @@
|
|||||||
<button type="button" class="btn btn-link"
|
<button type="button" class="btn btn-link"
|
||||||
(click)="versionDownload($event, item)">{{'HELM_CHART.DOWNLOAD' | translate}}</button>
|
(click)="versionDownload($event, item)">{{'HELM_CHART.DOWNLOAD' | translate}}</button>
|
||||||
<button type="button" class="btn btn-link"
|
<button type="button" class="btn btn-link"
|
||||||
[disabled]="selectedRows.length<=0 || !hasDeleteHelmChartVersionPermission"
|
[disabled]="!hasDeleteHelmChartVersionPermission"
|
||||||
(click)="deleteVersionCard($event, item)">{{'BUTTON.DELETE' | translate}}</button>
|
(click)="deleteVersionCard($event, item)">{{'BUTTON.DELETE' | translate}}</button>
|
||||||
</clr-dropdown>
|
</clr-dropdown>
|
||||||
</div>
|
</div>
|
||||||
|
@ -48,7 +48,7 @@ hbr-resource-label-signpost {
|
|||||||
}
|
}
|
||||||
|
|
||||||
.card-container {
|
.card-container {
|
||||||
margin-top: 21px;
|
margin-top: 40px;
|
||||||
.card-header {
|
.card-header {
|
||||||
.card-media-block {
|
.card-media-block {
|
||||||
img {
|
img {
|
||||||
|
@ -177,7 +177,7 @@ describe('RecentLogComponent (inline template)', () => {
|
|||||||
});
|
});
|
||||||
}));
|
}));
|
||||||
|
|
||||||
xit('should support refreshing', async(() => {
|
it('should support refreshing', async(() => {
|
||||||
fixture.detectChanges();
|
fixture.detectChanges();
|
||||||
|
|
||||||
fixture.whenStable().then(() => {
|
fixture.whenStable().then(() => {
|
||||||
|
@ -110,6 +110,9 @@ export class RecentLogComponent implements OnInit {
|
|||||||
}
|
}
|
||||||
|
|
||||||
load(state: State) {
|
load(state: State) {
|
||||||
|
if (!state || !state.page) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
// Keep it for future filter
|
// Keep it for future filter
|
||||||
this.currentState = state;
|
this.currentState = state;
|
||||||
|
|
||||||
@ -155,7 +158,6 @@ export class RecentLogComponent implements OnInit {
|
|||||||
this.recentLogs = doSorting<AccessLogItem>(this.recentLogs, state);
|
this.recentLogs = doSorting<AccessLogItem>(this.recentLogs, state);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
isMatched(terms: string, log: AccessLogItem): boolean {
|
isMatched(terms: string, log: AccessLogItem): boolean {
|
||||||
let reg = new RegExp('.*' + terms + '.*', 'i');
|
let reg = new RegExp('.*' + terms + '.*', 'i');
|
||||||
return reg.test(log.username) ||
|
return reg.test(log.username) ||
|
||||||
|
@ -23,7 +23,7 @@ import { PUSH_IMAGE_BUTTON_DIRECTIVES } from '../push-image/index';
|
|||||||
import { INLINE_ALERT_DIRECTIVES } from '../inline-alert/index';
|
import { INLINE_ALERT_DIRECTIVES } from '../inline-alert/index';
|
||||||
import { LabelPieceComponent } from "../label-piece/label-piece.component";
|
import { LabelPieceComponent } from "../label-piece/label-piece.component";
|
||||||
import { OperationService } from "../operation/operation.service";
|
import { OperationService } from "../operation/operation.service";
|
||||||
import {ProjectDefaultService, ProjectService, RetagDefaultService, RetagService} from "../service";
|
import { ProjectDefaultService, ProjectService, RetagDefaultService, RetagService } from "../service";
|
||||||
import { UserPermissionService, UserPermissionDefaultService } from "../service/permission.service";
|
import { UserPermissionService, UserPermissionDefaultService } from "../service/permission.service";
|
||||||
import { USERSTATICPERMISSION } from "../service/permission-static";
|
import { USERSTATICPERMISSION } from "../service/permission-static";
|
||||||
import { of } from "rxjs";
|
import { of } from "rxjs";
|
||||||
@ -70,11 +70,26 @@ describe('RepositoryComponentGridview (inline template)', () => {
|
|||||||
"tags_count": 1
|
"tags_count": 1
|
||||||
}
|
}
|
||||||
];
|
];
|
||||||
|
let mockRepoNginxData: RepositoryItem[] = [
|
||||||
|
{
|
||||||
|
"id": 2,
|
||||||
|
"name": "library/nginx",
|
||||||
|
"project_id": 1,
|
||||||
|
"description": "asdf",
|
||||||
|
"pull_count": 0,
|
||||||
|
"star_count": 0,
|
||||||
|
"tags_count": 1
|
||||||
|
}
|
||||||
|
];
|
||||||
|
|
||||||
let mockRepo: Repository = {
|
let mockRepo: Repository = {
|
||||||
metadata: {xTotalCount: 2},
|
metadata: { xTotalCount: 2 },
|
||||||
data: mockRepoData
|
data: mockRepoData
|
||||||
};
|
};
|
||||||
|
let mockNginxRepo: Repository = {
|
||||||
|
metadata: { xTotalCount: 2 },
|
||||||
|
data: mockRepoNginxData
|
||||||
|
};
|
||||||
let mockHasCreateRepositoryPermission: boolean = true;
|
let mockHasCreateRepositoryPermission: boolean = true;
|
||||||
let mockHasDeleteRepositoryPermission: boolean = true;
|
let mockHasDeleteRepositoryPermission: boolean = true;
|
||||||
// let mockTagData: Tag[] = [
|
// let mockTagData: Tag[] = [
|
||||||
@ -130,37 +145,52 @@ describe('RepositoryComponentGridview (inline template)', () => {
|
|||||||
});
|
});
|
||||||
}));
|
}));
|
||||||
|
|
||||||
beforeEach(async() => {
|
beforeEach(async () => {
|
||||||
fixtureRepo = TestBed.createComponent(RepositoryGridviewComponent);
|
fixtureRepo = TestBed.createComponent(RepositoryGridviewComponent);
|
||||||
compRepo = fixtureRepo.componentInstance;
|
compRepo = fixtureRepo.componentInstance;
|
||||||
compRepo.projectId = 1;
|
compRepo.projectId = 1;
|
||||||
|
compRepo.mode = '';
|
||||||
compRepo.hasProjectAdminRole = true;
|
compRepo.hasProjectAdminRole = true;
|
||||||
|
|
||||||
repositoryService = fixtureRepo.debugElement.injector.get(RepositoryService);
|
repositoryService = fixtureRepo.debugElement.injector.get(RepositoryService);
|
||||||
systemInfoService = fixtureRepo.debugElement.injector.get(SystemInfoService);
|
systemInfoService = fixtureRepo.debugElement.injector.get(SystemInfoService);
|
||||||
|
|
||||||
spyRepos = spyOn(repositoryService, 'getRepositories').and.returnValues(Promise.resolve(mockRepo));
|
|
||||||
spySystemInfo = spyOn(systemInfoService, 'getSystemInfo').and.returnValues(Promise.resolve(mockSystemInfo));
|
spySystemInfo = spyOn(systemInfoService, 'getSystemInfo').and.returnValues(Promise.resolve(mockSystemInfo));
|
||||||
|
spyRepos = spyOn(repositoryService, 'getRepositories')
|
||||||
|
.and.callFake(function (projectId: number, name: string) {
|
||||||
|
if (name === 'nginx') {
|
||||||
|
return Promise.resolve(mockNginxRepo);
|
||||||
|
}
|
||||||
|
return Promise.resolve(mockRepo);
|
||||||
|
});
|
||||||
userPermissionService = fixtureRepo.debugElement.injector.get(UserPermissionService);
|
userPermissionService = fixtureRepo.debugElement.injector.get(UserPermissionService);
|
||||||
spyOn(userPermissionService, "getPermission")
|
spyOn(userPermissionService, "getPermission")
|
||||||
.withArgs(compRepo.projectId,
|
.withArgs(compRepo.projectId,
|
||||||
USERSTATICPERMISSION.REPOSITORY.KEY, USERSTATICPERMISSION.REPOSITORY.VALUE.CREATE )
|
USERSTATICPERMISSION.REPOSITORY.KEY, USERSTATICPERMISSION.REPOSITORY.VALUE.CREATE)
|
||||||
.and.returnValue(of(mockHasCreateRepositoryPermission))
|
.and.returnValue(of(mockHasCreateRepositoryPermission))
|
||||||
.withArgs(compRepo.projectId, USERSTATICPERMISSION.REPOSITORY.KEY, USERSTATICPERMISSION.REPOSITORY.VALUE.DELETE )
|
.withArgs(compRepo.projectId, USERSTATICPERMISSION.REPOSITORY.KEY, USERSTATICPERMISSION.REPOSITORY.VALUE.DELETE)
|
||||||
.and.returnValue(of(mockHasDeleteRepositoryPermission));
|
.and.returnValue(of(mockHasDeleteRepositoryPermission));
|
||||||
fixtureRepo.detectChanges();
|
fixtureRepo.detectChanges();
|
||||||
});
|
});
|
||||||
it('should create', () => {
|
let originalTimeout;
|
||||||
expect(compRepo).toBeTruthy();
|
|
||||||
|
beforeEach(function () {
|
||||||
|
originalTimeout = jasmine.DEFAULT_TIMEOUT_INTERVAL;
|
||||||
|
jasmine.DEFAULT_TIMEOUT_INTERVAL = 100000;
|
||||||
});
|
});
|
||||||
|
|
||||||
// Will fail after upgrade to angular 6. todo: need to fix it.
|
afterEach(function () {
|
||||||
xit('should load and render data', async(() => {
|
jasmine.DEFAULT_TIMEOUT_INTERVAL = originalTimeout;
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should create', async(() => {
|
||||||
|
expect(compRepo).toBeTruthy();
|
||||||
|
}));
|
||||||
|
|
||||||
|
it('should load and render data', async(() => {
|
||||||
fixtureRepo.whenStable().then(() => {
|
fixtureRepo.whenStable().then(() => {
|
||||||
fixtureRepo.detectChanges();
|
fixtureRepo.detectChanges();
|
||||||
let deRepo: DebugElement = fixtureRepo.debugElement.query(By.css('.datagrid-cell'));
|
let deRepo: DebugElement = fixtureRepo.debugElement.query(del => del.classes['datagrid-cell']);
|
||||||
expect(deRepo).toBeTruthy();
|
expect(deRepo).toBeTruthy();
|
||||||
let elRepo: HTMLElement = deRepo.nativeElement;
|
let elRepo: HTMLElement = deRepo.nativeElement;
|
||||||
expect(elRepo).toBeTruthy();
|
expect(elRepo).toBeTruthy();
|
||||||
@ -173,13 +203,17 @@ describe('RepositoryComponentGridview (inline template)', () => {
|
|||||||
fixtureRepo.detectChanges();
|
fixtureRepo.detectChanges();
|
||||||
|
|
||||||
compRepo.doSearchRepoNames('nginx');
|
compRepo.doSearchRepoNames('nginx');
|
||||||
fixtureRepo.detectChanges();
|
fixtureRepo.whenStable().then(() => {
|
||||||
let de: DebugElement[] = fixtureRepo.debugElement.queryAll(By.css('.datagrid-cell'));
|
|
||||||
expect(de).toBeTruthy();
|
fixtureRepo.detectChanges();
|
||||||
expect(de.length).toEqual(1);
|
let de: DebugElement[] = fixtureRepo.debugElement.queryAll(By.css('.datagrid-cell'));
|
||||||
let el: HTMLElement = de[0].nativeElement;
|
expect(de).toBeTruthy();
|
||||||
expect(el).toBeTruthy();
|
expect(compRepo.repositories.length).toEqual(1);
|
||||||
expect(el.textContent).toEqual('library/nginx');
|
expect(de.length).toEqual(1);
|
||||||
|
let el: HTMLElement = de[0].nativeElement;
|
||||||
|
expect(el).toBeTruthy();
|
||||||
|
expect(el.textContent).toEqual('library/nginx');
|
||||||
|
});
|
||||||
});
|
});
|
||||||
}));
|
}));
|
||||||
});
|
});
|
||||||
|
@ -222,7 +222,7 @@ export class RepositoryGridviewComponent implements OnChanges, OnInit {
|
|||||||
this.lastFilteredRepoName = repoName;
|
this.lastFilteredRepoName = repoName;
|
||||||
this.currentPage = 1;
|
this.currentPage = 1;
|
||||||
let st: State = this.currentState;
|
let st: State = this.currentState;
|
||||||
if (!st) {
|
if (!st || !st.page) {
|
||||||
st = { page: {} };
|
st = { page: {} };
|
||||||
}
|
}
|
||||||
st.page.size = this.pageSize;
|
st.page.size = this.pageSize;
|
||||||
@ -391,6 +391,9 @@ export class RepositoryGridviewComponent implements OnChanges, OnInit {
|
|||||||
}
|
}
|
||||||
|
|
||||||
clrLoad(state: State): void {
|
clrLoad(state: State): void {
|
||||||
|
if (!state || !state.page) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
this.selectedRow = [];
|
this.selectedRow = [];
|
||||||
// Keep it for future filtering and sorting
|
// Keep it for future filtering and sorting
|
||||||
this.currentState = state;
|
this.currentState = state;
|
||||||
@ -509,8 +512,8 @@ export class RepositoryGridviewComponent implements OnChanges, OnInit {
|
|||||||
let hasDeleteRepositoryPermission = this.userPermissionService.getPermission(this.projectId,
|
let hasDeleteRepositoryPermission = this.userPermissionService.getPermission(this.projectId,
|
||||||
USERSTATICPERMISSION.REPOSITORY.KEY, USERSTATICPERMISSION.REPOSITORY.VALUE.DELETE);
|
USERSTATICPERMISSION.REPOSITORY.KEY, USERSTATICPERMISSION.REPOSITORY.VALUE.DELETE);
|
||||||
forkJoin(hasCreateRepositoryPermission, hasDeleteRepositoryPermission).subscribe(permissions => {
|
forkJoin(hasCreateRepositoryPermission, hasDeleteRepositoryPermission).subscribe(permissions => {
|
||||||
this.hasCreateRepositoryPermission = permissions[0] as boolean;
|
this.hasCreateRepositoryPermission = permissions[0] as boolean;
|
||||||
this.hasDeleteRepositoryPermission = permissions[1] as boolean;
|
this.hasDeleteRepositoryPermission = permissions[1] as boolean;
|
||||||
}, error => this.errorHandler.error(error));
|
}, error => this.errorHandler.error(error));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -7,7 +7,6 @@ import { SharedModule } from '../shared/shared.module';
|
|||||||
import { ConfirmationDialogComponent } from '../confirmation-dialog/confirmation-dialog.component';
|
import { ConfirmationDialogComponent } from '../confirmation-dialog/confirmation-dialog.component';
|
||||||
import { ImageNameInputComponent } from "../image-name-input/image-name-input.component";
|
import { ImageNameInputComponent } from "../image-name-input/image-name-input.component";
|
||||||
import { RepositoryComponent } from './repository.component';
|
import { RepositoryComponent } from './repository.component';
|
||||||
import { RepositoryGridviewComponent } from '../repository-gridview/repository-gridview.component';
|
|
||||||
import { GridViewComponent } from '../gridview/grid-view.component';
|
import { GridViewComponent } from '../gridview/grid-view.component';
|
||||||
import { FilterComponent } from '../filter/filter.component';
|
import { FilterComponent } from '../filter/filter.component';
|
||||||
import { TagComponent } from '../tag/tag.component';
|
import { TagComponent } from '../tag/tag.component';
|
||||||
@ -166,7 +165,6 @@ describe('RepositoryComponent (inline template)', () => {
|
|||||||
declarations: [
|
declarations: [
|
||||||
RepositoryComponent,
|
RepositoryComponent,
|
||||||
GridViewComponent,
|
GridViewComponent,
|
||||||
RepositoryGridviewComponent,
|
|
||||||
ConfirmationDialogComponent,
|
ConfirmationDialogComponent,
|
||||||
ImageNameInputComponent,
|
ImageNameInputComponent,
|
||||||
FilterComponent,
|
FilterComponent,
|
||||||
@ -224,22 +222,30 @@ describe('RepositoryComponent (inline template)', () => {
|
|||||||
.and.returnValue(of(mockHasScanImagePermission));
|
.and.returnValue(of(mockHasScanImagePermission));
|
||||||
fixture.detectChanges();
|
fixture.detectChanges();
|
||||||
});
|
});
|
||||||
|
let originalTimeout;
|
||||||
|
|
||||||
|
beforeEach(function () {
|
||||||
|
originalTimeout = jasmine.DEFAULT_TIMEOUT_INTERVAL;
|
||||||
|
jasmine.DEFAULT_TIMEOUT_INTERVAL = 100000;
|
||||||
|
});
|
||||||
|
|
||||||
|
afterEach(function () {
|
||||||
|
jasmine.DEFAULT_TIMEOUT_INTERVAL = originalTimeout;
|
||||||
|
});
|
||||||
it('should create', () => {
|
it('should create', () => {
|
||||||
expect(compRepo).toBeTruthy();
|
expect(compRepo).toBeTruthy();
|
||||||
});
|
});
|
||||||
|
|
||||||
// fail after upgrade to angular 6.
|
it('should load and render data', async(() => {
|
||||||
xit('should load and render data', async(() => {
|
|
||||||
fixture.detectChanges();
|
fixture.detectChanges();
|
||||||
fixture.whenStable().then(() => {
|
fixture.whenStable().then(() => {
|
||||||
fixture.detectChanges();
|
fixture.detectChanges();
|
||||||
let de: DebugElement = fixture.debugElement.query(By.css('datagrid-cell'));
|
let de: DebugElement = fixture.debugElement.query(del => del.classes['datagrid-cell']);
|
||||||
fixture.detectChanges();
|
fixture.detectChanges();
|
||||||
expect(de).toBeTruthy();
|
expect(de).toBeTruthy();
|
||||||
let el: HTMLElement = de.nativeElement;
|
let el: HTMLElement = de.nativeElement;
|
||||||
expect(el).toBeTruthy();
|
expect(el).toBeTruthy();
|
||||||
expect(el.textContent).toEqual('library/busybox');
|
expect(el.textContent).toEqual('1.11.5');
|
||||||
});
|
});
|
||||||
}));
|
}));
|
||||||
});
|
});
|
||||||
|
@ -175,8 +175,7 @@ describe("TagComponent (inline template)", () => {
|
|||||||
expect(spy.calls.any).toBeTruthy();
|
expect(spy.calls.any).toBeTruthy();
|
||||||
}));
|
}));
|
||||||
|
|
||||||
// fail after upgrade to angular 6.
|
it("should load and render data", () => {
|
||||||
xit("should load and render data", async(() => {
|
|
||||||
fixture.detectChanges();
|
fixture.detectChanges();
|
||||||
fixture.whenStable().then(() => {
|
fixture.whenStable().then(() => {
|
||||||
fixture.detectChanges();
|
fixture.detectChanges();
|
||||||
@ -187,7 +186,7 @@ describe("TagComponent (inline template)", () => {
|
|||||||
expect(el).toBeTruthy();
|
expect(el).toBeTruthy();
|
||||||
expect(el.textContent.trim()).toEqual("1.11.5");
|
expect(el.textContent.trim()).toEqual("1.11.5");
|
||||||
});
|
});
|
||||||
}));
|
});
|
||||||
|
|
||||||
});
|
});
|
||||||
|
|
||||||
|
@ -2,7 +2,7 @@
|
|||||||
<clr-dg-action-bar>
|
<clr-dg-action-bar>
|
||||||
<button type="button" class="btn btn-sm btn-secondary" (click)="addNewProject()" *ngIf="projectCreationRestriction">
|
<button type="button" class="btn btn-sm btn-secondary" (click)="addNewProject()" *ngIf="projectCreationRestriction">
|
||||||
<clr-icon shape="plus" size="16"></clr-icon> {{'PROJECT.NEW_PROJECT' | translate}}</button>
|
<clr-icon shape="plus" size="16"></clr-icon> {{'PROJECT.NEW_PROJECT' | translate}}</button>
|
||||||
<button type="button" class="btn btn-sm btn-secondary" [disabled]="!(selectedRow.length && (isSystemAdmin || canDelete))"
|
<button type="button" class="btn btn-sm btn-secondary" [disabled]="!canDelete"
|
||||||
(click)="deleteProjects(selectedRow)">
|
(click)="deleteProjects(selectedRow)">
|
||||||
<clr-icon shape="times" size="16"></clr-icon> {{'PROJECT.DELETE' | translate}}</button>
|
<clr-icon shape="times" size="16"></clr-icon> {{'PROJECT.DELETE' | translate}}</button>
|
||||||
</clr-dg-action-bar>
|
</clr-dg-action-bar>
|
||||||
|
@ -113,10 +113,11 @@ export class ListProjectComponent implements OnDestroy {
|
|||||||
}
|
}
|
||||||
|
|
||||||
public get canDelete(): boolean {
|
public get canDelete(): boolean {
|
||||||
if (this.projects.length) {
|
if (!this.selectedRow.length) {
|
||||||
return this.projects.some((pro: Project) => pro.current_user_role_id === 1);
|
return false;
|
||||||
}
|
}
|
||||||
return false;
|
|
||||||
|
return this.isSystemAdmin || this.selectedRow.every((pro: Project) => pro.current_user_role_id === 1);
|
||||||
}
|
}
|
||||||
|
|
||||||
ngOnDestroy(): void {
|
ngOnDestroy(): void {
|
||||||
|
@ -24,15 +24,27 @@ class DockerAPI(object):
|
|||||||
_tag = tag
|
_tag = tag
|
||||||
else:
|
else:
|
||||||
_tag = "latest"
|
_tag = "latest"
|
||||||
|
if expected_error_message is "":
|
||||||
|
expected_error_message = None
|
||||||
|
caught_err = False
|
||||||
|
ret = ""
|
||||||
try:
|
try:
|
||||||
base._get_string_from_unicode(self.DCLIENT.pull(r'{}:{}'.format(image, _tag)))
|
ret = base._get_string_from_unicode(self.DCLIENT.pull(r'{}:{}'.format(image, _tag)))
|
||||||
except Exception, err:
|
except Exception, err:
|
||||||
|
caught_err = True
|
||||||
if expected_error_message is not None:
|
if expected_error_message is not None:
|
||||||
print "docker image pull error:", str(err)
|
print "docker image pull error:", str(err)
|
||||||
if str(err).lower().find(expected_error_message.lower()) < 0:
|
if str(err).lower().find(expected_error_message.lower()) < 0:
|
||||||
raise Exception(r"Pull image: Return message {} is not as expected {}".format(return_message, expected_error_message))
|
raise Exception(r"Pull image: Return message {} is not as expected {}".format(str(err), expected_error_message))
|
||||||
else:
|
else:
|
||||||
raise Exception(r" Docker pull image {} failed, error is [{}]".format (image, e.message))
|
raise Exception(r" Docker pull image {} failed, error is [{}]".format (image, err.message))
|
||||||
|
if caught_err == False:
|
||||||
|
if expected_error_message is not None:
|
||||||
|
if str(ret).lower().find(expected_error_message.lower()) < 0:
|
||||||
|
raise Exception(r" Failed to catch error [{}] when pull image {}".format (expected_error_message, image))
|
||||||
|
else:
|
||||||
|
if str(ret).lower().find("error".lower()) >= 0:
|
||||||
|
raise Exception(r" It's was not suppose to catch error when pull image {}, return message is [{}]".format (image, ret))
|
||||||
|
|
||||||
def docker_image_tag(self, image, harbor_registry, tag = None):
|
def docker_image_tag(self, image, harbor_registry, tag = None):
|
||||||
_tag = base._random_name("tag")
|
_tag = base._random_name("tag")
|
||||||
@ -44,8 +56,25 @@ class DockerAPI(object):
|
|||||||
except docker.errors.APIError, e:
|
except docker.errors.APIError, e:
|
||||||
raise Exception(r" Docker tag image {} failed, error is [{}]".format (image, e.message))
|
raise Exception(r" Docker tag image {} failed, error is [{}]".format (image, e.message))
|
||||||
|
|
||||||
def docker_image_push(self, harbor_registry, tag):
|
def docker_image_push(self, harbor_registry, tag, expected_error_message = None):
|
||||||
|
caught_err = False
|
||||||
|
ret = ""
|
||||||
|
if expected_error_message is "":
|
||||||
|
expected_error_message = None
|
||||||
try:
|
try:
|
||||||
base._get_string_from_unicode(self.DCLIENT.push(harbor_registry, tag, stream=True))
|
ret = base._get_string_from_unicode(self.DCLIENT.push(harbor_registry, tag, stream=True))
|
||||||
except docker.errors.APIError, e:
|
except Exception, err:
|
||||||
raise Exception(r" Docker tag image {} failed, error is [{}]".format (image, e.message))
|
caught_err = True
|
||||||
|
if expected_error_message is not None:
|
||||||
|
print "docker image push error:", str(err)
|
||||||
|
if str(err).lower().find(expected_error_message.lower()) < 0:
|
||||||
|
raise Exception(r"Push image: Return message {} is not as expected {}".format(str(err), expected_error_message))
|
||||||
|
else:
|
||||||
|
raise Exception(r" Docker push image {} failed, error is [{}]".format (harbor_registry, err.message))
|
||||||
|
if caught_err == False:
|
||||||
|
if expected_error_message is not None:
|
||||||
|
if str(ret).lower().find(expected_error_message.lower()) < 0:
|
||||||
|
raise Exception(r" Failed to catch error [{}] when push image {}".format (expected_error_message, harbor_registry))
|
||||||
|
else:
|
||||||
|
if str(ret).lower().find("errorDetail".lower()) >= 0:
|
||||||
|
raise Exception(r" It's was not suppose to catch error when push image {}, return message is [{}]".format (harbor_registry, ret))
|
@ -34,7 +34,6 @@ class Project(base.Base):
|
|||||||
base._assert_status_code(201, status_code)
|
base._assert_status_code(201, status_code)
|
||||||
return base._get_id_from_header(header), name
|
return base._get_id_from_header(header), name
|
||||||
|
|
||||||
|
|
||||||
def get_projects(self, params, **kwargs):
|
def get_projects(self, params, **kwargs):
|
||||||
client = self._get_client(**kwargs)
|
client = self._get_client(**kwargs)
|
||||||
data = []
|
data = []
|
||||||
@ -150,12 +149,14 @@ class Project(base.Base):
|
|||||||
data = []
|
data = []
|
||||||
data, status_code, _ = client.projects_project_id_members_mid_put_with_http_info(project_id, member_id, role = role)
|
data, status_code, _ = client.projects_project_id_members_mid_put_with_http_info(project_id, member_id, role = role)
|
||||||
base._assert_status_code(expect_status_code, status_code)
|
base._assert_status_code(expect_status_code, status_code)
|
||||||
|
base._assert_status_code(200, status_code)
|
||||||
return data
|
return data
|
||||||
|
|
||||||
def delete_project_member(self, project_id, member_id, expect_status_code = 200, **kwargs):
|
def delete_project_member(self, project_id, member_id, expect_status_code = 200, **kwargs):
|
||||||
client = self._get_client(**kwargs)
|
client = self._get_client(**kwargs)
|
||||||
_, status_code, _ = client.projects_project_id_members_mid_delete_with_http_info(project_id, member_id)
|
_, status_code, _ = client.projects_project_id_members_mid_delete_with_http_info(project_id, member_id)
|
||||||
base._assert_status_code(expect_status_code, status_code)
|
base._assert_status_code(expect_status_code, status_code)
|
||||||
|
base._assert_status_code(200, status_code)
|
||||||
|
|
||||||
def add_project_members(self, project_id, user_id, member_role_id = None, expect_status_code = 201, **kwargs):
|
def add_project_members(self, project_id, user_id, member_role_id = None, expect_status_code = 201, **kwargs):
|
||||||
if member_role_id is None:
|
if member_role_id is None:
|
||||||
@ -165,6 +166,53 @@ class Project(base.Base):
|
|||||||
client = self._get_client(**kwargs)
|
client = self._get_client(**kwargs)
|
||||||
data = []
|
data = []
|
||||||
data, status_code, header = client.projects_project_id_members_post_with_http_info(project_id, project_member = projectMember)
|
data, status_code, header = client.projects_project_id_members_post_with_http_info(project_id, project_member = projectMember)
|
||||||
base._assert_status_code(201, status_code)
|
base._assert_status_code(expect_status_code, status_code)
|
||||||
return base._get_id_from_header(header)
|
return base._get_id_from_header(header)
|
||||||
|
|
||||||
|
def add_project_robot_account(self, project_id, project_name, robot_name = None, robot_desc = None, has_pull_right = True, has_push_right = True, expect_status_code = 201, **kwargs):
|
||||||
|
if robot_name is None:
|
||||||
|
robot_name = base._random_name("robot")
|
||||||
|
if robot_desc is None:
|
||||||
|
robot_desc = base._random_name("robot_desc")
|
||||||
|
if has_pull_right is False and has_push_right is False:
|
||||||
|
has_pull_right = True
|
||||||
|
access_list = []
|
||||||
|
resource_by_project_id = "/project/"+str(project_id)+"/repository"
|
||||||
|
resource_by_project_name = "/project/"+project_name+"/repository"
|
||||||
|
action_pull = "pull"
|
||||||
|
action_push = "push"
|
||||||
|
if has_pull_right is True:
|
||||||
|
robotAccountAccess = swagger_client.RobotAccountAccess(resource = resource_by_project_id, action = action_pull)
|
||||||
|
access_list.append(robotAccountAccess)
|
||||||
|
robotAccountAccess = swagger_client.RobotAccountAccess(resource = resource_by_project_name, action = action_pull)
|
||||||
|
access_list.append(robotAccountAccess)
|
||||||
|
if has_push_right is True:
|
||||||
|
robotAccountAccess = swagger_client.RobotAccountAccess(resource = resource_by_project_id, action = action_push)
|
||||||
|
access_list.append(robotAccountAccess)
|
||||||
|
robotAccountAccess = swagger_client.RobotAccountAccess(resource = resource_by_project_name, action = action_push)
|
||||||
|
access_list.append(robotAccountAccess)
|
||||||
|
robotAccountCreate = swagger_client.RobotAccountCreate(robot_name, robot_desc, access_list)
|
||||||
|
client = self._get_client(**kwargs)
|
||||||
|
data = []
|
||||||
|
data, status_code, header = client.projects_project_id_robots_post_with_http_info(project_id, robotAccountCreate)
|
||||||
|
base._assert_status_code(expect_status_code, status_code)
|
||||||
|
base._assert_status_code(201, status_code)
|
||||||
|
return base._get_id_from_header(header), data
|
||||||
|
|
||||||
|
def get_project_robot_account_by_id(self, project_id, robot_id, **kwargs):
|
||||||
|
client = self._get_client(**kwargs)
|
||||||
|
data, status_code, _ = client.projects_project_id_robots_robot_id_get_with_http_info(project_id, robot_id)
|
||||||
|
return data
|
||||||
|
|
||||||
|
def disable_project_robot_account(self, project_id, robot_id, disable, expect_status_code = 200, **kwargs):
|
||||||
|
client = self._get_client(**kwargs)
|
||||||
|
robotAccountUpdate = swagger_client.RobotAccountUpdate(disable)
|
||||||
|
_, status_code, _ = client.projects_project_id_robots_robot_id_put_with_http_info(project_id, robot_id, robotAccountUpdate)
|
||||||
|
base._assert_status_code(expect_status_code, status_code)
|
||||||
|
base._assert_status_code(200, status_code)
|
||||||
|
|
||||||
|
def delete_project_robot_account(self, project_id, robot_id, expect_status_code = 200, **kwargs):
|
||||||
|
client = self._get_client(**kwargs)
|
||||||
|
_, status_code, _ = client.projects_project_id_robots_robot_id_delete_with_http_info(project_id, robot_id)
|
||||||
|
base._assert_status_code(expect_status_code, status_code)
|
||||||
|
base._assert_status_code(200, status_code)
|
@ -11,7 +11,7 @@ def pull_harbor_image(registry, username, password, image, tag, expected_error_m
|
|||||||
time.sleep(2)
|
time.sleep(2)
|
||||||
_docker_api.docker_image_pull(r'{}/{}'.format(registry, image), tag = tag, expected_error_message = expected_error_message)
|
_docker_api.docker_image_pull(r'{}/{}'.format(registry, image), tag = tag, expected_error_message = expected_error_message)
|
||||||
|
|
||||||
def push_image_to_project(project_name, registry, username, password, image, tag):
|
def push_image_to_project(project_name, registry, username, password, image, tag, expected_error_message = None):
|
||||||
_docker_api = DockerAPI()
|
_docker_api = DockerAPI()
|
||||||
_docker_api.docker_login(registry, username, password)
|
_docker_api.docker_login(registry, username, password)
|
||||||
time.sleep(2)
|
time.sleep(2)
|
||||||
@ -22,7 +22,7 @@ def push_image_to_project(project_name, registry, username, password, image, tag
|
|||||||
new_harbor_registry, new_tag = _docker_api.docker_image_tag(r'{}:{}'.format(image, tag), r'{}/{}/{}'.format(registry, project_name, image))
|
new_harbor_registry, new_tag = _docker_api.docker_image_tag(r'{}:{}'.format(image, tag), r'{}/{}/{}'.format(registry, project_name, image))
|
||||||
time.sleep(2)
|
time.sleep(2)
|
||||||
|
|
||||||
_docker_api.docker_image_push(new_harbor_registry, new_tag)
|
_docker_api.docker_image_push(new_harbor_registry, new_tag, expected_error_message = expected_error_message)
|
||||||
|
|
||||||
return r'{}/{}'.format(project_name, image), new_tag
|
return r'{}/{}'.format(project_name, image), new_tag
|
||||||
|
|
||||||
|
135
tests/apitests/python/test_robot_account.py
Normal file
135
tests/apitests/python/test_robot_account.py
Normal file
@ -0,0 +1,135 @@
|
|||||||
|
from __future__ import absolute_import
|
||||||
|
|
||||||
|
import unittest
|
||||||
|
|
||||||
|
from testutils import ADMIN_CLIENT
|
||||||
|
from testutils import TEARDOWN
|
||||||
|
from library.user import User
|
||||||
|
from library.project import Project
|
||||||
|
from library.repository import Repository
|
||||||
|
from library.repository import pull_harbor_image
|
||||||
|
from library.repository import push_image_to_project
|
||||||
|
from testutils import harbor_server
|
||||||
|
from library.base import _assert_status_code
|
||||||
|
|
||||||
|
class TestProjects(unittest.TestCase):
|
||||||
|
@classmethod
|
||||||
|
def setUp(self):
|
||||||
|
project = Project()
|
||||||
|
self.project= project
|
||||||
|
|
||||||
|
user = User()
|
||||||
|
self.user= user
|
||||||
|
|
||||||
|
repo = Repository()
|
||||||
|
self.repo= repo
|
||||||
|
|
||||||
|
@classmethod
|
||||||
|
def tearDown(self):
|
||||||
|
print "Case completed"
|
||||||
|
|
||||||
|
@unittest.skipIf(TEARDOWN == False, "Test data won't be erased.")
|
||||||
|
def test_ClearData(self):
|
||||||
|
#1. Delete repository(RA) by user(UA);
|
||||||
|
self.repo.delete_repoitory(TestProjects.repo_name_in_project_a, **TestProjects.USER_RA_CLIENT)
|
||||||
|
self.repo.delete_repoitory(TestProjects.repo_name_in_project_b, **TestProjects.USER_RA_CLIENT)
|
||||||
|
self.repo.delete_repoitory(TestProjects.repo_name_in_project_c, **TestProjects.USER_RA_CLIENT)
|
||||||
|
self.repo.delete_repoitory(TestProjects.repo_name_pa, **TestProjects.USER_RA_CLIENT)
|
||||||
|
|
||||||
|
#2. Delete project(PA);
|
||||||
|
self.project.delete_project(TestProjects.project_ra_id_a, **TestProjects.USER_RA_CLIENT)
|
||||||
|
self.project.delete_project(TestProjects.project_ra_id_b, **TestProjects.USER_RA_CLIENT)
|
||||||
|
self.project.delete_project(TestProjects.project_ra_id_c, **TestProjects.USER_RA_CLIENT)
|
||||||
|
|
||||||
|
#3. Delete user(UA);
|
||||||
|
self.user.delete_user(TestProjects.user_ra_id, **ADMIN_CLIENT)
|
||||||
|
|
||||||
|
def testRobotAccount(self):
|
||||||
|
"""
|
||||||
|
Test case:
|
||||||
|
Robot Account
|
||||||
|
Test step and expected result:
|
||||||
|
1. Create user(UA);
|
||||||
|
2. Create private project(PA), private project(PB) and public project(PC) by user(UA);
|
||||||
|
3. Push image(ImagePA) to project(PA), image(ImagePB) to project(PB) and image(ImagePC) to project(PC) by user(UA);
|
||||||
|
4. Create a new robot account(RA) with pull and push privilige in project(PA) by user(UA);
|
||||||
|
5. Check robot account info, it should has both pull and push priviliges;
|
||||||
|
6. Pull image(ImagePA) from project(PA) by robot account(RA), it must be successful;
|
||||||
|
7. Push image(ImageRA) to project(PA) by robot account(RA), it must be successful;
|
||||||
|
8. Push image(ImageRA) to project(PB) by robot account(RA), it must be not successful;
|
||||||
|
9. Pull image(ImagePB) from project(PB) by robot account(RA), it must be not successful;
|
||||||
|
10. Pull image from project(PC), it must be successful;
|
||||||
|
11. Push image(ImageRA) to project(PC) by robot account(RA), it must be not successful;
|
||||||
|
12. Update action property of robot account(RA);
|
||||||
|
13. Pull image(ImagePA) from project(PA) by robot account(RA), it must be not successful;
|
||||||
|
14. Push image(ImageRA) to project(PA) by robot account(RA), it must be not successful;
|
||||||
|
15. Push image(ImageRA) to project(PA) by robot account(RA), it must be not successful;
|
||||||
|
Tear down:
|
||||||
|
1. Delete project(PA) (PB) (PC);
|
||||||
|
2. Delete user(UA).
|
||||||
|
"""
|
||||||
|
url = ADMIN_CLIENT["endpoint"]
|
||||||
|
admin_name = ADMIN_CLIENT["username"]
|
||||||
|
admin_password = ADMIN_CLIENT["password"]
|
||||||
|
user_ra_password = "Aa123456"
|
||||||
|
image_project_a = "tomcat"
|
||||||
|
image_project_b = "hello-world"
|
||||||
|
image_project_c = "mysql"
|
||||||
|
image_robot_account = "mariadb"
|
||||||
|
tag = "latest"
|
||||||
|
|
||||||
|
print "#1. Create user(UA);"
|
||||||
|
TestProjects.user_ra_id, user_ra_name = self.user.create_user(user_password = user_ra_password, **ADMIN_CLIENT)
|
||||||
|
TestProjects.USER_RA_CLIENT=dict(endpoint = url, username = user_ra_name, password = user_ra_password)
|
||||||
|
|
||||||
|
print "#2. Create private project(PA), private project(PB) and public project(PC) by user(UA);"
|
||||||
|
TestProjects.project_ra_id_a, project_ra_name_a = self.project.create_project(metadata = {"public": "false"}, **TestProjects.USER_RA_CLIENT)
|
||||||
|
TestProjects.project_ra_id_b, project_ra_name_b = self.project.create_project(metadata = {"public": "false"}, **TestProjects.USER_RA_CLIENT)
|
||||||
|
TestProjects.project_ra_id_c, project_ra_name_c = self.project.create_project(metadata = {"public": "true"}, **TestProjects.USER_RA_CLIENT)
|
||||||
|
|
||||||
|
print "#3. Push image(ImagePA) to project(PA), image(ImagePB) to project(PB) and image(ImagePC) to project(PC) by user(UA);"
|
||||||
|
TestProjects.repo_name_in_project_a, tag_a = push_image_to_project(project_ra_name_a, harbor_server, user_ra_name, user_ra_password, image_project_a, tag)
|
||||||
|
TestProjects.repo_name_in_project_b, tag_b = push_image_to_project(project_ra_name_b, harbor_server, user_ra_name, user_ra_password, image_project_b, tag)
|
||||||
|
TestProjects.repo_name_in_project_c, tag_c = push_image_to_project(project_ra_name_c, harbor_server, user_ra_name, user_ra_password, image_project_c, tag)
|
||||||
|
|
||||||
|
print "#4. Create a new robot account(RA) with pull and push privilige in project(PA) by user(UA);"
|
||||||
|
robot_id, robot_account = self.project.add_project_robot_account(TestProjects.project_ra_id_a, project_ra_name_a, **TestProjects.USER_RA_CLIENT)
|
||||||
|
print robot_account.name
|
||||||
|
print robot_account.token
|
||||||
|
|
||||||
|
print "#5. Check robot account info, it should has both pull and push priviliges;"
|
||||||
|
data = self.project.get_project_robot_account_by_id(TestProjects.project_ra_id_a, robot_id, **TestProjects.USER_RA_CLIENT)
|
||||||
|
_assert_status_code(robot_account.name, data.name)
|
||||||
|
|
||||||
|
print "#6. Pull image(ImagePA) from project(PA) by robot account(RA), it must be successful;"
|
||||||
|
pull_harbor_image(harbor_server, robot_account.name, robot_account.token, TestProjects.repo_name_in_project_a, tag_a)
|
||||||
|
|
||||||
|
print "#7. Push image(ImageRA) to project(PA) by robot account(RA), it must be successful;"
|
||||||
|
TestProjects.repo_name_pa, _ = push_image_to_project(project_ra_name_a, harbor_server, robot_account.name, robot_account.token, image_robot_account, tag)
|
||||||
|
|
||||||
|
print "#8. Push image(ImageRA) to project(PB) by robot account(RA), it must be not successful;"
|
||||||
|
push_image_to_project(project_ra_name_b, harbor_server, robot_account.name, robot_account.token, image_robot_account, tag, expected_error_message = "denied: requested access to the resource is denied")
|
||||||
|
|
||||||
|
print "#9. Pull image(ImagePB) from project(PB) by robot account(RA), it must be not successful;"
|
||||||
|
pull_harbor_image(harbor_server, robot_account.name, robot_account.token, TestProjects.repo_name_in_project_b, tag_b, expected_error_message = r"pull access denied for " + harbor_server + "/" + TestProjects.repo_name_in_project_b)
|
||||||
|
|
||||||
|
print "#10. Pull image from project(PC), it must be successful;"
|
||||||
|
pull_harbor_image(harbor_server, robot_account.name, robot_account.token, TestProjects.repo_name_in_project_c, tag_c)
|
||||||
|
|
||||||
|
print "#11. Push image(ImageRA) to project(PC) by robot account(RA), it must be not successful;"
|
||||||
|
push_image_to_project(project_ra_name_c, harbor_server, robot_account.name, robot_account.token, image_robot_account, tag, expected_error_message = "denied: requested access to the resource is denied")
|
||||||
|
|
||||||
|
print "#12. Update action property of robot account(RA);"
|
||||||
|
#self.project.disable_project_robot_account(TestProjects.project_ra_id_a, robot_id, True, **TestProjects.USER_RA_CLIENT)
|
||||||
|
|
||||||
|
print "#13. Pull image(ImagePA) from project(PA) by robot account(RA), it must be not successful;"
|
||||||
|
#pull_harbor_image(harbor_server, robot_account.name, robot_account.token, TestProjects.repo_name_in_project_a, tag_a, expected_error_message = "")
|
||||||
|
|
||||||
|
print "#14. Push image(ImageRA) to project(PA) by robot account(RA), it must be not successful;"
|
||||||
|
#push_image_to_project(project_ra_name_a, harbor_server, robot_account.name, robot_account.token, image_robot_account, tag, expected_error_message = "")
|
||||||
|
|
||||||
|
print "#15. Push image(ImageRA) to project(PA) by robot account(RA), it must be not successful;"
|
||||||
|
self.project.delete_project_robot_account(TestProjects.project_ra_id_a, robot_id, **TestProjects.USER_RA_CLIENT)
|
||||||
|
|
||||||
|
if __name__ == '__main__':
|
||||||
|
unittest.main()
|
49
tests/configharbor.py
Normal file
49
tests/configharbor.py
Normal file
@ -0,0 +1,49 @@
|
|||||||
|
#!/usr/bin/python
|
||||||
|
|
||||||
|
import argparse
|
||||||
|
import json
|
||||||
|
import requests
|
||||||
|
import sys
|
||||||
|
|
||||||
|
parser = argparse.ArgumentParser()
|
||||||
|
parser.add_argument("-H", "--host", help="The Harbor server need to config")
|
||||||
|
parser.add_argument("-u", "--user", default="admin", help="The Harbor username")
|
||||||
|
parser.add_argument("-p", "--password", default="Harbor12345", help="The Harbor password")
|
||||||
|
parser.add_argument("-c", "--config", nargs='+', help="The configure settings <key>=<value>, it can take more than one configures")
|
||||||
|
args = parser.parse_args()
|
||||||
|
reqJson = {}
|
||||||
|
for item in args.config :
|
||||||
|
configs = item.split("=", 1)
|
||||||
|
key = configs[0].strip()
|
||||||
|
value = configs[1].strip()
|
||||||
|
if value.lower() in ['true', 'yes', '1'] :
|
||||||
|
reqJson[key] = True
|
||||||
|
elif value.lower() in ['false', 'no', '0'] :
|
||||||
|
reqJson[key] = False
|
||||||
|
elif value.isdigit() :
|
||||||
|
reqJson[key] = int(value)
|
||||||
|
else:
|
||||||
|
reqJson[key] = value
|
||||||
|
|
||||||
|
# Sample Basic Auth Url with login values as username and password
|
||||||
|
url = "https://"+args.host+"/api/configurations"
|
||||||
|
user = args.user
|
||||||
|
passwd = args.password
|
||||||
|
|
||||||
|
# Make a request to the endpoint using the correct auth values
|
||||||
|
auth_values = (user, passwd)
|
||||||
|
session = requests.Session()
|
||||||
|
session.verify = False
|
||||||
|
data = json.dumps(reqJson)
|
||||||
|
headers = {'Content-type': 'application/json', 'Accept': 'text/plain'}
|
||||||
|
response = session.put(url, auth=auth_values, data=data, headers=headers)
|
||||||
|
|
||||||
|
# Convert JSON to dict and print
|
||||||
|
if response.status_code == 200 :
|
||||||
|
print("Configure setting success")
|
||||||
|
print("values:"+data)
|
||||||
|
sys.exit(0)
|
||||||
|
else:
|
||||||
|
print("Failed with http return code:"+ str(response.status_code))
|
||||||
|
sys.exit(1)
|
||||||
|
|
@ -19,17 +19,6 @@ services:
|
|||||||
- ./common/config/db/env
|
- ./common/config/db/env
|
||||||
ports:
|
ports:
|
||||||
- 5432:5432
|
- 5432:5432
|
||||||
adminserver:
|
|
||||||
image: goharbor/harbor-adminserver:__version__
|
|
||||||
env_file:
|
|
||||||
- ./common/config/adminserver/env
|
|
||||||
restart: always
|
|
||||||
volumes:
|
|
||||||
- /data/config/:/etc/adminserver/config/
|
|
||||||
- /data/secretkey:/etc/adminserver/key
|
|
||||||
- /data/:/data/
|
|
||||||
ports:
|
|
||||||
- 8888:8080
|
|
||||||
redis:
|
redis:
|
||||||
image: goharbor/redis-photon:4.0
|
image: goharbor/redis-photon:4.0
|
||||||
restart: always
|
restart: always
|
||||||
|
@ -5,12 +5,3 @@ PROTOCOL='https'
|
|||||||
#echo $IP
|
#echo $IP
|
||||||
sudo sed "s/reg.mydomain.com/$IP/" -i make/harbor.cfg
|
sudo sed "s/reg.mydomain.com/$IP/" -i make/harbor.cfg
|
||||||
sudo sed "s/^ui_url_protocol = .*/ui_url_protocol = $PROTOCOL/g" -i make/harbor.cfg
|
sudo sed "s/^ui_url_protocol = .*/ui_url_protocol = $PROTOCOL/g" -i make/harbor.cfg
|
||||||
|
|
||||||
if [ "$1" = 'LDAP' ]; then
|
|
||||||
sudo sed "s/db_auth/ldap_auth/" -i make/harbor.cfg
|
|
||||||
sudo sed "s/ldaps:\/\/ldap.mydomain.com/ldap:\/\/$IP/g" -i make/harbor.cfg
|
|
||||||
sudo sed "s/#ldap_searchdn = uid=searchuser,ou=people,dc=mydomain,dc=com/ldap_searchdn = cn=admin,dc=example,dc=com/" -i make/harbor.cfg
|
|
||||||
sudo sed "s/#ldap_search_pwd = password/ldap_search_pwd = admin/" -i make/harbor.cfg
|
|
||||||
sudo sed "s/ldap_basedn = ou=people,dc=mydomain,dc=com/ldap_basedn = dc=example,dc=com/" -i make/harbor.cfg
|
|
||||||
sudo sed "s/ldap_uid = uid/ldap_uid = cn/" -i make/harbor.cfg
|
|
||||||
fi
|
|
@ -13,6 +13,7 @@ else
|
|||||||
IP=`ip addr s eth0 |grep "inet "|awk '{print $2}' |awk -F "/" '{print $1}'`
|
IP=`ip addr s eth0 |grep "inet "|awk '{print $2}' |awk -F "/" '{print $1}'`
|
||||||
fi
|
fi
|
||||||
echo "server ip is "$IP
|
echo "server ip is "$IP
|
||||||
|
|
||||||
sed -i -r "s/POSTGRESQL_HOST=postgresql/POSTGRESQL_HOST=$IP/" make/common/config/adminserver/env
|
sed -i -r "s/POSTGRESQL_HOST=postgresql/POSTGRESQL_HOST=$IP/" make/common/config/adminserver/env
|
||||||
sed -i -r "s|REGISTRY_URL=http://registry:5000|REGISTRY_URL=http://$IP:5000|" make/common/config/adminserver/env
|
sed -i -r "s|REGISTRY_URL=http://registry:5000|REGISTRY_URL=http://$IP:5000|" make/common/config/adminserver/env
|
||||||
sed -i -r "s/CORE_SECRET=.*/CORE_SECRET=$CORE_SECRET/" make/common/config/adminserver/env
|
sed -i -r "s/CORE_SECRET=.*/CORE_SECRET=$CORE_SECRET/" make/common/config/adminserver/env
|
||||||
|
@ -15,9 +15,8 @@ if [ "$2" = 'LDAP' ]; then
|
|||||||
cd tests && sudo ./ldapprepare.sh && cd ..
|
cd tests && sudo ./ldapprepare.sh && cd ..
|
||||||
fi
|
fi
|
||||||
|
|
||||||
if [ "$2" = 'DB' ]; then
|
sudo ./tests/hostcfg.sh
|
||||||
sudo ./tests/hostcfg.sh
|
|
||||||
fi
|
|
||||||
|
|
||||||
# prepare a chart file for API_DB test...
|
# prepare a chart file for API_DB test...
|
||||||
sudo curl -o /home/travis/gopath/src/github.com/goharbor/harbor/tests/apitests/python/mariadb-4.3.1.tgz https://storage.googleapis.com/harbor-builds/bin/charts/mariadb-4.3.1.tgz
|
sudo curl -o /home/travis/gopath/src/github.com/goharbor/harbor/tests/apitests/python/mariadb-4.3.1.tgz https://storage.googleapis.com/harbor-builds/bin/charts/mariadb-4.3.1.tgz
|
||||||
|
@ -32,6 +32,12 @@ if [ "$1" = 'DB' ]; then
|
|||||||
pybot -v ip:$2 -v HARBOR_PASSWORD:Harbor12345 /home/travis/gopath/src/github.com/goharbor/harbor/tests/robot-cases/Group0-BAT/API_DB.robot
|
pybot -v ip:$2 -v HARBOR_PASSWORD:Harbor12345 /home/travis/gopath/src/github.com/goharbor/harbor/tests/robot-cases/Group0-BAT/API_DB.robot
|
||||||
elif [ "$1" = 'LDAP' ]; then
|
elif [ "$1" = 'LDAP' ]; then
|
||||||
# run ldap api cases
|
# run ldap api cases
|
||||||
|
python /home/travis/gopath/src/github.com/goharbor/harbor/tests/configharbor.py -H $IP -u $HARBOR_ADMIN -p $HARBOR_ADMIN_PASSWD -c auth_mode=ldap_auth \
|
||||||
|
ldap_url=ldap://$IP \
|
||||||
|
ldap_search_dn=cn=admin,dc=example,dc=com \
|
||||||
|
ldap_search_password=admin \
|
||||||
|
ldap_base_dn=dc=example,dc=com \
|
||||||
|
ldap_uid=cn
|
||||||
pybot -v ip:$2 -v HARBOR_PASSWORD:Harbor12345 /home/travis/gopath/src/github.com/goharbor/harbor/tests/robot-cases/Group0-BAT/API_LDAP.robot
|
pybot -v ip:$2 -v HARBOR_PASSWORD:Harbor12345 /home/travis/gopath/src/github.com/goharbor/harbor/tests/robot-cases/Group0-BAT/API_LDAP.robot
|
||||||
else
|
else
|
||||||
rc=999
|
rc=999
|
||||||
|
@ -33,7 +33,7 @@ sudo ./tests/testprepare.sh
|
|||||||
|
|
||||||
cd tests && sudo ./ldapprepare.sh && sudo ./admiral.sh && cd ..
|
cd tests && sudo ./ldapprepare.sh && sudo ./admiral.sh && cd ..
|
||||||
sudo make compile_adminserver
|
sudo make compile_adminserver
|
||||||
sudo make -f make/photon/Makefile _build_adminserver _build_db _build_registry -e VERSIONTAG=dev -e CLAIRDBVERSION=dev -e REGISTRYVERSION=${REG_VERSION}
|
sudo make -f make/photon/Makefile _build_db _build_registry -e VERSIONTAG=dev -e CLAIRDBVERSION=dev -e REGISTRYVERSION=${REG_VERSION}
|
||||||
sudo sed -i 's/__reg_version__/${REG_VERSION}-dev/g' ./make/docker-compose.test.yml
|
sudo sed -i 's/__reg_version__/${REG_VERSION}-dev/g' ./make/docker-compose.test.yml
|
||||||
sudo sed -i 's/__version__/dev/g' ./make/docker-compose.test.yml
|
sudo sed -i 's/__version__/dev/g' ./make/docker-compose.test.yml
|
||||||
sudo mkdir -p ./make/common/config/registry/ && sudo mv ./tests/reg_config.yml ./make/common/config/registry/config.yml
|
sudo mkdir -p ./make/common/config/registry/ && sudo mv ./tests/reg_config.yml ./make/common/config/registry/config.yml
|
@ -14,6 +14,6 @@ sleep 10
|
|||||||
./tests/pushimage.sh
|
./tests/pushimage.sh
|
||||||
docker ps
|
docker ps
|
||||||
|
|
||||||
go test -race -i ./src/core ./src/adminserver ./src/jobservice
|
go test -race -i ./src/core ./src/jobservice
|
||||||
sudo -E env "PATH=$PATH" "POSTGRES_MIGRATION_SCRIPTS_PATH=/home/travis/gopath/src/github.com/goharbor/harbor/make/migrations/postgresql/" ./tests/coverage4gotest.sh
|
sudo -E env "PATH=$PATH" "POSTGRES_MIGRATION_SCRIPTS_PATH=/home/travis/gopath/src/github.com/goharbor/harbor/make/migrations/postgresql/" ./tests/coverage4gotest.sh
|
||||||
goveralls -coverprofile=profile.cov -service=travis-ci
|
goveralls -coverprofile=profile.cov -service=travis-ci
|
Loading…
Reference in New Issue
Block a user