mirror of
https://github.com/goharbor/harbor.git
synced 2025-01-25 00:51:24 +01:00
Remove everything of adminserver
Signed-off-by: stonezdj <stonezdj@gmail.com>
This commit is contained in:
parent
a1063edae6
commit
0cba36d79f
1
.gitignore
vendored
1
.gitignore
vendored
@ -41,3 +41,4 @@ src/portal/src/**/*.js.map
|
||||
**/aot
|
||||
**/dist
|
||||
**/.bin
|
||||
src/core/conf/app.conf
|
||||
|
30
Makefile
30
Makefile
@ -4,13 +4,13 @@
|
||||
#
|
||||
# all: prepare env, compile binaries, build images and install images
|
||||
# prepare: prepare env
|
||||
# compile: compile adminserver, ui and jobservice code
|
||||
# compile: compile core and jobservice code
|
||||
#
|
||||
# compile_golangimage:
|
||||
# compile from golang image
|
||||
# for example: make compile_golangimage -e GOBUILDIMAGE= \
|
||||
# golang:1.11.2
|
||||
# compile_adminserver, compile_core, compile_jobservice: compile specific binary
|
||||
# compile_core, compile_jobservice: compile specific binary
|
||||
#
|
||||
# build: build Harbor docker images from photon baseimage
|
||||
#
|
||||
@ -43,7 +43,7 @@
|
||||
#
|
||||
# clean: remove binary, Harbor images, specific version docker-compose \
|
||||
# file, specific version tag and online/offline install package
|
||||
# cleanbinary: remove adminserver, ui and jobservice binary
|
||||
# cleanbinary: remove core and jobservice binary
|
||||
# cleanimage: remove Harbor images
|
||||
# cleandockercomposefile:
|
||||
# remove specific version docker-compose
|
||||
@ -129,21 +129,17 @@ GOBUILDIMAGE=golang:1.11.2
|
||||
GOBUILDPATH=$(GOBASEPATH)/harbor
|
||||
GOIMAGEBUILDCMD=/usr/local/go/bin/go
|
||||
GOIMAGEBUILD=$(GOIMAGEBUILDCMD) build
|
||||
GOBUILDPATH_ADMINSERVER=$(GOBUILDPATH)/src/adminserver
|
||||
GOBUILDPATH_CORE=$(GOBUILDPATH)/src/core
|
||||
GOBUILDPATH_JOBSERVICE=$(GOBUILDPATH)/src/jobservice
|
||||
GOBUILDPATH_REGISTRYCTL=$(GOBUILDPATH)/src/registryctl
|
||||
GOBUILDPATH_MIGRATEPATCH=$(GOBUILDPATH)/src/cmd/migrate-patch
|
||||
GOBUILDMAKEPATH=$(GOBUILDPATH)/make
|
||||
GOBUILDMAKEPATH_ADMINSERVER=$(GOBUILDMAKEPATH)/photon/adminserver
|
||||
GOBUILDMAKEPATH_CORE=$(GOBUILDMAKEPATH)/photon/core
|
||||
GOBUILDMAKEPATH_JOBSERVICE=$(GOBUILDMAKEPATH)/photon/jobservice
|
||||
GOBUILDMAKEPATH_REGISTRYCTL=$(GOBUILDMAKEPATH)/photon/registryctl
|
||||
GOBUILDMAKEPATH_NOTARY=$(GOBUILDMAKEPATH)/photon/notary
|
||||
|
||||
# binary
|
||||
ADMINSERVERBINARYPATH=$(MAKEDEVPATH)/adminserver
|
||||
ADMINSERVERBINARYNAME=harbor_adminserver
|
||||
CORE_BINARYPATH=$(MAKEDEVPATH)/core
|
||||
CORE_BINARYNAME=harbor_core
|
||||
JOBSERVICEBINARYPATH=$(MAKEDEVPATH)/jobservice
|
||||
@ -178,7 +174,6 @@ MAKEFILEPATH_PHOTON=$(MAKEPATH)/photon
|
||||
DOCKERFILEPATH_COMMON=$(MAKEPATH)/common
|
||||
|
||||
# docker image name
|
||||
DOCKERIMAGENAME_ADMINSERVER=goharbor/harbor-adminserver
|
||||
DOCKERIMAGENAME_PORTAL=goharbor/harbor-portal
|
||||
DOCKERIMAGENAME_CORE=goharbor/harbor-core
|
||||
DOCKERIMAGENAME_JOBSERVICE=goharbor/harbor-jobservice
|
||||
@ -213,8 +208,7 @@ REGISTRYUSER=user
|
||||
REGISTRYPASSWORD=default
|
||||
|
||||
# cmds
|
||||
DOCKERSAVE_PARA=$(DOCKERIMAGENAME_ADMINSERVER):$(VERSIONTAG) \
|
||||
$(DOCKERIMAGENAME_PORTAL):$(VERSIONTAG) \
|
||||
DOCKERSAVE_PARA= $(DOCKERIMAGENAME_PORTAL):$(VERSIONTAG) \
|
||||
$(DOCKERIMAGENAME_CORE):$(VERSIONTAG) \
|
||||
$(DOCKERIMAGENAME_LOG):$(VERSIONTAG) \
|
||||
$(DOCKERIMAGENAME_DB):$(VERSIONTAG) \
|
||||
@ -268,13 +262,6 @@ ui_version:
|
||||
check_environment:
|
||||
@$(MAKEPATH)/$(CHECKENVCMD)
|
||||
|
||||
compile_adminserver:
|
||||
@echo "compiling binary for adminserver (golang image)..."
|
||||
@echo $(GOBASEPATH)
|
||||
@echo $(GOBUILDPATH)
|
||||
$(DOCKERCMD) run --rm -v $(BUILDPATH):$(GOBUILDPATH) -w $(GOBUILDPATH_ADMINSERVER) $(GOBUILDIMAGE) $(GOIMAGEBUILD) -o $(GOBUILDMAKEPATH_ADMINSERVER)/$(ADMINSERVERBINARYNAME)
|
||||
@echo "Done."
|
||||
|
||||
compile_core:
|
||||
@echo "compiling binary for core (golang image)..."
|
||||
@echo $(GOBASEPATH)
|
||||
@ -297,7 +284,7 @@ compile_notary_migrate_patch:
|
||||
@$(DOCKERCMD) run --rm -v $(BUILDPATH):$(GOBUILDPATH) -w $(GOBUILDPATH_MIGRATEPATCH) $(GOBUILDIMAGE) $(GOIMAGEBUILD) -o $(GOBUILDMAKEPATH_NOTARY)/$(MIGRATEPATCHBINARYNAME)
|
||||
@echo "Done."
|
||||
|
||||
compile:check_environment compile_adminserver compile_core compile_jobservice compile_registryctl compile_notary_migrate_patch
|
||||
compile:check_environment compile_core compile_jobservice compile_registryctl compile_notary_migrate_patch
|
||||
|
||||
prepare:
|
||||
@echo "preparing..."
|
||||
@ -417,11 +404,6 @@ govet:
|
||||
|
||||
pushimage:
|
||||
@echo "pushing harbor images ..."
|
||||
@$(DOCKERTAG) $(DOCKERIMAGENAME_ADMINSERVER):$(VERSIONTAG) $(REGISTRYSERVER)$(DOCKERIMAGENAME_ADMINSERVER):$(VERSIONTAG)
|
||||
@$(PUSHSCRIPTPATH)/$(PUSHSCRIPTNAME) $(REGISTRYSERVER)$(DOCKERIMAGENAME_ADMINSERVER):$(VERSIONTAG) \
|
||||
$(REGISTRYUSER) $(REGISTRYPASSWORD) $(REGISTRYSERVER)
|
||||
@$(DOCKERRMIMAGE) $(REGISTRYSERVER)$(DOCKERIMAGENAME_ADMINSERVER):$(VERSIONTAG)
|
||||
|
||||
@$(DOCKERTAG) $(DOCKERIMAGENAME_PORTAL):$(VERSIONTAG) $(REGISTRYSERVER)$(DOCKERIMAGENAME_PORTAL):$(VERSIONTAG)
|
||||
@$(PUSHSCRIPTPATH)/$(PUSHSCRIPTNAME) $(REGISTRYSERVER)$(DOCKERIMAGENAME_PORTAL):$(VERSIONTAG) \
|
||||
$(REGISTRYUSER) $(REGISTRYPASSWORD) $(REGISTRYSERVER)
|
||||
@ -474,13 +456,11 @@ swagger_client:
|
||||
|
||||
cleanbinary:
|
||||
@echo "cleaning binary..."
|
||||
@if [ -f $(ADMINSERVERBINARYPATH)/$(ADMINSERVERBINARYNAME) ] ; then rm $(ADMINSERVERBINARYPATH)/$(ADMINSERVERBINARYNAME) ; fi
|
||||
@if [ -f $(CORE_BINARYPATH)/$(CORE_BINARYNAME) ] ; then rm $(CORE_BINARYPATH)/$(CORE_BINARYNAME) ; fi
|
||||
@if [ -f $(JOBSERVICEBINARYPATH)/$(JOBSERVICEBINARYNAME) ] ; then rm $(JOBSERVICEBINARYPATH)/$(JOBSERVICEBINARYNAME) ; fi
|
||||
|
||||
cleanimage:
|
||||
@echo "cleaning image for photon..."
|
||||
- $(DOCKERRMIMAGE) -f $(DOCKERIMAGENAME_ADMINSERVER):$(VERSIONTAG)
|
||||
- $(DOCKERRMIMAGE) -f $(DOCKERIMAGENAME_CORE):$(VERSIONTAG)
|
||||
- $(DOCKERRMIMAGE) -f $(DOCKERIMAGENAME_DB):$(VERSIONTAG)
|
||||
- $(DOCKERRMIMAGE) -f $(DOCKERIMAGENAME_JOBSERVICE):$(VERSIONTAG)
|
||||
|
@ -210,7 +210,6 @@ Stopping registry ... done
|
||||
Stopping redis ... done
|
||||
Stopping registryctl ... done
|
||||
Stopping harbor-db ... done
|
||||
Stopping harbor-adminserver ... done
|
||||
Stopping harbor-log ... done
|
||||
```
|
||||
Restarting Harbor after stopping:
|
||||
@ -220,7 +219,6 @@ Starting log ... done
|
||||
Starting registry ... done
|
||||
Starting registryctl ... done
|
||||
Starting postgresql ... done
|
||||
Starting adminserver ... done
|
||||
Starting core ... done
|
||||
Starting portal ... done
|
||||
Starting redis ... done
|
||||
@ -390,7 +388,6 @@ By default, Harbor limits the CPU usage of Clair container to 150000 and avoids
|
||||
$ sudo docker-compose ps
|
||||
Name Command State Ports
|
||||
-----------------------------------------------------------------------------------------------------------------------------
|
||||
harbor-adminserver /harbor/start.sh Up
|
||||
harbor-core /harbor/start.sh Up
|
||||
harbor-db /entrypoint.sh postgres Up 5432/tcp
|
||||
harbor-jobservice /harbor/start.sh Up
|
||||
|
@ -47,8 +47,8 @@ or above it's not necessary to call the migrator tool to migrate the schema.
|
||||
```
|
||||
docker run -it --rm -v ${harbor_cfg}:/harbor-migration/harbor-cfg/harbor.cfg goharbor/harbor-migrator:[tag] --cfg up
|
||||
```
|
||||
**NOTE:** The schema upgrade and data migration of Database is performed by adminserver when Harbor starts, if the migration fails,
|
||||
please check the log of adminserver to debug.
|
||||
**NOTE:** The schema upgrade and data migration of Database is performed by core when Harbor starts, if the migration fails,
|
||||
please check the log of core to debug.
|
||||
|
||||
6. Under the directory `./harbor`, run the `./install.sh` script to install the new Harbor instance. If you choose to install Harbor with components like Notary, Clair, and chartmuseum, refer to [Installation & Configuration Guide](../docs/installation_guide.md) for more information.
|
||||
|
||||
|
@ -15,9 +15,8 @@ all | prepare env, compile binaries, build images and install im
|
||||
prepare | prepare env
|
||||
compile | compile ui and jobservice code
|
||||
compile_portal | compile portal code
|
||||
compile_ui | compile ui binary
|
||||
compile_core | compile core binary
|
||||
compile_jobservice | compile jobservice binary
|
||||
compile_adminserver | compile admin server binary
|
||||
build | build Harbor docker images (default: using build_photon)
|
||||
build_photon | build Harbor docker images from Photon OS base image
|
||||
install | compile binaries, build images, prepare specific version of compose file and startup Harbor instance
|
||||
|
@ -2,7 +2,6 @@ LOG_LEVEL=info
|
||||
CONFIG_PATH=/etc/core/app.conf
|
||||
CORE_SECRET=$core_secret
|
||||
JOBSERVICE_SECRET=$jobservice_secret
|
||||
ADMINSERVER_URL=$adminserver_url
|
||||
UAA_CA_ROOT=/etc/core/certificates/uaa_ca.pem
|
||||
_REDIS_URL=$redis_host:$redis_port,100,$redis_password
|
||||
SYNC_REGISTRY=false
|
||||
|
@ -99,7 +99,7 @@ services:
|
||||
container_name: harbor-core
|
||||
env_file:
|
||||
- ./common/config/core/env
|
||||
- ./common/config/adminserver/env
|
||||
- ./common/config/core/config_env
|
||||
restart: always
|
||||
cap_drop:
|
||||
- ALL
|
||||
|
@ -3,7 +3,7 @@
|
||||
# Targets:
|
||||
#
|
||||
# build: build harbor photon images
|
||||
# clean: clean adminserver, ui and jobservice harbor images
|
||||
# clean: clean core and jobservice harbor images
|
||||
|
||||
# common
|
||||
SHELL := /bin/bash
|
||||
@ -21,10 +21,7 @@ DOCKERBUILD=$(DOCKERCMD) build --pull
|
||||
DOCKERRMIMAGE=$(DOCKERCMD) rmi
|
||||
DOCKERIMASES=$(DOCKERCMD) images
|
||||
|
||||
# binary
|
||||
ADMINSERVERSOURCECODE=$(SRCPATH)/adminserver
|
||||
ADMINSERVERBINARYPATH=$(MAKEDEVPATH)/adminserver
|
||||
ADMINSERVERBINARYNAME=harbor_adminserver
|
||||
# binary
|
||||
CORE_SOURCECODE=$(SRCPATH)/core
|
||||
CORE_BINARYPATH=$(MAKEDEVPATH)/core
|
||||
CORE_BINARYNAME=harbor_core
|
||||
@ -35,10 +32,6 @@ JOBSERVICEBINARYNAME=harbor_jobservice
|
||||
# photon dockerfile
|
||||
DOCKERFILEPATH=$(MAKEPATH)/photon
|
||||
|
||||
DOCKERFILEPATH_ADMINSERVER=$(DOCKERFILEPATH)/adminserver
|
||||
DOCKERFILENAME_ADMINSERVER=Dockerfile
|
||||
DOCKERIMAGENAME_ADMINSERVER=goharbor/harbor-adminserver
|
||||
|
||||
DOCKERFILEPATH_PORTAL=$(DOCKERFILEPATH)/portal
|
||||
DOCKERFILENAME_PORTAL=Dockerfile
|
||||
DOCKERIMAGENAME_PORTAL=goharbor/harbor-portal
|
||||
@ -105,11 +98,6 @@ _build_db:
|
||||
@$(DOCKERBUILD) -f $(DOCKERFILEPATH_DB)/$(DOCKERFILENAME_DB) -t $(DOCKERIMAGENAME_DB):$(VERSIONTAG) .
|
||||
@echo "Done."
|
||||
|
||||
_build_adminserver:
|
||||
@echo "building adminserver container for photon..."
|
||||
@$(DOCKERBUILD) -f $(DOCKERFILEPATH_ADMINSERVER)/$(DOCKERFILENAME_ADMINSERVER) -t $(DOCKERIMAGENAME_ADMINSERVER):$(VERSIONTAG) .
|
||||
@echo "Done."
|
||||
|
||||
_build_portal:
|
||||
@echo "building portal container for photon..."
|
||||
$(DOCKERBUILD) -f $(DOCKERFILEPATH_PORTAL)/$(DOCKERFILENAME_PORTAL) -t $(DOCKERIMAGENAME_PORTAL):$(VERSIONTAG) .
|
||||
@ -212,11 +200,10 @@ define _get_binary
|
||||
$(WGET) --timeout 30 --no-check-certificate $1 -O $2
|
||||
endef
|
||||
|
||||
build: _build_db _build_adminserver _build_portal _build_core _build_jobservice _build_log _build_nginx _build_registry _build_registryctl _build_notary _build_clair _build_redis _build_migrator _build_chart_server
|
||||
build: _build_db _build_portal _build_core _build_jobservice _build_log _build_nginx _build_registry _build_registryctl _build_notary _build_clair _build_redis _build_migrator _build_chart_server
|
||||
|
||||
cleanimage:
|
||||
@echo "cleaning image for photon..."
|
||||
- $(DOCKERRMIMAGE) -f $(DOCKERIMAGENAME_ADMINSERVER):$(VERSIONTAG)
|
||||
- $(DOCKERRMIMAGE) -f $(DOCKERIMAGENAME_PORTAL):$(VERSIONTAG)
|
||||
- $(DOCKERRMIMAGE) -f $(DOCKERIMAGENAME_CORE):$(VERSIONTAG)
|
||||
- $(DOCKERRMIMAGE) -f $(DOCKERIMAGENAME_JOBSERVICE):$(VERSIONTAG)
|
||||
|
@ -1,15 +0,0 @@
|
||||
FROM photon:2.0
|
||||
|
||||
RUN tdnf install -y sudo >> /dev/null \
|
||||
&& tdnf clean all \
|
||||
&& groupadd -r -g 10000 harbor && useradd --no-log-init -r -g 10000 -u 10000 harbor \
|
||||
&& mkdir /harbor/
|
||||
COPY ./make/photon/adminserver/harbor_adminserver ./make/photon/adminserver/start.sh /harbor/
|
||||
#As UI will be blocked until adminserver is ready, let adminserver do the initialise work for DB
|
||||
COPY ./make/migrations /harbor/migrations
|
||||
|
||||
HEALTHCHECK CMD curl --fail -s http://127.0.0.1:8080/api/ping || exit 1
|
||||
|
||||
RUN chmod u+x /harbor/harbor_adminserver /harbor/start.sh
|
||||
WORKDIR /harbor/
|
||||
ENTRYPOINT ["/harbor/start.sh"]
|
@ -1,7 +0,0 @@
|
||||
#!/bin/sh
|
||||
|
||||
#In the case when the config store is set to filesystem, the directory has to be writable.
|
||||
if [ -d /etc/adminserver/config ]; then
|
||||
chown -R 10000:10000 /etc/adminserver/config
|
||||
fi
|
||||
sudo -E -u \#10000 "/harbor/harbor_adminserver"
|
14
make/prepare
14
make/prepare
@ -253,10 +253,6 @@ registry_custom_ca_bundle_path = rcp.get("configuration", "registry_custom_ca_bu
|
||||
core_secret = ''.join(random.choice(string.ascii_letters+string.digits) for i in range(16))
|
||||
jobservice_secret = ''.join(random.choice(string.ascii_letters+string.digits) for i in range(16))
|
||||
|
||||
adminserver_config_dir = os.path.join(config_dir,"adminserver")
|
||||
if not os.path.exists(adminserver_config_dir):
|
||||
os.makedirs(os.path.join(config_dir, "adminserver"))
|
||||
|
||||
core_config_dir = prep_conf_dir(config_dir,"core")
|
||||
core_certificates_dir = prep_conf_dir(core_config_dir,"certificates")
|
||||
db_config_dir = prep_conf_dir(config_dir, "db")
|
||||
@ -267,7 +263,7 @@ nginx_config_dir = prep_conf_dir (config_dir, "nginx")
|
||||
nginx_conf_d = prep_conf_dir(nginx_config_dir, "conf.d")
|
||||
log_config_dir = prep_conf_dir (config_dir, "log")
|
||||
|
||||
adminserver_conf_env = os.path.join(config_dir, "adminserver", "env")
|
||||
conf_env = os.path.join(config_dir, "core", "config_env")
|
||||
core_conf_env = os.path.join(config_dir, "core", "env")
|
||||
core_conf = os.path.join(config_dir, "core", "app.conf")
|
||||
core_cert_dir = os.path.join(config_dir, "core", "certificates")
|
||||
@ -279,8 +275,7 @@ db_conf_env = os.path.join(config_dir, "db", "env")
|
||||
job_conf_env = os.path.join(config_dir, "jobservice", "env")
|
||||
nginx_conf = os.path.join(config_dir, "nginx", "nginx.conf")
|
||||
cert_dir = os.path.join(config_dir, "nginx", "cert")
|
||||
log_rotate_config = os.path.join(config_dir, "log", "logrotate.conf")
|
||||
adminserver_url = "http://adminserver:8080"
|
||||
log_rotate_config = os.path.join(config_dir, "log", "logrotate.conf")
|
||||
registry_url = "http://registry:5000"
|
||||
registry_controller_url = "http://registryctl:8080"
|
||||
core_url = "http://core:8080"
|
||||
@ -338,8 +333,8 @@ reload_key = ''.join(random.choice(string.ascii_uppercase + string.digits) for _
|
||||
|
||||
ldap_group_admin_dn = rcp.get("configuration", "ldap_group_admin_dn") if rcp.has_option("configuration", "ldap_group_admin_dn") else ""
|
||||
|
||||
render(os.path.join(templates_dir, "adminserver", "env"),
|
||||
adminserver_conf_env,
|
||||
render(os.path.join(templates_dir, "core", "config_env"),
|
||||
conf_env,
|
||||
reload_config=reload_config,
|
||||
public_url=public_url,
|
||||
core_url=core_url,
|
||||
@ -415,7 +410,6 @@ render(os.path.join(templates_dir, "core", "env"),
|
||||
redis_host=redis_host,
|
||||
redis_port=redis_port,
|
||||
redis_password=redis_password,
|
||||
adminserver_url = adminserver_url,
|
||||
chart_cache_driver = chart_cache_driver,
|
||||
redis_url_reg = redis_url_reg)
|
||||
|
||||
|
@ -1,48 +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 api
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
"net/http"
|
||||
)
|
||||
|
||||
func handleInternalServerError(w http.ResponseWriter) {
|
||||
http.Error(w, http.StatusText(http.StatusInternalServerError),
|
||||
http.StatusInternalServerError)
|
||||
}
|
||||
|
||||
func handleBadRequestError(w http.ResponseWriter, error string) {
|
||||
http.Error(w, error, http.StatusBadRequest)
|
||||
}
|
||||
|
||||
func handleUnauthorized(w http.ResponseWriter) {
|
||||
http.Error(w, http.StatusText(http.StatusUnauthorized),
|
||||
http.StatusUnauthorized)
|
||||
}
|
||||
|
||||
// response status code will be written automatically if there is an error
|
||||
func writeJSON(w http.ResponseWriter, v interface{}) error {
|
||||
b, err := json.Marshal(v)
|
||||
if err != nil {
|
||||
handleInternalServerError(w)
|
||||
return err
|
||||
}
|
||||
|
||||
if _, err = w.Write(b); err != nil {
|
||||
return err
|
||||
}
|
||||
return nil
|
||||
}
|
@ -1,78 +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 api
|
||||
|
||||
import (
|
||||
"net/http"
|
||||
"net/http/httptest"
|
||||
"testing"
|
||||
)
|
||||
|
||||
func TestHandleInternalServerError(t *testing.T) {
|
||||
w := httptest.NewRecorder()
|
||||
handleInternalServerError(w)
|
||||
|
||||
if w.Code != http.StatusInternalServerError {
|
||||
t.Errorf("unexpected status code: %d != %d", w.Code, http.StatusInternalServerError)
|
||||
}
|
||||
}
|
||||
|
||||
func TestHandleBadRequestError(t *testing.T) {
|
||||
w := httptest.NewRecorder()
|
||||
err := "error message"
|
||||
handleBadRequestError(w, err)
|
||||
|
||||
if w.Code != http.StatusBadRequest {
|
||||
t.Errorf("unexpected status code: %d != %d", w.Code, http.StatusBadRequest)
|
||||
}
|
||||
}
|
||||
|
||||
func TestHandleUnauthorized(t *testing.T) {
|
||||
w := httptest.NewRecorder()
|
||||
handleUnauthorized(w)
|
||||
|
||||
if w.Code != http.StatusUnauthorized {
|
||||
t.Errorf("unexpected status code: %d != %d", w.Code, http.StatusUnauthorized)
|
||||
}
|
||||
}
|
||||
|
||||
func TestWriteJSONNilInterface(t *testing.T) {
|
||||
w := httptest.NewRecorder()
|
||||
|
||||
if err := writeJSON(w, nil); err != nil {
|
||||
t.Errorf("Expected nil error, received: %v", err)
|
||||
}
|
||||
}
|
||||
|
||||
func TestWriteJSONMarshallErr(t *testing.T) {
|
||||
// Tests capture json.Marshall error
|
||||
x := map[string]interface{}{
|
||||
"foo": make(chan int),
|
||||
}
|
||||
|
||||
w := httptest.NewRecorder()
|
||||
|
||||
if err := writeJSON(w, x); err == nil {
|
||||
t.Errorf("Expected %v error received: no no error", err)
|
||||
}
|
||||
}
|
||||
|
||||
func TestWriteJSON(t *testing.T) {
|
||||
w := httptest.NewRecorder()
|
||||
|
||||
if err := writeJSON(w, "Pong"); err != nil {
|
||||
t.Errorf("Expected nil error, received: %v", err)
|
||||
}
|
||||
}
|
@ -1,75 +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 api
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
"io/ioutil"
|
||||
"net/http"
|
||||
|
||||
"github.com/goharbor/harbor/src/adminserver/systemcfg"
|
||||
"github.com/goharbor/harbor/src/common/utils/log"
|
||||
)
|
||||
|
||||
// ListCfgs lists configurations
|
||||
func ListCfgs(w http.ResponseWriter, r *http.Request) {
|
||||
cfg, err := systemcfg.CfgStore.Read()
|
||||
if err != nil {
|
||||
log.Errorf("failed to get system configurations: %v", err)
|
||||
handleInternalServerError(w)
|
||||
return
|
||||
}
|
||||
systemcfg.AddMissedKey(cfg)
|
||||
if err = writeJSON(w, cfg); err != nil {
|
||||
log.Errorf("failed to write response: %v", err)
|
||||
return
|
||||
}
|
||||
}
|
||||
|
||||
// UpdateCfgs updates configurations
|
||||
func UpdateCfgs(w http.ResponseWriter, r *http.Request) {
|
||||
b, err := ioutil.ReadAll(r.Body)
|
||||
if err != nil {
|
||||
log.Errorf("failed to read request body: %v", err)
|
||||
handleInternalServerError(w)
|
||||
return
|
||||
}
|
||||
|
||||
m := map[string]interface{}{}
|
||||
if err = json.Unmarshal(b, &m); err != nil {
|
||||
handleBadRequestError(w, err.Error())
|
||||
return
|
||||
}
|
||||
if err = systemcfg.CfgStore.Write(m); err != nil {
|
||||
log.Errorf("failed to update system configurations: %v", err)
|
||||
handleInternalServerError(w)
|
||||
return
|
||||
}
|
||||
}
|
||||
|
||||
// ResetCfgs resets configurations from environment variables
|
||||
func ResetCfgs(w http.ResponseWriter, r *http.Request) {
|
||||
cfgs := map[string]interface{}{}
|
||||
if err := systemcfg.LoadFromEnv(cfgs, true); err != nil {
|
||||
log.Errorf("failed to reset system configurations: %v", err)
|
||||
handleInternalServerError(w)
|
||||
return
|
||||
}
|
||||
if err := systemcfg.CfgStore.Write(cfgs); err != nil {
|
||||
log.Errorf("failed to write system configurations to storage: %v", err)
|
||||
handleInternalServerError(w)
|
||||
return
|
||||
}
|
||||
}
|
@ -1,168 +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 api
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"encoding/json"
|
||||
"errors"
|
||||
"fmt"
|
||||
"io"
|
||||
"io/ioutil"
|
||||
"net/http"
|
||||
"net/http/httptest"
|
||||
"os"
|
||||
"testing"
|
||||
|
||||
"github.com/goharbor/harbor/src/adminserver/systemcfg"
|
||||
"github.com/goharbor/harbor/src/common"
|
||||
"github.com/stretchr/testify/assert"
|
||||
)
|
||||
|
||||
type fakeCfgStore struct {
|
||||
cfgs map[string]interface{}
|
||||
err error
|
||||
}
|
||||
|
||||
func (f *fakeCfgStore) Name() string {
|
||||
return "fake"
|
||||
}
|
||||
|
||||
func (f *fakeCfgStore) Read() (map[string]interface{}, error) {
|
||||
return f.cfgs, f.err
|
||||
}
|
||||
|
||||
func (f *fakeCfgStore) Write(cfgs map[string]interface{}) error {
|
||||
f.cfgs = cfgs
|
||||
return f.err
|
||||
}
|
||||
|
||||
func TestListCfgs(t *testing.T) {
|
||||
// 500
|
||||
systemcfg.CfgStore = &fakeCfgStore{
|
||||
cfgs: nil,
|
||||
err: errors.New("error"),
|
||||
}
|
||||
|
||||
w := httptest.NewRecorder()
|
||||
ListCfgs(w, nil)
|
||||
assert.Equal(t, http.StatusInternalServerError, w.Code)
|
||||
|
||||
// 200
|
||||
key := "key"
|
||||
value := "value"
|
||||
cfgs := map[string]interface{}{
|
||||
key: value,
|
||||
}
|
||||
systemcfg.CfgStore = &fakeCfgStore{
|
||||
cfgs: cfgs,
|
||||
err: nil,
|
||||
}
|
||||
w = httptest.NewRecorder()
|
||||
ListCfgs(w, nil)
|
||||
assert.Equal(t, http.StatusOK, w.Code)
|
||||
result, err := parse(w.Body)
|
||||
if err != nil {
|
||||
t.Fatalf("failed to parse response body: %v", err)
|
||||
}
|
||||
assert.Equal(t, value, result[key])
|
||||
}
|
||||
|
||||
func TestUpdateCfgs(t *testing.T) {
|
||||
// 400
|
||||
w := httptest.NewRecorder()
|
||||
r, err := http.NewRequest("", "", bytes.NewReader([]byte{'a'}))
|
||||
if err != nil {
|
||||
t.Fatalf("failed to create request: %v", err)
|
||||
}
|
||||
|
||||
UpdateCfgs(w, r)
|
||||
assert.Equal(t, http.StatusBadRequest, w.Code)
|
||||
|
||||
// 500
|
||||
systemcfg.CfgStore = &fakeCfgStore{
|
||||
cfgs: nil,
|
||||
err: errors.New("error"),
|
||||
}
|
||||
w = httptest.NewRecorder()
|
||||
r, err = http.NewRequest("", "", bytes.NewBufferString("{}"))
|
||||
if err != nil {
|
||||
t.Fatalf("failed to create request: %v", err)
|
||||
}
|
||||
|
||||
UpdateCfgs(w, r)
|
||||
assert.Equal(t, http.StatusInternalServerError, w.Code)
|
||||
|
||||
// 200
|
||||
key := "key"
|
||||
value := "value"
|
||||
systemcfg.CfgStore = &fakeCfgStore{
|
||||
cfgs: nil,
|
||||
err: nil,
|
||||
}
|
||||
w = httptest.NewRecorder()
|
||||
r, err = http.NewRequest("", "",
|
||||
bytes.NewBufferString(fmt.Sprintf(`{"%s":"%s"}`, key, value)))
|
||||
if err != nil {
|
||||
t.Fatalf("failed to create request: %v", err)
|
||||
}
|
||||
|
||||
UpdateCfgs(w, r)
|
||||
assert.Equal(t, http.StatusOK, w.Code)
|
||||
|
||||
}
|
||||
|
||||
func TestResetCfgs(t *testing.T) {
|
||||
// 500
|
||||
systemcfg.CfgStore = &fakeCfgStore{
|
||||
cfgs: nil,
|
||||
err: errors.New("error"),
|
||||
}
|
||||
w := httptest.NewRecorder()
|
||||
|
||||
ResetCfgs(w, nil)
|
||||
assert.Equal(t, http.StatusInternalServerError, w.Code)
|
||||
|
||||
// 200
|
||||
os.Clearenv()
|
||||
key := "LDAP_URL"
|
||||
value := "ldap://ldap.com"
|
||||
if err := os.Setenv(key, value); err != nil {
|
||||
t.Fatalf("failed to set env: %v", err)
|
||||
}
|
||||
store := &fakeCfgStore{
|
||||
cfgs: nil,
|
||||
err: nil,
|
||||
}
|
||||
systemcfg.CfgStore = store
|
||||
w = httptest.NewRecorder()
|
||||
|
||||
ResetCfgs(w, nil)
|
||||
assert.Equal(t, http.StatusOK, w.Code)
|
||||
assert.Equal(t, value, store.cfgs[common.LDAPURL])
|
||||
}
|
||||
|
||||
func parse(reader io.Reader) (map[string]interface{}, error) {
|
||||
b, err := ioutil.ReadAll(reader)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
m := map[string]interface{}{}
|
||||
if err := json.Unmarshal(b, &m); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return m, nil
|
||||
}
|
@ -1,14 +0,0 @@
|
||||
package api
|
||||
|
||||
import (
|
||||
"github.com/goharbor/harbor/src/common/utils/log"
|
||||
"net/http"
|
||||
)
|
||||
|
||||
// Ping monitor the server status
|
||||
func Ping(w http.ResponseWriter, r *http.Request) {
|
||||
if err := writeJSON(w, "Pong"); err != nil {
|
||||
log.Errorf("Failed to write response: %v", err)
|
||||
return
|
||||
}
|
||||
}
|
@ -1,17 +0,0 @@
|
||||
package api
|
||||
|
||||
import (
|
||||
"github.com/stretchr/testify/assert"
|
||||
"io/ioutil"
|
||||
"net/http"
|
||||
"net/http/httptest"
|
||||
"testing"
|
||||
)
|
||||
|
||||
func TestPing(t *testing.T) {
|
||||
w := httptest.NewRecorder()
|
||||
Ping(w, nil)
|
||||
assert.Equal(t, http.StatusOK, w.Code)
|
||||
result, _ := ioutil.ReadAll(w.Body)
|
||||
assert.Equal(t, "\"Pong\"", string(result))
|
||||
}
|
@ -1,54 +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 auth
|
||||
|
||||
import (
|
||||
"github.com/goharbor/harbor/src/common/secret"
|
||||
"net/http"
|
||||
)
|
||||
|
||||
// Authenticator defines Authenticate function to authenticate requests
|
||||
type Authenticator interface {
|
||||
// Authenticate the request, if there is no error, the bool value
|
||||
// determines whether the request is authenticated or not
|
||||
Authenticate(req *http.Request) (bool, error)
|
||||
}
|
||||
|
||||
type secretAuthenticator struct {
|
||||
secrets map[string]string
|
||||
}
|
||||
|
||||
// NewSecretAuthenticator returns an instance of secretAuthenticator
|
||||
func NewSecretAuthenticator(secrets map[string]string) Authenticator {
|
||||
return &secretAuthenticator{
|
||||
secrets: secrets,
|
||||
}
|
||||
}
|
||||
|
||||
// Authenticate the request according the secret
|
||||
func (s *secretAuthenticator) Authenticate(req *http.Request) (bool, error) {
|
||||
if len(s.secrets) == 0 {
|
||||
return true, nil
|
||||
}
|
||||
reqSecret := secret.FromRequest(req)
|
||||
|
||||
for _, v := range s.secrets {
|
||||
if reqSecret == v {
|
||||
return true, nil
|
||||
}
|
||||
}
|
||||
|
||||
return false, nil
|
||||
}
|
@ -1,53 +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 auth
|
||||
|
||||
import (
|
||||
"net/http"
|
||||
"testing"
|
||||
|
||||
commonsecret "github.com/goharbor/harbor/src/common/secret"
|
||||
"github.com/stretchr/testify/assert"
|
||||
)
|
||||
|
||||
func TestAuthenticate(t *testing.T) {
|
||||
secret := "correct"
|
||||
req1, err := http.NewRequest("", "", nil)
|
||||
if err != nil {
|
||||
t.Fatalf("failed to create request: %v", err)
|
||||
}
|
||||
|
||||
req2, err := http.NewRequest("", "", nil)
|
||||
if err != nil {
|
||||
t.Fatalf("failed to create request: %v", err)
|
||||
}
|
||||
_ = commonsecret.AddToRequest(req2, secret)
|
||||
cases := []struct {
|
||||
secrets map[string]string
|
||||
req *http.Request
|
||||
result bool
|
||||
}{
|
||||
{nil, req1, true},
|
||||
{map[string]string{"secret1": "incorrect"}, req2, false},
|
||||
{map[string]string{"secret1": "incorrect", "secret2": secret}, req2, true},
|
||||
}
|
||||
|
||||
for _, c := range cases {
|
||||
authenticator := NewSecretAuthenticator(c.secrets)
|
||||
authenticated, err := authenticator.Authenticate(c.req)
|
||||
assert.Nil(t, err, "unexpected error")
|
||||
assert.Equal(t, c.result, authenticated, "unexpected result")
|
||||
}
|
||||
}
|
@ -1,105 +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 client
|
||||
|
||||
import (
|
||||
"strings"
|
||||
|
||||
"github.com/goharbor/harbor/src/common/http"
|
||||
"github.com/goharbor/harbor/src/common/http/modifier/auth"
|
||||
"github.com/goharbor/harbor/src/common/utils"
|
||||
"github.com/goharbor/harbor/src/core/systeminfo/imagestorage"
|
||||
)
|
||||
|
||||
// Client defines methods that an Adminserver client should implement
|
||||
type Client interface {
|
||||
// Ping tests the connection with server
|
||||
Ping() error
|
||||
// GetCfgs returns system configurations
|
||||
GetCfgs() (map[string]interface{}, error)
|
||||
// UpdateCfgs updates system configurations
|
||||
UpdateCfgs(map[string]interface{}) error
|
||||
// ResetCfgs resets system configuratoins form environment variables
|
||||
ResetCfgs() error
|
||||
// Capacity returns the capacity of image storage
|
||||
Capacity() (*imagestorage.Capacity, error)
|
||||
}
|
||||
|
||||
// NewClient return an instance of Adminserver client
|
||||
func NewClient(baseURL string, cfg *Config) Client {
|
||||
baseURL = strings.TrimRight(baseURL, "/")
|
||||
if !strings.Contains(baseURL, "://") {
|
||||
baseURL = "http://" + baseURL
|
||||
}
|
||||
client := &client{
|
||||
baseURL: baseURL,
|
||||
}
|
||||
if cfg != nil {
|
||||
authorizer := auth.NewSecretAuthorizer(cfg.Secret)
|
||||
client.client = http.NewClient(nil, authorizer)
|
||||
}
|
||||
return client
|
||||
}
|
||||
|
||||
type client struct {
|
||||
baseURL string
|
||||
client *http.Client
|
||||
}
|
||||
|
||||
// Config contains configurations needed for client
|
||||
type Config struct {
|
||||
Secret string
|
||||
}
|
||||
|
||||
func (c *client) Ping() error {
|
||||
addr := strings.Split(c.baseURL, "://")[1]
|
||||
if !strings.Contains(addr, ":") {
|
||||
addr = addr + ":80"
|
||||
}
|
||||
|
||||
return utils.TestTCPConn(addr, 60, 2)
|
||||
}
|
||||
|
||||
// GetCfgs ...
|
||||
func (c *client) GetCfgs() (map[string]interface{}, error) {
|
||||
url := c.baseURL + "/api/configs"
|
||||
cfgs := map[string]interface{}{}
|
||||
if err := c.client.Get(url, &cfgs); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return cfgs, nil
|
||||
}
|
||||
|
||||
// UpdateCfgs ...
|
||||
func (c *client) UpdateCfgs(cfgs map[string]interface{}) error {
|
||||
url := c.baseURL + "/api/configurations"
|
||||
return c.client.Put(url, cfgs)
|
||||
}
|
||||
|
||||
// ResetCfgs ...
|
||||
func (c *client) ResetCfgs() error {
|
||||
url := c.baseURL + "/api/configurations/reset"
|
||||
return c.client.Post(url)
|
||||
}
|
||||
|
||||
// Capacity ...
|
||||
func (c *client) Capacity() (*imagestorage.Capacity, error) {
|
||||
url := c.baseURL + "/api/systeminfo/capacity"
|
||||
capacity := &imagestorage.Capacity{}
|
||||
if err := c.client.Get(url, capacity); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return capacity, nil
|
||||
}
|
@ -1,71 +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 client
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"os"
|
||||
"testing"
|
||||
|
||||
"github.com/goharbor/harbor/src/common"
|
||||
"github.com/goharbor/harbor/src/common/utils/test"
|
||||
"github.com/stretchr/testify/assert"
|
||||
)
|
||||
|
||||
var c Client
|
||||
|
||||
func TestMain(m *testing.M) {
|
||||
|
||||
server, err := test.NewAdminserver(nil)
|
||||
if err != nil {
|
||||
fmt.Printf("failed to create adminserver: %v", err)
|
||||
os.Exit(1)
|
||||
}
|
||||
|
||||
c = NewClient(server.URL, &Config{})
|
||||
|
||||
os.Exit(m.Run())
|
||||
}
|
||||
|
||||
func TestPing(t *testing.T) {
|
||||
err := c.Ping()
|
||||
assert.Nil(t, err, "unexpected error")
|
||||
}
|
||||
|
||||
func TestGetCfgs(t *testing.T) {
|
||||
cfgs, err := c.GetCfgs()
|
||||
if !assert.Nil(t, err, "unexpected error") {
|
||||
return
|
||||
}
|
||||
|
||||
assert.Equal(t, common.DBAuth, cfgs[common.AUTHMode], "unexpected configuration")
|
||||
}
|
||||
|
||||
func TestUpdateCfgs(t *testing.T) {
|
||||
cfgs := map[string]interface{}{
|
||||
common.AUTHMode: common.LDAPAuth,
|
||||
}
|
||||
err := c.UpdateCfgs(cfgs)
|
||||
if !assert.Nil(t, err, "unexpected error") {
|
||||
return
|
||||
}
|
||||
}
|
||||
|
||||
func TestResetCfgs(t *testing.T) {
|
||||
err := c.ResetCfgs()
|
||||
if !assert.Nil(t, err, "unexpected error") {
|
||||
return
|
||||
}
|
||||
}
|
@ -1,88 +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 handlers
|
||||
|
||||
import (
|
||||
"net/http"
|
||||
"os"
|
||||
|
||||
"github.com/goharbor/harbor/src/adminserver/auth"
|
||||
"github.com/goharbor/harbor/src/common/utils/log"
|
||||
gorilla_handlers "github.com/gorilla/handlers"
|
||||
)
|
||||
|
||||
// NewHandler returns a gorilla router which is wrapped by authenticate handler
|
||||
// and logging handler
|
||||
func NewHandler() http.Handler {
|
||||
h := newRouter()
|
||||
secrets := map[string]string{
|
||||
"uiSecret": os.Getenv("CORE_SECRET"),
|
||||
"jobserviceSecret": os.Getenv("JOBSERVICE_SECRET"),
|
||||
}
|
||||
insecureAPIs := map[string]bool{
|
||||
"/api/ping": true,
|
||||
}
|
||||
h = newAuthHandler(auth.NewSecretAuthenticator(secrets), h, insecureAPIs)
|
||||
h = gorilla_handlers.LoggingHandler(os.Stdout, h)
|
||||
return h
|
||||
}
|
||||
|
||||
type authHandler struct {
|
||||
authenticator auth.Authenticator
|
||||
handler http.Handler
|
||||
insecureAPIs map[string]bool
|
||||
}
|
||||
|
||||
func newAuthHandler(authenticator auth.Authenticator, handler http.Handler, insecureAPIs map[string]bool) http.Handler {
|
||||
return &authHandler{
|
||||
authenticator: authenticator,
|
||||
handler: handler,
|
||||
insecureAPIs: insecureAPIs,
|
||||
}
|
||||
}
|
||||
|
||||
func (a *authHandler) ServeHTTP(w http.ResponseWriter, r *http.Request) {
|
||||
if a.authenticator == nil {
|
||||
if a.handler != nil {
|
||||
a.handler.ServeHTTP(w, r)
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
if a.insecureAPIs != nil && a.insecureAPIs[r.URL.Path] {
|
||||
if a.handler != nil {
|
||||
a.handler.ServeHTTP(w, r)
|
||||
}
|
||||
return
|
||||
}
|
||||
valid, err := a.authenticator.Authenticate(r)
|
||||
if err != nil {
|
||||
log.Errorf("failed to authenticate request: %v", err)
|
||||
http.Error(w, http.StatusText(http.StatusInternalServerError),
|
||||
http.StatusInternalServerError)
|
||||
return
|
||||
}
|
||||
|
||||
if !valid {
|
||||
http.Error(w, http.StatusText(http.StatusUnauthorized),
|
||||
http.StatusUnauthorized)
|
||||
return
|
||||
}
|
||||
|
||||
if a.handler != nil {
|
||||
a.handler.ServeHTTP(w, r)
|
||||
}
|
||||
return
|
||||
}
|
@ -1,84 +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 handlers
|
||||
|
||||
import (
|
||||
"errors"
|
||||
"net/http"
|
||||
"net/http/httptest"
|
||||
"testing"
|
||||
|
||||
"github.com/goharbor/harbor/src/adminserver/auth"
|
||||
"github.com/stretchr/testify/assert"
|
||||
)
|
||||
|
||||
type fakeAuthenticator struct {
|
||||
authenticated bool
|
||||
err error
|
||||
}
|
||||
|
||||
func (f *fakeAuthenticator) Authenticate(req *http.Request) (bool, error) {
|
||||
return f.authenticated, f.err
|
||||
}
|
||||
|
||||
type fakeHandler struct {
|
||||
responseCode int
|
||||
}
|
||||
|
||||
func (f *fakeHandler) ServeHTTP(w http.ResponseWriter, r *http.Request) {
|
||||
w.WriteHeader(f.responseCode)
|
||||
}
|
||||
|
||||
func TestNewAuthHandler(t *testing.T) {
|
||||
cases := []struct {
|
||||
authenticator auth.Authenticator
|
||||
handler http.Handler
|
||||
insecureAPIs map[string]bool
|
||||
responseCode int
|
||||
requestURL string
|
||||
}{
|
||||
|
||||
{nil, nil, nil, http.StatusOK, "http://localhost/good"},
|
||||
{&fakeAuthenticator{
|
||||
authenticated: false,
|
||||
err: nil,
|
||||
}, nil, nil, http.StatusUnauthorized, "http://localhost/hello"},
|
||||
{&fakeAuthenticator{
|
||||
authenticated: false,
|
||||
err: errors.New("error"),
|
||||
}, nil, nil, http.StatusInternalServerError, "http://localhost/hello"},
|
||||
{&fakeAuthenticator{
|
||||
authenticated: true,
|
||||
err: nil,
|
||||
}, &fakeHandler{http.StatusNotFound}, nil, http.StatusNotFound, "http://localhost/notexsit"},
|
||||
{&fakeAuthenticator{
|
||||
authenticated: false,
|
||||
err: nil,
|
||||
}, &fakeHandler{http.StatusOK}, map[string]bool{"/api/ping": true}, http.StatusOK, "http://localhost/api/ping"},
|
||||
}
|
||||
|
||||
for _, c := range cases {
|
||||
handler := newAuthHandler(c.authenticator, c.handler, c.insecureAPIs)
|
||||
w := httptest.NewRecorder()
|
||||
r := httptest.NewRequest("GET", c.requestURL, nil)
|
||||
handler.ServeHTTP(w, r)
|
||||
assert.Equal(t, c.responseCode, w.Code, "unexpected response code")
|
||||
}
|
||||
handler := NewHandler()
|
||||
w := httptest.NewRecorder()
|
||||
r := httptest.NewRequest("GET", "http://localhost/api/ping", nil)
|
||||
handler.ServeHTTP(w, r)
|
||||
|
||||
}
|
@ -1,31 +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 handlers
|
||||
|
||||
import (
|
||||
"net/http"
|
||||
|
||||
"github.com/goharbor/harbor/src/adminserver/api"
|
||||
"github.com/gorilla/mux"
|
||||
)
|
||||
|
||||
func newRouter() http.Handler {
|
||||
r := mux.NewRouter()
|
||||
r.HandleFunc("/api/configurations", api.UpdateCfgs).Methods("PUT")
|
||||
r.HandleFunc("/api/configs", api.ListCfgs).Methods("GET")
|
||||
r.HandleFunc("/api/configurations/reset", api.ResetCfgs).Methods("POST")
|
||||
r.HandleFunc("/api/ping", api.Ping).Methods("GET")
|
||||
return r
|
||||
}
|
@ -1,60 +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 main
|
||||
|
||||
import (
|
||||
"net/http"
|
||||
"os"
|
||||
|
||||
"github.com/goharbor/harbor/src/adminserver/handlers"
|
||||
syscfg "github.com/goharbor/harbor/src/adminserver/systemcfg"
|
||||
"github.com/goharbor/harbor/src/common/utils/log"
|
||||
)
|
||||
|
||||
// Server for admin component
|
||||
type Server struct {
|
||||
Port string
|
||||
Handler http.Handler
|
||||
}
|
||||
|
||||
// Serve the API
|
||||
func (s *Server) Serve() error {
|
||||
server := &http.Server{
|
||||
Addr: ":" + s.Port,
|
||||
Handler: s.Handler,
|
||||
}
|
||||
|
||||
return server.ListenAndServe()
|
||||
}
|
||||
|
||||
func main() {
|
||||
log.Info("initializing system configurations...")
|
||||
if err := syscfg.Init(); err != nil {
|
||||
log.Fatalf("failed to initialize the system: %v", err)
|
||||
}
|
||||
log.Info("system initialization completed")
|
||||
|
||||
port := os.Getenv("PORT")
|
||||
if len(port) == 0 {
|
||||
port = "80"
|
||||
}
|
||||
server := &Server{
|
||||
Port: port,
|
||||
Handler: handlers.NewHandler(),
|
||||
}
|
||||
if err := server.Serve(); err != nil {
|
||||
log.Fatal(err)
|
||||
}
|
||||
}
|
@ -1,60 +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 encrypt
|
||||
|
||||
import (
|
||||
comcfg "github.com/goharbor/harbor/src/common/config"
|
||||
"github.com/goharbor/harbor/src/common/utils"
|
||||
)
|
||||
|
||||
// Encryptor encrypts or decrypts a strings
|
||||
type Encryptor interface {
|
||||
// Encrypt encrypts plaintext
|
||||
Encrypt(string) (string, error)
|
||||
// Decrypt decrypts ciphertext
|
||||
Decrypt(string) (string, error)
|
||||
}
|
||||
|
||||
// AESEncryptor uses AES to encrypt or decrypt string
|
||||
type AESEncryptor struct {
|
||||
keyProvider comcfg.KeyProvider
|
||||
keyParams map[string]interface{}
|
||||
}
|
||||
|
||||
// NewAESEncryptor returns an instance of an AESEncryptor
|
||||
func NewAESEncryptor(keyProvider comcfg.KeyProvider,
|
||||
keyParams map[string]interface{}) Encryptor {
|
||||
return &AESEncryptor{
|
||||
keyProvider: keyProvider,
|
||||
}
|
||||
}
|
||||
|
||||
// Encrypt ...
|
||||
func (a *AESEncryptor) Encrypt(plaintext string) (string, error) {
|
||||
key, err := a.keyProvider.Get(a.keyParams)
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
return utils.ReversibleEncrypt(plaintext, key)
|
||||
}
|
||||
|
||||
// Decrypt ...
|
||||
func (a *AESEncryptor) Decrypt(ciphertext string) (string, error) {
|
||||
key, err := a.keyProvider.Get(a.keyParams)
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
return utils.ReversibleDecrypt(ciphertext, key)
|
||||
}
|
@ -1,92 +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 encrypt
|
||||
|
||||
import (
|
||||
"errors"
|
||||
"testing"
|
||||
|
||||
comcfg "github.com/goharbor/harbor/src/common/config"
|
||||
"github.com/stretchr/testify/assert"
|
||||
)
|
||||
|
||||
type fakeKeyProvider struct {
|
||||
key string
|
||||
err error
|
||||
}
|
||||
|
||||
func (f *fakeKeyProvider) Get(params map[string]interface{}) (
|
||||
string, error) {
|
||||
return f.key, f.err
|
||||
}
|
||||
|
||||
func TestEncrypt(t *testing.T) {
|
||||
cases := []struct {
|
||||
plaintext string
|
||||
keyProvider comcfg.KeyProvider
|
||||
err bool
|
||||
}{
|
||||
{"", &fakeKeyProvider{"", errors.New("error")}, true},
|
||||
{"text", &fakeKeyProvider{"1234567890123456", nil}, false},
|
||||
}
|
||||
|
||||
for _, c := range cases {
|
||||
encrptor := NewAESEncryptor(c.keyProvider, nil)
|
||||
ciphertext, err := encrptor.Encrypt(c.plaintext)
|
||||
if c.err {
|
||||
assert.NotNil(t, err)
|
||||
} else {
|
||||
assert.Nil(t, err)
|
||||
str, err := encrptor.Decrypt(ciphertext)
|
||||
assert.Nil(t, err)
|
||||
assert.Equal(t, c.plaintext, str)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func TestDecrypt(t *testing.T) {
|
||||
plaintext := "text"
|
||||
key := "1234567890123456"
|
||||
|
||||
encrptor := NewAESEncryptor(&fakeKeyProvider{
|
||||
key: key,
|
||||
err: nil,
|
||||
}, nil)
|
||||
|
||||
ciphertext, err := encrptor.Encrypt(plaintext)
|
||||
if err != nil {
|
||||
t.Fatalf("failed to encrpt %s: %v", plaintext, err)
|
||||
}
|
||||
|
||||
cases := []struct {
|
||||
ciphertext string
|
||||
keyProvider comcfg.KeyProvider
|
||||
err bool
|
||||
}{
|
||||
{"", &fakeKeyProvider{"", errors.New("error")}, true},
|
||||
{ciphertext, &fakeKeyProvider{key, nil}, false},
|
||||
}
|
||||
|
||||
for _, c := range cases {
|
||||
encrptor := NewAESEncryptor(c.keyProvider, nil)
|
||||
str, err := encrptor.Decrypt(c.ciphertext)
|
||||
if c.err {
|
||||
assert.NotNil(t, err)
|
||||
} else {
|
||||
assert.Nil(t, err)
|
||||
assert.Equal(t, plaintext, str)
|
||||
}
|
||||
}
|
||||
}
|
@ -1,151 +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 database
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"strconv"
|
||||
|
||||
"github.com/goharbor/harbor/src/adminserver/systemcfg/store"
|
||||
"github.com/goharbor/harbor/src/common"
|
||||
"github.com/goharbor/harbor/src/common/dao"
|
||||
"github.com/goharbor/harbor/src/common/models"
|
||||
)
|
||||
|
||||
const (
|
||||
name = "database"
|
||||
)
|
||||
|
||||
var (
|
||||
numKeys = map[string]bool{
|
||||
common.EmailPort: true,
|
||||
common.LDAPScope: true,
|
||||
common.LDAPGroupSearchScope: true,
|
||||
common.LDAPTimeout: true,
|
||||
common.TokenExpiration: true,
|
||||
common.MaxJobWorkers: true,
|
||||
common.CfgExpiration: true,
|
||||
common.ClairDBPort: true,
|
||||
common.PostGreSQLPort: true,
|
||||
}
|
||||
boolKeys = map[string]bool{
|
||||
common.WithClair: true,
|
||||
common.WithNotary: true,
|
||||
common.SelfRegistration: true,
|
||||
common.EmailSSL: true,
|
||||
common.EmailInsecure: true,
|
||||
common.LDAPVerifyCert: true,
|
||||
common.UAAVerifyCert: true,
|
||||
common.ReadOnly: true,
|
||||
common.WithChartMuseum: true,
|
||||
}
|
||||
mapKeys = map[string]bool{
|
||||
common.ScanAllPolicy: true,
|
||||
}
|
||||
)
|
||||
|
||||
type cfgStore struct {
|
||||
name string
|
||||
}
|
||||
|
||||
// Name The name of the driver
|
||||
func (c *cfgStore) Name() string {
|
||||
return name
|
||||
}
|
||||
|
||||
// NewCfgStore New a cfg store for database driver
|
||||
func NewCfgStore() (store.Driver, error) {
|
||||
return &cfgStore{
|
||||
name: name,
|
||||
}, nil
|
||||
}
|
||||
|
||||
// Read configuration from database
|
||||
func (c *cfgStore) Read() (map[string]interface{}, error) {
|
||||
configEntries, err := dao.GetConfigEntries()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return WrapperConfig(configEntries)
|
||||
}
|
||||
|
||||
// WrapperConfig Wrapper the configuration
|
||||
func WrapperConfig(configEntries []*models.ConfigEntry) (map[string]interface{}, error) {
|
||||
config := make(map[string]interface{})
|
||||
for _, entry := range configEntries {
|
||||
if numKeys[entry.Key] {
|
||||
strvalue, err := strconv.Atoi(entry.Value)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
config[entry.Key] = float64(strvalue)
|
||||
} else if boolKeys[entry.Key] {
|
||||
strvalue, err := strconv.ParseBool(entry.Value)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
config[entry.Key] = strvalue
|
||||
} else if mapKeys[entry.Key] {
|
||||
m := map[string]interface{}{}
|
||||
if err := json.Unmarshal([]byte(entry.Value), &m); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
config[entry.Key] = m
|
||||
} else {
|
||||
config[entry.Key] = entry.Value
|
||||
}
|
||||
|
||||
}
|
||||
return config, nil
|
||||
}
|
||||
|
||||
// Write save configuration to database
|
||||
func (c *cfgStore) Write(config map[string]interface{}) error {
|
||||
configEntries, err := TranslateConfig(config)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
return dao.SaveConfigEntries(configEntries)
|
||||
}
|
||||
|
||||
// TranslateConfig Translate configuration from int, bool, float64 to string
|
||||
func TranslateConfig(config map[string]interface{}) ([]models.ConfigEntry, error) {
|
||||
var configEntries []models.ConfigEntry
|
||||
for k, v := range config {
|
||||
var entry = new(models.ConfigEntry)
|
||||
entry.Key = k
|
||||
switch v.(type) {
|
||||
case string:
|
||||
entry.Value = v.(string)
|
||||
case int:
|
||||
entry.Value = strconv.Itoa(v.(int))
|
||||
case bool:
|
||||
entry.Value = strconv.FormatBool(v.(bool))
|
||||
case float64:
|
||||
entry.Value = strconv.Itoa(int(v.(float64)))
|
||||
case map[string]interface{}:
|
||||
data, err := json.Marshal(v)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
entry.Value = string(data)
|
||||
default:
|
||||
return nil, fmt.Errorf("unknown type %v", v)
|
||||
}
|
||||
configEntries = append(configEntries, *entry)
|
||||
}
|
||||
return configEntries, nil
|
||||
}
|
@ -1,75 +0,0 @@
|
||||
package database
|
||||
|
||||
import (
|
||||
"testing"
|
||||
|
||||
"github.com/goharbor/harbor/src/common"
|
||||
"github.com/goharbor/harbor/src/common/models"
|
||||
"github.com/stretchr/testify/assert"
|
||||
)
|
||||
|
||||
func TestCfgStore_Name(t *testing.T) {
|
||||
driver, err := NewCfgStore()
|
||||
if err != nil {
|
||||
t.Fatalf("Failed to create db configuration store %v", err)
|
||||
}
|
||||
assert.Equal(t, name, driver.Name())
|
||||
}
|
||||
|
||||
func TestWrapperConfig(t *testing.T) {
|
||||
cfg := []*models.ConfigEntry{
|
||||
{
|
||||
Key: common.CfgExpiration,
|
||||
Value: "500",
|
||||
},
|
||||
{
|
||||
Key: common.WithNotary,
|
||||
Value: "true",
|
||||
},
|
||||
{
|
||||
Key: common.PostGreSQLHOST,
|
||||
Value: "192.168.1.210",
|
||||
},
|
||||
}
|
||||
result, err := WrapperConfig(cfg)
|
||||
if err != nil {
|
||||
t.Fatalf("Failed to wrapper config %v", err)
|
||||
}
|
||||
withNotary, _ := result[common.WithNotary].(bool)
|
||||
assert.Equal(t, true, withNotary)
|
||||
|
||||
postgresqlhost, ok := result[common.PostGreSQLHOST].(string)
|
||||
assert.True(t, ok)
|
||||
assert.Equal(t, "192.168.1.210", postgresqlhost)
|
||||
|
||||
expiration, ok := result[common.CfgExpiration].(float64)
|
||||
|
||||
assert.True(t, ok)
|
||||
assert.Equal(t, float64(500), expiration)
|
||||
}
|
||||
|
||||
func TestTranslateConfig(t *testing.T) {
|
||||
config := map[string]interface{}{}
|
||||
config[common.PostGreSQLHOST] = "192.168.1.210"
|
||||
|
||||
entries, err := TranslateConfig(config)
|
||||
if err != nil {
|
||||
t.Fatalf("Failed to translate configuration %v", err)
|
||||
}
|
||||
assert.Equal(t, "192.168.1.210", entries[0].Value)
|
||||
config = make(map[string]interface{})
|
||||
config[common.WithNotary] = true
|
||||
entries, err = TranslateConfig(config)
|
||||
if err != nil {
|
||||
t.Fatalf("Failed to translate configuration %v", err)
|
||||
}
|
||||
assert.Equal(t, "true", entries[0].Value)
|
||||
|
||||
config = make(map[string]interface{})
|
||||
config[common.CfgExpiration] = float64(500)
|
||||
entries, err = TranslateConfig(config)
|
||||
if err != nil {
|
||||
t.Fatalf("Failed to translate configuration %v", err)
|
||||
}
|
||||
assert.Equal(t, "500", entries[0].Value)
|
||||
}
|
@ -1,26 +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 store
|
||||
|
||||
// Driver defines methods that a configuration store driver must implement
|
||||
type Driver interface {
|
||||
// Name returns a human-readable name of the driver
|
||||
Name() string
|
||||
// Read reads all the configurations from store
|
||||
Read() (map[string]interface{}, error)
|
||||
// Write writes the configurations to store, the configurations can be
|
||||
// part of all
|
||||
Write(map[string]interface{}) error
|
||||
}
|
@ -1,97 +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 encrypt
|
||||
|
||||
import (
|
||||
"github.com/goharbor/harbor/src/adminserver/systemcfg/encrypt"
|
||||
"github.com/goharbor/harbor/src/adminserver/systemcfg/store"
|
||||
"github.com/goharbor/harbor/src/common/utils/log"
|
||||
)
|
||||
|
||||
const (
|
||||
name = "encrypt"
|
||||
)
|
||||
|
||||
// cfgStore wraps a store.Driver with an encryptor
|
||||
type cfgStore struct {
|
||||
// attrs need to be encrypted and decrypted
|
||||
keys []string
|
||||
encryptor encrypt.Encryptor
|
||||
store store.Driver
|
||||
}
|
||||
|
||||
// NewCfgStore returns an instance of cfgStore
|
||||
// keys are the attrs need to be encrypted or decrypted
|
||||
func NewCfgStore(encryptor encrypt.Encryptor,
|
||||
keys []string, store store.Driver) store.Driver {
|
||||
return &cfgStore{
|
||||
keys: keys,
|
||||
encryptor: encryptor,
|
||||
store: store,
|
||||
}
|
||||
}
|
||||
|
||||
func (c *cfgStore) Name() string {
|
||||
return name
|
||||
}
|
||||
|
||||
func (c *cfgStore) Read() (map[string]interface{}, error) {
|
||||
m, err := c.store.Read()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
for _, key := range c.keys {
|
||||
v, ok := m[key]
|
||||
if !ok {
|
||||
continue
|
||||
}
|
||||
|
||||
str, ok := v.(string)
|
||||
if !ok {
|
||||
log.Warningf("the value of %s is not string, skip decrypt", key)
|
||||
continue
|
||||
}
|
||||
|
||||
text, err := c.encryptor.Decrypt(str)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
m[key] = text
|
||||
}
|
||||
return m, nil
|
||||
}
|
||||
|
||||
func (c *cfgStore) Write(m map[string]interface{}) error {
|
||||
for _, key := range c.keys {
|
||||
v, ok := m[key]
|
||||
if !ok {
|
||||
continue
|
||||
}
|
||||
|
||||
str, ok := v.(string)
|
||||
if !ok {
|
||||
log.Warningf("%v is not string, skip encrypt", v)
|
||||
continue
|
||||
}
|
||||
|
||||
ciphertext, err := c.encryptor.Encrypt(str)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
m[key] = ciphertext
|
||||
}
|
||||
return c.store.Write(m)
|
||||
}
|
@ -1,81 +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 encrypt
|
||||
|
||||
import (
|
||||
"testing"
|
||||
|
||||
"github.com/stretchr/testify/assert"
|
||||
)
|
||||
|
||||
type fakeCfgStore struct {
|
||||
cfgs map[string]interface{}
|
||||
}
|
||||
|
||||
func (f *fakeCfgStore) Name() string {
|
||||
return "fake"
|
||||
}
|
||||
|
||||
func (f *fakeCfgStore) Read() (map[string]interface{}, error) {
|
||||
return f.cfgs, nil
|
||||
}
|
||||
|
||||
func (f *fakeCfgStore) Write(cfgs map[string]interface{}) error {
|
||||
f.cfgs = cfgs
|
||||
return nil
|
||||
}
|
||||
|
||||
type fakeEncryptor struct {
|
||||
}
|
||||
|
||||
func (f *fakeEncryptor) Encrypt(plaintext string) (string, error) {
|
||||
return "encrypted" + plaintext, nil
|
||||
}
|
||||
|
||||
func (f *fakeEncryptor) Decrypt(ciphertext string) (string, error) {
|
||||
return "decrypted" + ciphertext, nil
|
||||
}
|
||||
|
||||
func TestName(t *testing.T) {
|
||||
driver := NewCfgStore(nil, nil, nil)
|
||||
assert.Equal(t, name, driver.Name())
|
||||
}
|
||||
|
||||
func TestRead(t *testing.T) {
|
||||
keys := []string{"key"}
|
||||
driver := NewCfgStore(&fakeEncryptor{}, keys, &fakeCfgStore{
|
||||
cfgs: map[string]interface{}{"key": "value"},
|
||||
})
|
||||
|
||||
cfgs, err := driver.Read()
|
||||
assert.Nil(t, err)
|
||||
assert.Equal(t, "decryptedvalue", cfgs["key"])
|
||||
}
|
||||
|
||||
func TestWrite(t *testing.T) {
|
||||
keys := []string{"key"}
|
||||
store := &fakeCfgStore{
|
||||
cfgs: map[string]interface{}{},
|
||||
}
|
||||
driver := NewCfgStore(&fakeEncryptor{}, keys, store)
|
||||
|
||||
cfgs := map[string]interface{}{
|
||||
"key": "value",
|
||||
}
|
||||
|
||||
err := driver.Write(cfgs)
|
||||
assert.Nil(t, err)
|
||||
assert.Equal(t, "encryptedvalue", store.cfgs["key"])
|
||||
}
|
@ -1,119 +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 json
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
"io/ioutil"
|
||||
"os"
|
||||
"path/filepath"
|
||||
"sync"
|
||||
|
||||
"github.com/goharbor/harbor/src/adminserver/systemcfg/store"
|
||||
"github.com/goharbor/harbor/src/common/utils/log"
|
||||
)
|
||||
|
||||
const (
|
||||
// the default path of configuration file
|
||||
defaultPath = "/etc/harbor/config.json"
|
||||
)
|
||||
|
||||
type cfgStore struct {
|
||||
path string // the path of cfg file
|
||||
sync.RWMutex
|
||||
}
|
||||
|
||||
// NewCfgStore returns an instance of cfgStore that stores the configurations
|
||||
// in a json file. The file will be created if it does not exist.
|
||||
func NewCfgStore(path ...string) (store.Driver, error) {
|
||||
p := defaultPath
|
||||
if len(path) > 0 && len(path[0]) > 0 {
|
||||
p = path[0]
|
||||
}
|
||||
|
||||
log.Debugf("path of configuration file: %s", p)
|
||||
|
||||
if _, err := os.Stat(p); os.IsNotExist(err) {
|
||||
log.Infof("the configuration file %s does not exist, creating it...", p)
|
||||
if err = os.MkdirAll(filepath.Dir(p), 0600); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if err = ioutil.WriteFile(p, []byte{}, 0600); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
}
|
||||
|
||||
return &cfgStore{
|
||||
path: p,
|
||||
}, nil
|
||||
}
|
||||
|
||||
// Name ...
|
||||
func (c *cfgStore) Name() string {
|
||||
return "JSON"
|
||||
}
|
||||
|
||||
// Read ...
|
||||
func (c *cfgStore) Read() (map[string]interface{}, error) {
|
||||
c.RLock()
|
||||
defer c.RUnlock()
|
||||
|
||||
return read(c.path)
|
||||
}
|
||||
|
||||
func read(path string) (map[string]interface{}, error) {
|
||||
b, err := ioutil.ReadFile(path)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
// empty file
|
||||
if len(b) == 0 {
|
||||
return nil, nil
|
||||
}
|
||||
|
||||
config := map[string]interface{}{}
|
||||
if err = json.Unmarshal(b, &config); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return config, nil
|
||||
}
|
||||
|
||||
// Write ...
|
||||
func (c *cfgStore) Write(config map[string]interface{}) error {
|
||||
c.Lock()
|
||||
defer c.Unlock()
|
||||
|
||||
cfg, err := read(c.path)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
if cfg == nil {
|
||||
cfg = config
|
||||
} else {
|
||||
for k, v := range config {
|
||||
cfg[k] = v
|
||||
}
|
||||
}
|
||||
|
||||
b, err := json.MarshalIndent(cfg, "", " ")
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
return ioutil.WriteFile(c.path, b, 0600)
|
||||
}
|
@ -1,51 +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 json
|
||||
|
||||
import (
|
||||
"os"
|
||||
"testing"
|
||||
)
|
||||
|
||||
func TestReadWrite(t *testing.T) {
|
||||
path := "/tmp/config.json"
|
||||
store, err := NewCfgStore(path)
|
||||
if err != nil {
|
||||
t.Fatalf("failed to create json cfg store: %v", err)
|
||||
}
|
||||
defer func() {
|
||||
if err := os.Remove(path); err != nil {
|
||||
t.Fatalf("failed to remove the json file %s: %v", path, err)
|
||||
}
|
||||
}()
|
||||
|
||||
if store.Name() != "JSON" {
|
||||
t.Errorf("unexpected name: %s != %s", store.Name(), "JSON")
|
||||
return
|
||||
}
|
||||
|
||||
config := map[string]interface{}{
|
||||
"key": "value",
|
||||
}
|
||||
if err := store.Write(config); err != nil {
|
||||
t.Errorf("failed to write configurations to json file: %v", err)
|
||||
return
|
||||
}
|
||||
|
||||
if _, err = store.Read(); err != nil {
|
||||
t.Errorf("failed to read configurations from json file: %v", err)
|
||||
return
|
||||
}
|
||||
}
|
@ -1,483 +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 systemcfg
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"os"
|
||||
"regexp"
|
||||
"strconv"
|
||||
"strings"
|
||||
|
||||
enpt "github.com/goharbor/harbor/src/adminserver/systemcfg/encrypt"
|
||||
"github.com/goharbor/harbor/src/adminserver/systemcfg/store"
|
||||
"github.com/goharbor/harbor/src/adminserver/systemcfg/store/database"
|
||||
"github.com/goharbor/harbor/src/adminserver/systemcfg/store/encrypt"
|
||||
"github.com/goharbor/harbor/src/adminserver/systemcfg/store/json"
|
||||
"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/models"
|
||||
"github.com/goharbor/harbor/src/common/utils"
|
||||
"github.com/goharbor/harbor/src/common/utils/log"
|
||||
)
|
||||
|
||||
const (
|
||||
defaultJSONCfgStorePath string = "/etc/adminserver/config/config.json"
|
||||
defaultKeyPath string = "/etc/adminserver/key"
|
||||
ldapScopeKey string = "ldap_scope"
|
||||
)
|
||||
|
||||
var (
|
||||
// CfgStore is a storage driver that configurations
|
||||
// can be read from and wrote to
|
||||
CfgStore store.Driver
|
||||
|
||||
// attrs need to be encrypted or decrypted
|
||||
attrs = []string{
|
||||
common.EmailPassword,
|
||||
common.LDAPSearchPwd,
|
||||
common.PostGreSQLPassword,
|
||||
common.AdminInitialPassword,
|
||||
common.ClairDBPassword,
|
||||
common.UAAClientSecret,
|
||||
}
|
||||
|
||||
// all configurations need read from environment variables
|
||||
allEnvs = map[string]interface{}{
|
||||
common.ExtEndpoint: "EXT_ENDPOINT",
|
||||
common.AUTHMode: "AUTH_MODE",
|
||||
common.SelfRegistration: &parser{
|
||||
env: "SELF_REGISTRATION",
|
||||
parse: parseStringToBool,
|
||||
},
|
||||
common.DatabaseType: "DATABASE_TYPE",
|
||||
common.PostGreSQLHOST: "POSTGRESQL_HOST",
|
||||
common.PostGreSQLPort: &parser{
|
||||
env: "POSTGRESQL_PORT",
|
||||
parse: parseStringToInt,
|
||||
},
|
||||
common.PostGreSQLUsername: "POSTGRESQL_USERNAME",
|
||||
common.PostGreSQLPassword: "POSTGRESQL_PASSWORD",
|
||||
common.PostGreSQLDatabase: "POSTGRESQL_DATABASE",
|
||||
common.PostGreSQLSSLMode: "POSTGRESQL_SSLMODE",
|
||||
common.LDAPURL: "LDAP_URL",
|
||||
common.LDAPSearchDN: "LDAP_SEARCH_DN",
|
||||
common.LDAPSearchPwd: "LDAP_SEARCH_PWD",
|
||||
common.LDAPBaseDN: "LDAP_BASE_DN",
|
||||
common.LDAPFilter: "LDAP_FILTER",
|
||||
common.LDAPUID: "LDAP_UID",
|
||||
common.LDAPScope: &parser{
|
||||
env: "LDAP_SCOPE",
|
||||
parse: parseStringToInt,
|
||||
},
|
||||
common.LDAPTimeout: &parser{
|
||||
env: "LDAP_TIMEOUT",
|
||||
parse: parseStringToInt,
|
||||
},
|
||||
common.LDAPVerifyCert: &parser{
|
||||
env: "LDAP_VERIFY_CERT",
|
||||
parse: parseStringToBool,
|
||||
},
|
||||
common.LDAPGroupBaseDN: "LDAP_GROUP_BASEDN",
|
||||
common.LDAPGroupSearchFilter: "LDAP_GROUP_FILTER",
|
||||
common.LDAPGroupAttributeName: "LDAP_GROUP_GID",
|
||||
common.LDAPGroupSearchScope: &parser{
|
||||
env: "LDAP_GROUP_SCOPE",
|
||||
parse: parseStringToInt,
|
||||
},
|
||||
common.EmailHost: "EMAIL_HOST",
|
||||
common.EmailPort: &parser{
|
||||
env: "EMAIL_PORT",
|
||||
parse: parseStringToInt,
|
||||
},
|
||||
common.EmailUsername: "EMAIL_USR",
|
||||
common.EmailPassword: "EMAIL_PWD",
|
||||
common.EmailSSL: &parser{
|
||||
env: "EMAIL_SSL",
|
||||
parse: parseStringToBool,
|
||||
},
|
||||
common.EmailInsecure: &parser{
|
||||
env: "EMAIL_INSECURE",
|
||||
parse: parseStringToBool,
|
||||
},
|
||||
common.EmailFrom: "EMAIL_FROM",
|
||||
common.EmailIdentity: "EMAIL_IDENTITY",
|
||||
common.RegistryURL: "REGISTRY_URL",
|
||||
common.TokenExpiration: &parser{
|
||||
env: "TOKEN_EXPIRATION",
|
||||
parse: parseStringToInt,
|
||||
},
|
||||
common.CfgExpiration: &parser{
|
||||
env: "CFG_EXPIRATION",
|
||||
parse: parseStringToInt,
|
||||
},
|
||||
common.MaxJobWorkers: &parser{
|
||||
env: "MAX_JOB_WORKERS",
|
||||
parse: parseStringToInt,
|
||||
},
|
||||
common.ProjectCreationRestriction: "PROJECT_CREATION_RESTRICTION",
|
||||
common.AdminInitialPassword: "HARBOR_ADMIN_PASSWORD",
|
||||
common.AdmiralEndpoint: "ADMIRAL_URL",
|
||||
common.WithNotary: &parser{
|
||||
env: "WITH_NOTARY",
|
||||
parse: parseStringToBool,
|
||||
},
|
||||
common.WithClair: &parser{
|
||||
env: "WITH_CLAIR",
|
||||
parse: parseStringToBool,
|
||||
},
|
||||
common.ClairDBPassword: "CLAIR_DB_PASSWORD",
|
||||
common.ClairDB: "CLAIR_DB",
|
||||
common.ClairDBUsername: "CLAIR_DB_USERNAME",
|
||||
common.ClairDBHost: "CLAIR_DB_HOST",
|
||||
common.ClairDBPort: &parser{
|
||||
env: "CLAIR_DB_PORT",
|
||||
parse: parseStringToInt,
|
||||
},
|
||||
common.ClairDBSSLMode: "CLAIR_DB_SSLMODE",
|
||||
common.UAAEndpoint: "UAA_ENDPOINT",
|
||||
common.UAAClientID: "UAA_CLIENTID",
|
||||
common.UAAClientSecret: "UAA_CLIENTSECRET",
|
||||
common.UAAVerifyCert: &parser{
|
||||
env: "UAA_VERIFY_CERT",
|
||||
parse: parseStringToBool,
|
||||
},
|
||||
common.CoreURL: "CORE_URL",
|
||||
common.JobServiceURL: "JOBSERVICE_URL",
|
||||
common.TokenServiceURL: "TOKEN_SERVICE_URL",
|
||||
common.ClairURL: "CLAIR_URL",
|
||||
common.NotaryURL: "NOTARY_URL",
|
||||
common.RegistryStorageProviderName: "REGISTRY_STORAGE_PROVIDER_NAME",
|
||||
common.ReadOnly: &parser{
|
||||
env: "READ_ONLY",
|
||||
parse: parseStringToBool,
|
||||
},
|
||||
common.ReloadKey: "RELOAD_KEY",
|
||||
common.LdapGroupAdminDn: "LDAP_GROUP_ADMIN_DN",
|
||||
common.ChartRepoURL: "CHART_REPOSITORY_URL",
|
||||
common.WithChartMuseum: &parser{
|
||||
env: "WITH_CHARTMUSEUM",
|
||||
parse: parseStringToBool,
|
||||
},
|
||||
}
|
||||
|
||||
// configurations need read from environment variables
|
||||
// every time the system startup
|
||||
repeatLoadEnvs = map[string]interface{}{
|
||||
common.ExtEndpoint: "EXT_ENDPOINT",
|
||||
common.PostGreSQLHOST: "POSTGRESQL_HOST",
|
||||
common.PostGreSQLPort: &parser{
|
||||
env: "POSTGRESQL_PORT",
|
||||
parse: parseStringToInt,
|
||||
},
|
||||
common.PostGreSQLUsername: "POSTGRESQL_USERNAME",
|
||||
common.PostGreSQLPassword: "POSTGRESQL_PASSWORD",
|
||||
common.PostGreSQLDatabase: "POSTGRESQL_DATABASE",
|
||||
common.PostGreSQLSSLMode: "POSTGRESQL_SSLMODE",
|
||||
common.MaxJobWorkers: &parser{
|
||||
env: "MAX_JOB_WORKERS",
|
||||
parse: parseStringToInt,
|
||||
},
|
||||
common.CfgExpiration: &parser{
|
||||
env: "CFG_EXPIRATION",
|
||||
parse: parseStringToInt,
|
||||
},
|
||||
common.AdmiralEndpoint: "ADMIRAL_URL",
|
||||
common.WithNotary: &parser{
|
||||
env: "WITH_NOTARY",
|
||||
parse: parseStringToBool,
|
||||
},
|
||||
common.WithClair: &parser{
|
||||
env: "WITH_CLAIR",
|
||||
parse: parseStringToBool,
|
||||
},
|
||||
common.ClairDBPassword: "CLAIR_DB_PASSWORD",
|
||||
common.ClairDBHost: "CLAIR_DB_HOST",
|
||||
common.ClairDBUsername: "CLAIR_DB_USERNAME",
|
||||
common.ClairDBPort: &parser{
|
||||
env: "CLAIR_DB_PORT",
|
||||
parse: parseStringToInt,
|
||||
},
|
||||
common.ClairDBSSLMode: "CLAIR_DB_SSLMODE",
|
||||
common.UAAEndpoint: "UAA_ENDPOINT",
|
||||
common.UAAClientID: "UAA_CLIENTID",
|
||||
common.UAAClientSecret: "UAA_CLIENTSECRET",
|
||||
common.UAAVerifyCert: &parser{
|
||||
env: "UAA_VERIFY_CERT",
|
||||
parse: parseStringToBool,
|
||||
},
|
||||
common.RegistryStorageProviderName: "REGISTRY_STORAGE_PROVIDER_NAME",
|
||||
common.CoreURL: "CORE_URL",
|
||||
common.JobServiceURL: "JOBSERVICE_URL",
|
||||
common.RegistryURL: "REGISTRY_URL",
|
||||
common.TokenServiceURL: "TOKEN_SERVICE_URL",
|
||||
common.ClairURL: "CLAIR_URL",
|
||||
common.NotaryURL: "NOTARY_URL",
|
||||
common.DatabaseType: "DATABASE_TYPE",
|
||||
common.ChartRepoURL: "CHART_REPOSITORY_URL",
|
||||
common.WithChartMuseum: &parser{
|
||||
env: "WITH_CHARTMUSEUM",
|
||||
parse: parseStringToBool,
|
||||
},
|
||||
}
|
||||
)
|
||||
|
||||
type parser struct {
|
||||
// the name of env
|
||||
env string
|
||||
// parse the value of env, e.g. parse string to int or
|
||||
// parse string to bool
|
||||
parse func(string) (interface{}, error)
|
||||
}
|
||||
|
||||
func parseStringToInt(str string) (interface{}, error) {
|
||||
if len(str) == 0 {
|
||||
return 0, nil
|
||||
}
|
||||
return strconv.Atoi(str)
|
||||
}
|
||||
|
||||
func parseStringToBool(str string) (interface{}, error) {
|
||||
return strings.ToLower(str) == "true" ||
|
||||
strings.ToLower(str) == "on", nil
|
||||
}
|
||||
|
||||
// Init system configurations. If env RESET is set or configurations
|
||||
// read from storage driver is null, load all configurations from env
|
||||
func Init() (err error) {
|
||||
// init database
|
||||
envCfgs := map[string]interface{}{}
|
||||
if err := LoadFromEnv(envCfgs, true); err != nil {
|
||||
return err
|
||||
}
|
||||
db := GetDatabaseFromCfg(envCfgs)
|
||||
if err := dao.InitDatabase(db); err != nil {
|
||||
return err
|
||||
}
|
||||
if err := dao.UpgradeSchema(db); err != nil {
|
||||
return err
|
||||
}
|
||||
if err := dao.CheckSchemaVersion(); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
if err := initCfgStore(); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
// Use reload key to avoid reset customed setting after restart
|
||||
curCfgs, err := CfgStore.Read()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
loadAll := isLoadAll(curCfgs)
|
||||
if curCfgs == nil {
|
||||
curCfgs = map[string]interface{}{}
|
||||
}
|
||||
// restart: only repeatload envs will be load
|
||||
// reload_config: all envs will be reload except the skiped envs
|
||||
if err = LoadFromEnv(curCfgs, loadAll); err != nil {
|
||||
return err
|
||||
}
|
||||
AddMissedKey(curCfgs)
|
||||
return CfgStore.Write(curCfgs)
|
||||
}
|
||||
|
||||
func isLoadAll(cfg map[string]interface{}) bool {
|
||||
return cfg == nil || strings.EqualFold(os.Getenv("RESET"), "true") && os.Getenv("RELOAD_KEY") != cfg[common.ReloadKey]
|
||||
}
|
||||
|
||||
func initCfgStore() (err error) {
|
||||
|
||||
drivertype := os.Getenv("CFG_DRIVER")
|
||||
if len(drivertype) == 0 {
|
||||
drivertype = common.CfgDriverDB
|
||||
}
|
||||
path := os.Getenv("JSON_CFG_STORE_PATH")
|
||||
if len(path) == 0 {
|
||||
path = defaultJSONCfgStorePath
|
||||
}
|
||||
log.Infof("the path of json configuration storage: %s", path)
|
||||
|
||||
if drivertype == common.CfgDriverDB {
|
||||
CfgStore, err = database.NewCfgStore()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
// migration check: if no data in the db , then will try to load from path
|
||||
m, err := CfgStore.Read()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if m == nil || len(m) == 0 {
|
||||
if _, err := os.Stat(path); err == nil {
|
||||
jsondriver, err := json.NewCfgStore(path)
|
||||
if err != nil {
|
||||
log.Errorf("Failed to migrate configuration from %s", path)
|
||||
return err
|
||||
}
|
||||
jsonconfig, err := jsondriver.Read()
|
||||
if err != nil {
|
||||
log.Errorf("Failed to read old configuration from %s", path)
|
||||
return err
|
||||
}
|
||||
// Update LDAP Scope for migration
|
||||
// only used when migrating harbor release before v1.3
|
||||
// after v1.3 there is always a db configuration before migrate.
|
||||
validLdapScope(jsonconfig, true)
|
||||
err = CfgStore.Write(jsonconfig)
|
||||
if err != nil {
|
||||
log.Error("Failed to update old configuration to database")
|
||||
return err
|
||||
}
|
||||
}
|
||||
}
|
||||
} else {
|
||||
CfgStore, err = json.NewCfgStore(path)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
kp := os.Getenv("KEY_PATH")
|
||||
if len(kp) == 0 {
|
||||
kp = defaultKeyPath
|
||||
}
|
||||
log.Infof("the path of key used by key provider: %s", kp)
|
||||
|
||||
encryptor := enpt.NewAESEncryptor(
|
||||
comcfg.NewFileKeyProvider(kp), nil)
|
||||
|
||||
CfgStore = encrypt.NewCfgStore(encryptor, attrs, CfgStore)
|
||||
return nil
|
||||
}
|
||||
|
||||
// LoadFromEnv loads the configurations from allEnvs, if all is false, it just loads
|
||||
// the repeatLoadEnvs and the env which is absent in cfgs
|
||||
func LoadFromEnv(cfgs map[string]interface{}, all bool) error {
|
||||
var envs map[string]interface{}
|
||||
|
||||
if all {
|
||||
envs = allEnvs
|
||||
} else {
|
||||
envs = make(map[string]interface{})
|
||||
for k, v := range repeatLoadEnvs {
|
||||
envs[k] = v
|
||||
}
|
||||
for k, v := range allEnvs {
|
||||
if _, exist := cfgs[k]; !exist {
|
||||
envs[k] = v
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
reloadCfg := os.Getenv("RESET")
|
||||
skipPattern := os.Getenv("SKIP_RELOAD_ENV_PATTERN")
|
||||
skipPattern = strings.TrimSpace(skipPattern)
|
||||
if len(skipPattern) == 0 {
|
||||
skipPattern = "$^" // doesn't match any string by default
|
||||
}
|
||||
skipMatcher, err := regexp.Compile(skipPattern)
|
||||
if err != nil {
|
||||
log.Errorf("Regular express parse error, skipPattern:%v", skipPattern)
|
||||
skipMatcher = regexp.MustCompile("$^")
|
||||
}
|
||||
|
||||
for k, v := range envs {
|
||||
if str, ok := v.(string); ok {
|
||||
if skipMatcher.MatchString(str) && strings.EqualFold(reloadCfg, "true") {
|
||||
continue
|
||||
}
|
||||
cfgs[k] = os.Getenv(str)
|
||||
continue
|
||||
}
|
||||
|
||||
if parser, ok := v.(*parser); ok {
|
||||
if skipMatcher.MatchString(parser.env) && strings.EqualFold(reloadCfg, "true") {
|
||||
continue
|
||||
}
|
||||
i, err := parser.parse(os.Getenv(parser.env))
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
cfgs[k] = i
|
||||
continue
|
||||
}
|
||||
|
||||
return fmt.Errorf("%v is not string or parse type", v)
|
||||
}
|
||||
validLdapScope(cfgs, false)
|
||||
return nil
|
||||
}
|
||||
|
||||
// GetDatabaseFromCfg Create database object from config
|
||||
func GetDatabaseFromCfg(cfg map[string]interface{}) *models.Database {
|
||||
database := &models.Database{}
|
||||
database.Type = cfg[common.DatabaseType].(string)
|
||||
postgresql := &models.PostGreSQL{}
|
||||
postgresql.Host = utils.SafeCastString(cfg[common.PostGreSQLHOST])
|
||||
postgresql.Port = int(utils.SafeCastInt(cfg[common.PostGreSQLPort]))
|
||||
postgresql.Username = utils.SafeCastString(cfg[common.PostGreSQLUsername])
|
||||
postgresql.Password = utils.SafeCastString(cfg[common.PostGreSQLPassword])
|
||||
postgresql.Database = utils.SafeCastString(cfg[common.PostGreSQLDatabase])
|
||||
postgresql.SSLMode = utils.SafeCastString(cfg[common.PostGreSQLSSLMode])
|
||||
database.PostGreSQL = postgresql
|
||||
return database
|
||||
}
|
||||
|
||||
// Valid LDAP Scope
|
||||
func validLdapScope(cfg map[string]interface{}, isMigrate bool) {
|
||||
ldapScope, ok := cfg[ldapScopeKey].(int)
|
||||
if !ok {
|
||||
ldapScopeFloat, ok := cfg[ldapScopeKey].(float64)
|
||||
if ok {
|
||||
ldapScope = int(ldapScopeFloat)
|
||||
}
|
||||
}
|
||||
if isMigrate && ldapScope > 0 && ldapScope < 3 {
|
||||
ldapScope = ldapScope - 1
|
||||
}
|
||||
if ldapScope >= 3 {
|
||||
ldapScope = 2
|
||||
}
|
||||
if ldapScope < 0 {
|
||||
ldapScope = 0
|
||||
}
|
||||
cfg[ldapScopeKey] = ldapScope
|
||||
|
||||
}
|
||||
|
||||
// AddMissedKey ... If the configure key is missing in the cfg map, add default value to it
|
||||
func AddMissedKey(cfg map[string]interface{}) {
|
||||
|
||||
for k, v := range common.HarborStringKeysMap {
|
||||
if _, exist := cfg[k]; !exist {
|
||||
cfg[k] = v
|
||||
}
|
||||
}
|
||||
|
||||
for k, v := range common.HarborNumKeysMap {
|
||||
if _, exist := cfg[k]; !exist {
|
||||
cfg[k] = v
|
||||
}
|
||||
}
|
||||
|
||||
for k, v := range common.HarborBoolKeysMap {
|
||||
if _, exist := cfg[k]; !exist {
|
||||
cfg[k] = v
|
||||
}
|
||||
}
|
||||
|
||||
}
|
@ -1,290 +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 systemcfg
|
||||
|
||||
import (
|
||||
"os"
|
||||
"testing"
|
||||
|
||||
"github.com/goharbor/harbor/src/common"
|
||||
"github.com/stretchr/testify/assert"
|
||||
)
|
||||
|
||||
func TestParseStringToInt(t *testing.T) {
|
||||
cases := []struct {
|
||||
input string
|
||||
result int
|
||||
}{
|
||||
{"1", 1},
|
||||
{"-1", -1},
|
||||
{"0", 0},
|
||||
{"", 0},
|
||||
}
|
||||
|
||||
for _, c := range cases {
|
||||
i, err := parseStringToInt(c.input)
|
||||
assert.Nil(t, err)
|
||||
assert.Equal(t, c.result, i)
|
||||
}
|
||||
}
|
||||
|
||||
func TestParseStringToBool(t *testing.T) {
|
||||
cases := []struct {
|
||||
input string
|
||||
result bool
|
||||
}{
|
||||
{"true", true},
|
||||
{"on", true},
|
||||
{"TRUE", true},
|
||||
{"ON", true},
|
||||
{"other", false},
|
||||
{"", false},
|
||||
}
|
||||
|
||||
for _, c := range cases {
|
||||
b, _ := parseStringToBool(c.input)
|
||||
assert.Equal(t, c.result, b)
|
||||
}
|
||||
}
|
||||
|
||||
func TestInitCfgStore(t *testing.T) {
|
||||
os.Clearenv()
|
||||
path := "/tmp/config.json"
|
||||
if err := os.Setenv("CFG_DRIVER", "json"); err != nil {
|
||||
t.Fatalf("failed to set env: %v", err)
|
||||
}
|
||||
if err := os.Setenv("JSON_CFG_STORE_PATH", path); err != nil {
|
||||
t.Fatalf("failed to set env: %v", err)
|
||||
}
|
||||
defer os.RemoveAll(path)
|
||||
err := initCfgStore()
|
||||
assert.Nil(t, err)
|
||||
}
|
||||
|
||||
func TestLoadFromEnv(t *testing.T) {
|
||||
os.Clearenv()
|
||||
ldapURL := "ldap://ldap.com"
|
||||
extEndpoint := "http://harbor.com"
|
||||
if err := os.Setenv("LDAP_URL", ldapURL); err != nil {
|
||||
t.Fatalf("failed to set env: %v", err)
|
||||
}
|
||||
cfgs := map[string]interface{}{}
|
||||
err := LoadFromEnv(cfgs, true)
|
||||
assert.Nil(t, err)
|
||||
assert.Equal(t, ldapURL, cfgs[common.LDAPURL])
|
||||
|
||||
os.Clearenv()
|
||||
if err := os.Setenv("LDAP_URL", ldapURL); err != nil {
|
||||
t.Fatalf("failed to set env: %v", err)
|
||||
}
|
||||
if err := os.Setenv("EXT_ENDPOINT", extEndpoint); err != nil {
|
||||
t.Fatalf("failed to set env: %v", err)
|
||||
}
|
||||
|
||||
if err := os.Setenv("LDAP_VERIFY_CERT", "false"); err != nil {
|
||||
t.Fatalf("failed to set env: %v", err)
|
||||
}
|
||||
|
||||
cfgs = map[string]interface{}{}
|
||||
err = LoadFromEnv(cfgs, false)
|
||||
assert.Nil(t, err)
|
||||
assert.Equal(t, extEndpoint, cfgs[common.ExtEndpoint])
|
||||
assert.Equal(t, ldapURL, cfgs[common.LDAPURL])
|
||||
assert.Equal(t, false, cfgs[common.LDAPVerifyCert])
|
||||
|
||||
os.Clearenv()
|
||||
if err := os.Setenv("LDAP_URL", ldapURL); err != nil {
|
||||
t.Fatalf("failed to set env: %v", err)
|
||||
}
|
||||
if err := os.Setenv("EXT_ENDPOINT", extEndpoint); err != nil {
|
||||
t.Fatalf("failed to set env: %v", err)
|
||||
}
|
||||
|
||||
if err := os.Setenv("LDAP_VERIFY_CERT", "true"); err != nil {
|
||||
t.Fatalf("failed to set env: %v", err)
|
||||
}
|
||||
|
||||
cfgs = map[string]interface{}{
|
||||
common.LDAPURL: "ldap_url",
|
||||
}
|
||||
err = LoadFromEnv(cfgs, false)
|
||||
assert.Nil(t, err)
|
||||
assert.Equal(t, extEndpoint, cfgs[common.ExtEndpoint])
|
||||
assert.Equal(t, "ldap_url", cfgs[common.LDAPURL])
|
||||
assert.Equal(t, true, cfgs[common.LDAPVerifyCert])
|
||||
|
||||
}
|
||||
|
||||
func TestIsLoadAll(t *testing.T) {
|
||||
os.Clearenv()
|
||||
if err := os.Setenv("RELOAD_KEY", "123456"); err != nil {
|
||||
t.Fatalf("failed to set env: %v", err)
|
||||
}
|
||||
if err := os.Setenv("RESET", "True"); err != nil {
|
||||
t.Fatalf("failed to set env: %v", err)
|
||||
}
|
||||
cfg1 := map[string]interface{}{common.ReloadKey: "123456"}
|
||||
cfg2 := map[string]interface{}{common.ReloadKey: "654321"}
|
||||
assert.False(t, isLoadAll(cfg1))
|
||||
assert.True(t, isLoadAll(cfg2))
|
||||
}
|
||||
|
||||
func TestLoadFromEnvWithReloadConfigInvalidSkipPattern(t *testing.T) {
|
||||
os.Clearenv()
|
||||
ldapURL := "ldap://ldap.com"
|
||||
extEndpoint := "http://harbor.com"
|
||||
cfgsReload := map[string]interface{}{
|
||||
common.LDAPURL: "ldap_url",
|
||||
}
|
||||
if err := os.Setenv("LDAP_URL", ldapURL); err != nil {
|
||||
t.Fatalf("failed to set env: %v", err)
|
||||
}
|
||||
if err := os.Setenv("EXT_ENDPOINT", extEndpoint); err != nil {
|
||||
t.Fatalf("failed to set env: %v", err)
|
||||
}
|
||||
|
||||
if err := os.Setenv("LDAP_VERIFY_CERT", "false"); err != nil {
|
||||
t.Fatalf("failed to set env: %v", err)
|
||||
}
|
||||
|
||||
if err := os.Setenv("SKIP_RELOAD_ENV_PATTERN", "a(b"); err != nil {
|
||||
t.Fatalf("failed to set env: %v", err)
|
||||
}
|
||||
err := LoadFromEnv(cfgsReload, true)
|
||||
if err != nil {
|
||||
t.Fatalf("failed to load From env: %v", err)
|
||||
}
|
||||
assert.Equal(t, ldapURL, cfgsReload[common.LDAPURL])
|
||||
|
||||
os.Clearenv()
|
||||
|
||||
}
|
||||
|
||||
func TestLoadFromEnvWithReloadConfigSkipPattern(t *testing.T) {
|
||||
os.Clearenv()
|
||||
ldapURL := "ldap://ldap.com"
|
||||
extEndpoint := "http://harbor.com"
|
||||
cfgsReload := map[string]interface{}{
|
||||
common.LDAPURL: "ldap_url",
|
||||
}
|
||||
if err := os.Setenv("LDAP_URL", ldapURL); err != nil {
|
||||
t.Fatalf("failed to set env: %v", err)
|
||||
}
|
||||
if err := os.Setenv("EXT_ENDPOINT", extEndpoint); err != nil {
|
||||
t.Fatalf("failed to set env: %v", err)
|
||||
}
|
||||
|
||||
if err := os.Setenv("LDAP_VERIFY_CERT", "false"); err != nil {
|
||||
t.Fatalf("failed to set env: %v", err)
|
||||
}
|
||||
if err := os.Setenv("SKIP_RELOAD_ENV_PATTERN", "^LDAP.*"); err != nil {
|
||||
t.Fatalf("failed to set env: %v", err)
|
||||
}
|
||||
if err := os.Setenv("RESET", "true"); err != nil {
|
||||
t.Fatalf("failed to set env: %v", err)
|
||||
}
|
||||
err := LoadFromEnv(cfgsReload, false)
|
||||
if err != nil {
|
||||
t.Fatalf("failed to load From env: %v", err)
|
||||
}
|
||||
assert.Equal(t, "ldap_url", cfgsReload[common.LDAPURL]) // env value ignored
|
||||
|
||||
os.Clearenv()
|
||||
|
||||
}
|
||||
func TestGetDatabaseFromCfg(t *testing.T) {
|
||||
cfg := map[string]interface{}{
|
||||
common.DatabaseType: "postgresql",
|
||||
common.PostGreSQLDatabase: "registry",
|
||||
common.PostGreSQLHOST: "127.0.0.1",
|
||||
common.PostGreSQLPort: 5432,
|
||||
common.PostGreSQLPassword: "root123",
|
||||
common.PostGreSQLUsername: "postgres",
|
||||
}
|
||||
|
||||
database := GetDatabaseFromCfg(cfg)
|
||||
|
||||
assert.Equal(t, "postgresql", database.Type)
|
||||
}
|
||||
|
||||
func TestValidLdapScope(t *testing.T) {
|
||||
var dbValue float64
|
||||
dbValue = 2
|
||||
ldapScopeKey := "ldap_scope"
|
||||
testCfgs := []struct {
|
||||
config map[string]interface{}
|
||||
migrate bool
|
||||
ldapScopeResult int
|
||||
}{
|
||||
{map[string]interface{}{
|
||||
ldapScopeKey: 1,
|
||||
}, true, 0},
|
||||
{map[string]interface{}{
|
||||
ldapScopeKey: 2,
|
||||
}, true, 1},
|
||||
{map[string]interface{}{
|
||||
ldapScopeKey: 3,
|
||||
}, true, 2},
|
||||
{map[string]interface{}{
|
||||
ldapScopeKey: -1,
|
||||
}, true, 0},
|
||||
{map[string]interface{}{
|
||||
ldapScopeKey: 100,
|
||||
}, false, 2},
|
||||
{map[string]interface{}{
|
||||
ldapScopeKey: -100,
|
||||
}, false, 0},
|
||||
{map[string]interface{}{
|
||||
ldapScopeKey: dbValue,
|
||||
}, false, 2},
|
||||
}
|
||||
|
||||
for i, item := range testCfgs {
|
||||
validLdapScope(item.config, item.migrate)
|
||||
if item.config[ldapScopeKey].(int) != item.ldapScopeResult {
|
||||
t.Fatalf("Failed to update ldapScope expected %v, actual %v at index %v", item.ldapScopeResult, item.config[ldapScopeKey], i)
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
func Test_AddMissingKey(t *testing.T) {
|
||||
|
||||
cfg := map[string]interface{}{
|
||||
common.LDAPURL: "sampleurl",
|
||||
common.EmailPort: 555,
|
||||
common.LDAPVerifyCert: true,
|
||||
}
|
||||
|
||||
type args struct {
|
||||
cfg map[string]interface{}
|
||||
}
|
||||
tests := []struct {
|
||||
name string
|
||||
args args
|
||||
}{
|
||||
{"Add default value", args{cfg}},
|
||||
}
|
||||
for _, tt := range tests {
|
||||
t.Run(tt.name, func(t *testing.T) {
|
||||
AddMissedKey(tt.args.cfg)
|
||||
})
|
||||
}
|
||||
|
||||
if _, ok := cfg[common.LDAPBaseDN]; !ok {
|
||||
t.Errorf("Can not found default value for %v", common.LDAPBaseDN)
|
||||
}
|
||||
|
||||
}
|
@ -1,112 +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 config provide methods to get the configurations reqruied by code in src/common
|
||||
package config
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"time"
|
||||
|
||||
"github.com/astaxie/beego/cache"
|
||||
"github.com/goharbor/harbor/src/adminserver/client"
|
||||
"github.com/goharbor/harbor/src/common"
|
||||
)
|
||||
|
||||
// Manager manages configurations
|
||||
type Manager struct {
|
||||
client client.Client
|
||||
Cache bool
|
||||
cache cache.Cache
|
||||
key string
|
||||
}
|
||||
|
||||
// NewManager returns an instance of Manager
|
||||
func NewManager(client client.Client, enableCache bool) *Manager {
|
||||
m := &Manager{
|
||||
client: client,
|
||||
}
|
||||
|
||||
if enableCache {
|
||||
m.Cache = true
|
||||
m.cache = cache.NewMemoryCache()
|
||||
m.key = "cfg"
|
||||
}
|
||||
|
||||
return m
|
||||
}
|
||||
|
||||
// Load configurations, if cache is enabled, cache the configurations
|
||||
func (m *Manager) Load() (map[string]interface{}, error) {
|
||||
c, err := m.client.GetCfgs()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
if m.Cache {
|
||||
expi, err := getCfgExpiration(c)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
// copy the configuration map so that later modification to the
|
||||
// map does not effect the cached value
|
||||
cachedCfgs := map[string]interface{}{}
|
||||
for k, v := range c {
|
||||
cachedCfgs[k] = v
|
||||
}
|
||||
|
||||
if err = m.cache.Put(m.key, cachedCfgs,
|
||||
time.Duration(expi)*time.Second); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
}
|
||||
|
||||
return c, nil
|
||||
}
|
||||
|
||||
// Reset configurations
|
||||
func (m *Manager) Reset() error {
|
||||
return m.client.ResetCfgs()
|
||||
}
|
||||
|
||||
func getCfgExpiration(m map[string]interface{}) (int, error) {
|
||||
if m == nil {
|
||||
return 0, fmt.Errorf("can not get cfg expiration as configurations are null")
|
||||
}
|
||||
|
||||
expi, ok := m[common.CfgExpiration]
|
||||
if !ok {
|
||||
return 0, fmt.Errorf("cfg expiration is not set")
|
||||
}
|
||||
|
||||
return int(expi.(float64)), nil
|
||||
}
|
||||
|
||||
// Get : if cache is enabled, read configurations from cache,
|
||||
// if cache is null or cache is disabled it loads configurations directly
|
||||
func (m *Manager) Get() (map[string]interface{}, error) {
|
||||
if m.Cache {
|
||||
c := m.cache.Get(m.key)
|
||||
if c != nil {
|
||||
return c.(map[string]interface{}), nil
|
||||
}
|
||||
}
|
||||
return m.Load()
|
||||
}
|
||||
|
||||
// Upload configurations
|
||||
func (m *Manager) Upload(cfgs map[string]interface{}) error {
|
||||
return m.client.UpdateCfgs(cfgs)
|
||||
}
|
@ -1,17 +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 config
|
||||
|
||||
// the functions in common/config/config.go have been tested
|
||||
// by cases in UI and Jobservice
|
@ -125,8 +125,10 @@ func (c *CfgManager) GetAll() map[string]interface{} {
|
||||
metaDataList := metadata.Instance().GetAll()
|
||||
for _, item := range metaDataList {
|
||||
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)
|
||||
if err != nil {
|
||||
if err != metadata.ErrValueNotSet {
|
||||
log.Errorf("Failed to get value of key %v, error %v", item.Name, err)
|
||||
}
|
||||
continue
|
||||
}
|
||||
resultMap[item.Name] = cfgValue
|
||||
@ -145,8 +147,10 @@ func (c *CfgManager) GetUserCfgs() map[string]interface{} {
|
||||
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)
|
||||
if err != nil {
|
||||
if err != metadata.ErrValueNotSet {
|
||||
log.Errorf("Failed to get value of key %v, error %v", item.Name, err)
|
||||
}
|
||||
continue
|
||||
}
|
||||
resultMap[item.Name] = cfgValue
|
||||
|
@ -80,7 +80,6 @@ const (
|
||||
MaxJobWorkers = "max_job_workers"
|
||||
TokenExpiration = "token_expiration"
|
||||
CfgExpiration = "cfg_expiration"
|
||||
JobLogDir = "job_log_dir"
|
||||
AdminInitialPassword = "admin_initial_password"
|
||||
AdmiralEndpoint = "admiral_url"
|
||||
WithNotary = "with_notary"
|
||||
@ -98,7 +97,6 @@ const (
|
||||
UAAVerifyCert = "uaa_verify_cert"
|
||||
DefaultClairEndpoint = "http://clair:6060"
|
||||
CfgDriverDB = "db"
|
||||
CfgDriverJSON = "json"
|
||||
NewHarborAdminName = "admin@harbor.local"
|
||||
RegistryStorageProviderName = "registry_storage_provider_name"
|
||||
UserMember = "u"
|
||||
@ -106,7 +104,6 @@ const (
|
||||
ReadOnly = "read_only"
|
||||
ClairURL = "clair_url"
|
||||
NotaryURL = "notary_url"
|
||||
DefaultAdminserverEndpoint = "http://adminserver:8080"
|
||||
DefaultCoreEndpoint = "http://core:8080"
|
||||
DefaultNotaryEndpoint = "http://notary-server:4443"
|
||||
LdapGroupType = 1
|
||||
@ -123,48 +120,3 @@ const (
|
||||
RobotPrefix = "robot$"
|
||||
CoreConfigPath = "/api/internal/configurations"
|
||||
)
|
||||
|
||||
// TODO remove with adminserver
|
||||
// Shared variable, not allowed to modify
|
||||
var (
|
||||
|
||||
// value is default value
|
||||
HarborStringKeysMap = map[string]string{
|
||||
AUTHMode: "db_auth",
|
||||
LDAPURL: "",
|
||||
LDAPSearchDN: "",
|
||||
LDAPSearchPwd: "",
|
||||
LDAPBaseDN: "",
|
||||
LDAPUID: "",
|
||||
LDAPFilter: "",
|
||||
LDAPGroupAttributeName: "",
|
||||
LDAPGroupBaseDN: "",
|
||||
LdapGroupAdminDn: "",
|
||||
LDAPGroupSearchFilter: "",
|
||||
EmailHost: "smtp.mydomain.com",
|
||||
EmailUsername: "sample_admin@mydomain.com",
|
||||
EmailPassword: "abc",
|
||||
EmailFrom: "admin <sample_admin@mydomain.com>",
|
||||
EmailIdentity: "",
|
||||
ProjectCreationRestriction: ProCrtRestrEveryone,
|
||||
UAAClientID: "",
|
||||
UAAEndpoint: "",
|
||||
}
|
||||
|
||||
HarborNumKeysMap = map[string]int{
|
||||
EmailPort: 25,
|
||||
LDAPScope: 2,
|
||||
LDAPTimeout: 5,
|
||||
LDAPGroupSearchScope: 2,
|
||||
TokenExpiration: 30,
|
||||
}
|
||||
|
||||
HarborBoolKeysMap = map[string]bool{
|
||||
EmailSSL: false,
|
||||
EmailInsecure: false,
|
||||
SelfRegistration: true,
|
||||
LDAPVerifyCert: true,
|
||||
UAAVerifyCert: true,
|
||||
ReadOnly: false,
|
||||
}
|
||||
)
|
||||
|
@ -1,25 +1,15 @@
|
||||
package token
|
||||
|
||||
import (
|
||||
"github.com/goharbor/harbor/src/common/rbac"
|
||||
"github.com/goharbor/harbor/src/common/utils/test"
|
||||
"github.com/goharbor/harbor/src/core/config"
|
||||
"github.com/stretchr/testify/assert"
|
||||
"os"
|
||||
"testing"
|
||||
|
||||
"github.com/goharbor/harbor/src/common/rbac"
|
||||
"github.com/goharbor/harbor/src/core/config"
|
||||
"github.com/stretchr/testify/assert"
|
||||
)
|
||||
|
||||
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 {
|
||||
panic(err)
|
||||
}
|
||||
|
@ -15,14 +15,10 @@
|
||||
package test
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
"net/http"
|
||||
"net/http/httptest"
|
||||
|
||||
"github.com/goharbor/harbor/src/common"
|
||||
)
|
||||
|
||||
var adminServerDefaultConfig = map[string]interface{}{
|
||||
var defaultConfig = map[string]interface{}{
|
||||
common.ExtEndpoint: "https://host01.com",
|
||||
common.AUTHMode: common.DBAuth,
|
||||
common.DatabaseType: "postgresql",
|
||||
@ -77,54 +73,7 @@ var adminServerDefaultConfig = map[string]interface{}{
|
||||
common.NotaryURL: "http://notary-server:4443",
|
||||
}
|
||||
|
||||
// NewAdminserver returns a mock admin server
|
||||
func NewAdminserver(config map[string]interface{}) (*httptest.Server, error) {
|
||||
m := []*RequestHandlerMapping{}
|
||||
if config == nil {
|
||||
config = adminServerDefaultConfig
|
||||
} else {
|
||||
for k, v := range adminServerDefaultConfig {
|
||||
if _, ok := config[k]; !ok {
|
||||
config[k] = v
|
||||
}
|
||||
}
|
||||
}
|
||||
b, err := json.Marshal(config)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
resp := &Response{
|
||||
StatusCode: http.StatusOK,
|
||||
Body: b,
|
||||
}
|
||||
|
||||
m = append(m, &RequestHandlerMapping{
|
||||
Method: "GET",
|
||||
Pattern: "/api/configs",
|
||||
Handler: Handler(resp),
|
||||
})
|
||||
|
||||
m = append(m, &RequestHandlerMapping{
|
||||
Method: "PUT",
|
||||
Pattern: "/api/configurations",
|
||||
Handler: Handler(&Response{
|
||||
StatusCode: http.StatusOK,
|
||||
}),
|
||||
})
|
||||
|
||||
m = append(m, &RequestHandlerMapping{
|
||||
Method: "POST",
|
||||
Pattern: "/api/configurations/reset",
|
||||
Handler: Handler(&Response{
|
||||
StatusCode: http.StatusOK,
|
||||
}),
|
||||
})
|
||||
|
||||
return NewServer(m...), nil
|
||||
}
|
||||
|
||||
// GetDefaultConfigMap returns the defailt config map for easier modification.
|
||||
func GetDefaultConfigMap() map[string]interface{} {
|
||||
return adminServerDefaultConfig
|
||||
return defaultConfig
|
||||
}
|
@ -382,7 +382,7 @@ The following configuration options are supported:
|
||||
| worker_pool.redis_pool.namespace | The namespace used in redis| JOB_SERVICE_POOL_REDIS_NAMESPACE |
|
||||
| loggers | Loggers for job service itself. Refer to [Configure loggers](#configure-loggers)| |
|
||||
| job_loggers | Loggers for the running jobs. Refer to [Configure loggers](#configure-loggers) | |
|
||||
| admin_server | The harbor admin server endpoint which used to retrieve Harbor configures| ADMINSERVER_URL |
|
||||
| core_server | The harbor core server endpoint which used to retrieve Harbor configures| CORE_URL |
|
||||
|
||||
### Sample
|
||||
|
||||
@ -428,9 +428,6 @@ job_loggers:
|
||||
loggers:
|
||||
- name: "STD_OUTPUT" # Same with above
|
||||
level: "DEBUG"
|
||||
|
||||
#Admin server endpoint
|
||||
admin_server: "http://adminserver:9010/"
|
||||
```
|
||||
|
||||
## API
|
||||
|
@ -65,8 +65,6 @@ type Configuration struct {
|
||||
// Server listening port
|
||||
Port uint `yaml:"port"`
|
||||
|
||||
AdminServer string `yaml:"admin_server"`
|
||||
|
||||
// Additional config when using https
|
||||
HTTPSConfig *HTTPSConfig `yaml:"https_config,omitempty"`
|
||||
|
||||
@ -171,11 +169,6 @@ func GetUIAuthSecret() string {
|
||||
return utils.ReadEnv(uiAuthSecret)
|
||||
}
|
||||
|
||||
// GetAdminServerEndpoint return the admin server endpoint
|
||||
func GetAdminServerEndpoint() string {
|
||||
return DefaultConfig.AdminServer
|
||||
}
|
||||
|
||||
// Load env variables
|
||||
func (c *Configuration) loadEnvs() {
|
||||
prot := utils.ReadEnv(jobServiceProtocol)
|
||||
@ -251,11 +244,6 @@ func (c *Configuration) loadEnvs() {
|
||||
}
|
||||
}
|
||||
|
||||
// admin server
|
||||
if coreServer := utils.ReadEnv(jobServiceCoreServerEndpoint); !utils.IsEmptyStr(coreServer) {
|
||||
c.AdminServer = coreServer
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
// Check if the configurations are valid settings.
|
||||
@ -325,9 +313,5 @@ func (c *Configuration) validate() error {
|
||||
return errors.New("missing logger config of job")
|
||||
}
|
||||
|
||||
if _, err := url.Parse(c.AdminServer); err != nil {
|
||||
return fmt.Errorf("invalid admin server endpoint: %s", err)
|
||||
}
|
||||
|
||||
return nil // valid
|
||||
}
|
||||
|
@ -69,11 +69,6 @@ func TestDefaultConfig(t *testing.T) {
|
||||
if err := DefaultConfig.Load("../config_test.yml", true); err != nil {
|
||||
t.Fatalf("Load config from yaml file, expect nil error but got error '%s'\n", err)
|
||||
}
|
||||
|
||||
if endpoint := GetAdminServerEndpoint(); endpoint != "http://127.0.0.1:8888" {
|
||||
t.Errorf("expect default admin server endpoint 'http://127.0.0.1:8888' but got '%s'\n", endpoint)
|
||||
}
|
||||
|
||||
redisURL := DefaultConfig.PoolConfig.RedisPoolCfg.RedisURL
|
||||
if redisURL != "redis://localhost:6379" {
|
||||
t.Errorf("expect redisURL '%s' but got '%s'\n", "redis://localhost:6379", redisURL)
|
||||
|
@ -46,4 +46,3 @@ if response.status_code == 200 :
|
||||
else:
|
||||
print("Failed with http return code:"+ str(response.status_code))
|
||||
sys.exit(1)
|
||||
|
||||
|
@ -14,8 +14,4 @@ else
|
||||
fi
|
||||
echo "server ip is "$IP
|
||||
|
||||
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/CORE_SECRET=.*/CORE_SECRET=$CORE_SECRET/" make/common/config/adminserver/env
|
||||
|
||||
chmod 777 /data/
|
||||
|
@ -32,7 +32,6 @@ sudo mkdir -p /harbor && sudo mv ./VERSION /harbor/UIVERSION
|
||||
sudo ./tests/testprepare.sh
|
||||
|
||||
cd tests && sudo ./ldapprepare.sh && sudo ./admiral.sh && cd ..
|
||||
sudo make compile_adminserver
|
||||
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/__version__/dev/g' ./make/docker-compose.test.yml
|
||||
|
Loading…
Reference in New Issue
Block a user