mirror of https://github.com/goharbor/harbor.git
				
				
				
			Remove everything of adminserver
Signed-off-by: stonezdj <stonezdj@gmail.com>
This commit is contained in:
		
							parent
							
								
									a1063edae6
								
							
						
					
					
						commit
						0cba36d79f
					
				|  | @ -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 | ||||
|  | @ -22,9 +22,6 @@ DOCKERRMIMAGE=$(DOCKERCMD) rmi | |||
| DOCKERIMASES=$(DOCKERCMD) images | ||||
| 
 | ||||
| # binary
 | ||||
| ADMINSERVERSOURCECODE=$(SRCPATH)/adminserver | ||||
| ADMINSERVERBINARYPATH=$(MAKEDEVPATH)/adminserver | ||||
| ADMINSERVERBINARYNAME=harbor_adminserver | ||||
| 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" | ||||
							
								
								
									
										12
									
								
								make/prepare
								
								
								
								
							
							
						
						
									
										12
									
								
								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") | ||||
|  | @ -280,7 +276,6 @@ 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" | ||||
| 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 { | ||||
| 		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 { | ||||
| 			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