diff --git a/.gitignore b/.gitignore index 862a91fed..87860b72d 100644 --- a/.gitignore +++ b/.gitignore @@ -10,7 +10,10 @@ src/common/dao/dao.test *.pyc jobservice/test -src/ui/static/dist/ +src/ui/static/*.html +src/ui/static/*.bundle.js +src/ui/static/*.bundle.js.map +src/ui/static/harbor-log.*.png src/ui_ng/coverage/ src/ui_ng/dist/ diff --git a/.travis.yml b/.travis.yml index d552fba28..b0dd4c556 100644 --- a/.travis.yml +++ b/.travis.yml @@ -76,6 +76,8 @@ before_script: script: - sudo mkdir -p /harbor_storage/ca_download - sudo mv ./tests/ca.crt /harbor_storage/ca_download + - sudo mkdir -p /harbor + - sudo mv ./VERSION /harbor/VERSION - sudo service mysql stop - sudo ./tests/testprepare.sh - docker-compose -f ./make/docker-compose.test.yml up -d @@ -98,6 +100,7 @@ script: - sudo make install GOBUILDIMAGE=golang:1.7.3 COMPILETAG=compile_golangimage CLARITYIMAGE=danieljt/harbor-clarity-base:0.8.4 NOTARYFLAG=true - docker ps + - ./tests/notarytest.sh - go run tests/startuptest.go https://localhost/ - go run tests/userlogintest.go -name ${HARBOR_ADMIN} -passwd ${HARBOR_ADMIN_PASSWD} diff --git a/Makefile b/Makefile index 2246ebe0b..8e808d4fd 100644 --- a/Makefile +++ b/Makefile @@ -1,445 +1,443 @@ -# Makefile for Harbor project -# -# Targets: -# -# all: prepare env, compile binarys, build images and install images -# prepare: prepare env -# compile: compile adminserver, ui and jobservice code -# -# compile_golangimage: -# compile from golang image -# for example: make compile_golangimage -e GOBUILDIMAGE= \ -# golang:1.7.3 -# compile_adminserver, compile_ui, compile_jobservice: compile specific binary -# -# build: build Harbor docker images (defuault: build_photon) -# for example: make build -e BASEIMAGE=photon -# build_photon: build Harbor docker images from photon baseimage -# -# install: include compile binarys, build images, prepare specific \ -# version composefile and startup Harbor instance -# -# start: startup Harbor instance -# -# down: shutdown Harbor instance -# -# package_online: -# prepare online install package -# for example: make package_online -e DEVFLAG=false\ -# REGISTRYSERVER=reg-bj.eng.vmware.com \ -# REGISTRYPROJECTNAME=harborrelease -# -# package_offline: -# prepare offline install package -# -# pushimage: push Harbor images to specific registry server -# for example: make pushimage -e DEVFLAG=false REGISTRYUSER=admin \ -# REGISTRYPASSWORD=***** \ -# REGISTRYSERVER=reg-bj.eng.vmware.com/ \ -# REGISTRYPROJECTNAME=harborrelease -# note**: need add "/" on end of REGISTRYSERVER. If not setting \ -# this value will push images directly to dockerhub. -# make pushimage -e DEVFLAG=false REGISTRYUSER=vmware \ -# REGISTRYPASSWORD=***** \ -# REGISTRYPROJECTNAME=vmware -# -# 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 -# cleanimage: remove Harbor images -# cleandockercomposefile: -# remove specific version docker-compose -# cleanversiontag: -# cleanpackageremove specific version tag -# cleanpackage: remove online/offline install package -# -# other example: -# clean specific version binarys and images: -# make clean -e VERSIONTAG=[TAG] -# note**: If commit new code to github, the git commit TAG will \ -# change. Better use this commond clean previous images and \ -# files with specific TAG. -# By default DEVFLAG=true, if you want to release new version of Harbor, \ -# should setting the flag to false. -# make XXXX -e DEVFLAG=false - -SHELL := /bin/bash -BUILDPATH=$(CURDIR) -MAKEPATH=$(BUILDPATH)/make -MAKEDEVPATH=$(MAKEPATH)/dev -SRCPATH=./src -TOOLSPATH=$(BUILDPATH)/tools -UIPATH=$(BUILDPATH)/src/ui -UINGPATH=$(BUILDPATH)/src/ui_ng -GOBASEPATH=/go/src/github.com/vmware -CHECKENVCMD=checkenv.sh -BASEIMAGE=photon -COMPILETAG=compile_normal -REGISTRYSERVER= -REGISTRYPROJECTNAME=vmware -DEVFLAG=true -NOTARYFLAG=false -REGISTRYVERSION=photon-2.6.0 -NGINXVERSION=1.11.5 -PHOTONVERSION=1.0 -NOTARYVERSION=server-0.5.0 -NOTARYSIGNERVERSION=signer-0.5.0 -MARIADBVERSION=10.1.10 -HTTPPROXY= - -#clarity parameters -CLARITYIMAGE=danieljt/harbor-clarity-base[:tag] -CLARITYSEEDPATH=/clarity-seed -CLARITYBUILDSCRIPT=/entrypoint.sh - -# docker parameters -DOCKERCMD=$(shell which docker) -DOCKERBUILD=$(DOCKERCMD) build -DOCKERRMIMAGE=$(DOCKERCMD) rmi -DOCKERPULL=$(DOCKERCMD) pull -DOCKERIMASES=$(DOCKERCMD) images -DOCKERSAVE=$(DOCKERCMD) save -DOCKERCOMPOSECMD=$(shell which docker-compose) -DOCKERTAG=$(DOCKERCMD) tag - -# go parameters -GOCMD=$(shell which go) -GOBUILD=$(GOCMD) build -GOCLEAN=$(GOCMD) clean -GOINSTALL=$(GOCMD) install -GOTEST=$(GOCMD) test -GODEP=$(GOTEST) -i -GOFMT=gofmt -w -GOBUILDIMAGE=reg.mydomain.com/library/harborgo[:tag] -GOBUILDPATH=$(GOBASEPATH)/harbor -GOIMAGEBUILDCMD=/usr/local/go/bin/go -GOIMAGEBUILD=$(GOIMAGEBUILDCMD) build -GOBUILDPATH_ADMINSERVER=$(GOBUILDPATH)/src/adminserver -GOBUILDPATH_UI=$(GOBUILDPATH)/src/ui -GOBUILDPATH_JOBSERVICE=$(GOBUILDPATH)/src/jobservice -GOBUILDMAKEPATH=$(GOBUILDPATH)/make -GOBUILDMAKEPATH_ADMINSERVER=$(GOBUILDMAKEPATH)/dev/adminserver -GOBUILDMAKEPATH_UI=$(GOBUILDMAKEPATH)/dev/ui -GOBUILDMAKEPATH_JOBSERVICE=$(GOBUILDMAKEPATH)/dev/jobservice -GOLANGDOCKERFILENAME=Dockerfile.golang - -# binary -ADMINSERVERSOURCECODE=$(SRCPATH)/adminserver -ADMINSERVERBINARYPATH=$(MAKEDEVPATH)/adminserver -ADMINSERVERBINARYNAME=harbor_adminserver -UISOURCECODE=$(SRCPATH)/ui -UIBINARYPATH=$(MAKEDEVPATH)/ui -UIBINARYNAME=harbor_ui -JOBSERVICESOURCECODE=$(SRCPATH)/jobservice -JOBSERVICEBINARYPATH=$(MAKEDEVPATH)/jobservice -JOBSERVICEBINARYNAME=harbor_jobservice - -# prepare parameters -PREPAREPATH=$(TOOLSPATH) -PREPARECMD=prepare - -# configfile -CONFIGPATH=$(MAKEPATH) -CONFIGFILE=harbor.cfg - -# makefile -MAKEFILEPATH_PHOTON=$(MAKEPATH)/photon - -# common dockerfile -DOCKERFILEPATH_COMMON=$(MAKEPATH)/common -DOCKERFILEPATH_DB=$(DOCKERFILEPATH_COMMON)/db -DOCKERFILENAME_DB=Dockerfile - -# docker image name -DOCKERIMAGENAME_ADMINSERVER=vmware/harbor-adminserver -DOCKERIMAGENAME_UI=vmware/harbor-ui -DOCKERIMAGENAME_JOBSERVICE=vmware/harbor-jobservice -DOCKERIMAGENAME_LOG=vmware/harbor-log -DOCKERIMAGENAME_DB=vmware/harbor-db - -# docker-compose files -DOCKERCOMPOSEFILEPATH=$(MAKEPATH) -DOCKERCOMPOSETPLFILENAME=docker-compose.tpl -DOCKERCOMPOSEFILENAME=docker-compose.yml -DOCKERCOMPOSENOTARYFILENAME=docker-compose.notary.yml - -# version prepare -VERSIONFILEPATH=$(SRCPATH)/ui/views/sections -VERSIONFILENAME=header-content.htm -GITCMD=$(shell which git) -GITTAG=$(GITCMD) describe --tags -ifeq ($(DEVFLAG), true) - VERSIONTAG=dev -else - VERSIONTAG=$(shell $(GITTAG)) -endif - -SEDCMD=$(shell which sed) - -# package -TARCMD=$(shell which tar) -ZIPCMD=$(shell which gzip) -DOCKERIMGFILE=harbor -HARBORPKG=harbor - -# pushimage -PUSHSCRIPTPATH=$(MAKEPATH) -PUSHSCRIPTNAME=pushimage.sh -REGISTRYUSER=user -REGISTRYPASSWORD=default - -version: - @if [ "$(DEVFLAG)" = "false" ] ; then \ - $(SEDCMD) -i 's/version=\"{{.Version}}\"/version=\"$(VERSIONTAG)\"/' -i $(VERSIONFILEPATH)/$(VERSIONFILENAME) ; \ - fi - -check_environment: - @$(MAKEPATH)/$(CHECKENVCMD) - -compile_adminserver: - @echo "compiling binary for adminserver..." - @$(GOBUILD) -o $(ADMINSERVERBINARYPATH)/$(ADMINSERVERBINARYNAME) $(ADMINSERVERSOURCECODE) - @echo "Done." - -compile_ui: - @echo "compiling binary for ui..." - @$(GOBUILD) -o $(UIBINARYPATH)/$(UIBINARYNAME) $(UISOURCECODE) - @echo "Done." - -compile_jobservice: - @echo "compiling binary for jobservice..." - @$(GOBUILD) -o $(JOBSERVICEBINARYPATH)/$(JOBSERVICEBINARYNAME) $(JOBSERVICESOURCECODE) - @echo "Done." - -compile_clarity: - @echo "compiling binary for clarity ui..." - @if [ "$(HTTPPROXY)" != "" ] ; then \ - $(DOCKERCMD) run --rm -v $(UIPATH)/static:$(CLARITYSEEDPATH)/dist -v $(UINGPATH)/src:$(CLARITYSEEDPATH)/src $(CLARITYIMAGE) $(SHELL) $(CLARITYBUILDSCRIPT) -p $(HTTPPROXY); \ - else \ - $(DOCKERCMD) run --rm -v $(UIPATH)/static:$(CLARITYSEEDPATH)/dist -v $(UINGPATH)/src:$(CLARITYSEEDPATH)/src $(CLARITYIMAGE) $(SHELL) $(CLARITYBUILDSCRIPT); \ - fi - @echo "Done." - -compile_normal: compile_clarity compile_adminserver compile_ui compile_jobservice - -compile_golangimage: compile_clarity - @echo "compiling binary for adminserver (golang image)..." - @echo $(GOBASEPATH) - @echo $(GOBUILDPATH) - @$(DOCKERCMD) run --rm -v $(BUILDPATH):$(GOBUILDPATH) -w $(GOBUILDPATH_ADMINSERVER) $(GOBUILDIMAGE) $(GOIMAGEBUILD) -v -o $(GOBUILDMAKEPATH_ADMINSERVER)/$(ADMINSERVERBINARYNAME) - @echo "Done." - - @echo "compiling binary for ui (golang image)..." - @echo $(GOBASEPATH) - @echo $(GOBUILDPATH) - @$(DOCKERCMD) run --rm -v $(BUILDPATH):$(GOBUILDPATH) -w $(GOBUILDPATH_UI) $(GOBUILDIMAGE) $(GOIMAGEBUILD) -v -o $(GOBUILDMAKEPATH_UI)/$(UIBINARYNAME) - @echo "Done." - - @echo "compiling binary for jobservice (golang image)..." - @$(DOCKERCMD) run --rm -v $(BUILDPATH):$(GOBUILDPATH) -w $(GOBUILDPATH_JOBSERVICE) $(GOBUILDIMAGE) $(GOIMAGEBUILD) -v -o $(GOBUILDMAKEPATH_JOBSERVICE)/$(JOBSERVICEBINARYNAME) - @echo "Done." - -compile:check_environment $(COMPILETAG) - -prepare: - @echo "preparing..." - @if [ "$(NOTARYFLAG)" = "true" ] ; then \ - $(MAKEPATH)/$(PREPARECMD) --conf $(CONFIGPATH)/$(CONFIGFILE) --with-notary; \ - else \ - $(MAKEPATH)/$(PREPARECMD) --conf $(CONFIGPATH)/$(CONFIGFILE) ; \ - fi - -build_common: version - @echo "buildging db container for photon..." - @cd $(DOCKERFILEPATH_DB) && $(DOCKERBUILD) -f $(DOCKERFILENAME_DB) -t $(DOCKERIMAGENAME_DB):$(VERSIONTAG) . - @echo "Done." - -build_photon: build_common - make -f $(MAKEFILEPATH_PHOTON)/Makefile build -e DEVFLAG=$(DEVFLAG) - -build: build_$(BASEIMAGE) - -modify_composefile: - @echo "preparing docker-compose file..." - @cp $(DOCKERCOMPOSEFILEPATH)/$(DOCKERCOMPOSETPLFILENAME) $(DOCKERCOMPOSEFILEPATH)/$(DOCKERCOMPOSEFILENAME) - @$(SEDCMD) -i 's/__version__/$(VERSIONTAG)/g' $(DOCKERCOMPOSEFILEPATH)/$(DOCKERCOMPOSEFILENAME) - -install: compile build prepare modify_composefile start - -package_online: modify_composefile - @echo "packing online package ..." - @cp -r make $(HARBORPKG) - @if [ -n "$(REGISTRYSERVER)" ] ; then \ - $(SEDCMD) -i 's/image\: vmware/image\: $(REGISTRYSERVER)\/$(REGISTRYPROJECTNAME)/' \ - $(HARBORPKG)/docker-compose.yml ; \ - fi - @cp LICENSE $(HARBORPKG)/LICENSE - @cp NOTICE $(HARBORPKG)/NOTICE - - @if [ "$(NOTARYFLAG)" = "true" ] ; then \ - $(TARCMD) -zcvf harbor-online-installer-$(VERSIONTAG).tgz \ - $(HARBORPKG)/common/templates $(HARBORPKG)/prepare \ - $(HARBORPKG)/LICENSE $(HARBORPKG)/NOTICE \ - $(HARBORPKG)/install.sh $(HARBORPKG)/$(DOCKERCOMPOSEFILENAME) \ - $(HARBORPKG)/harbor.cfg $(HARBORPKG)/$(DOCKERCOMPOSENOTARYFILENAME); \ - else \ - $(TARCMD) -zcvf harbor-online-installer-$(VERSIONTAG).tgz \ - $(HARBORPKG)/common/templates $(HARBORPKG)/prepare \ - $(HARBORPKG)/LICENSE $(HARBORPKG)/NOTICE \ - $(HARBORPKG)/install.sh $(HARBORPKG)/$(DOCKERCOMPOSEFILENAME) \ - $(HARBORPKG)/harbor.cfg ; \ - fi - - @rm -rf $(HARBORPKG) - @echo "Done." - -package_offline: compile build modify_composefile - @echo "packing offline package ..." - @cp -r make $(HARBORPKG) - - @cp LICENSE $(HARBORPKG)/LICENSE - @cp NOTICE $(HARBORPKG)/NOTICE - - @echo "pulling nginx and registry..." - @$(DOCKERPULL) registry:$(REGISTRYVERSION) - @$(DOCKERPULL) nginx:$(NGINXVERSION) - @if [ "$(NOTARYFLAG)" = "true" ] ; then \ - echo "pulling notary and mariadb..."; \ - $(DOCKERPULL) vmware/notary-photon:$(NOTARYVERSION); \ - $(DOCKERPULL) vmware/notary-photon:$(NOTARYSIGNERVERSION); \ - $(DOCKERPULL) mariadb:$(MARIADBVERSION); \ - fi - - @echo "saving harbor docker image" - @if [ "$(NOTARYFLAG)" = "true" ] ; then \ - $(DOCKERSAVE) -o $(HARBORPKG)/$(DOCKERIMGFILE).$(VERSIONTAG).tgz \ - $(DOCKERIMAGENAME_ADMINSERVER):$(VERSIONTAG) \ - $(DOCKERIMAGENAME_UI):$(VERSIONTAG) \ - $(DOCKERIMAGENAME_LOG):$(VERSIONTAG) \ - $(DOCKERIMAGENAME_DB):$(VERSIONTAG) \ - $(DOCKERIMAGENAME_JOBSERVICE):$(VERSIONTAG) \ - nginx:$(NGINXVERSION) vmware/registry:$(REGISTRYVERSION) photon:$(PHOTONVERSION) \ - vmware/notary-photon:$(NOTARYVERSION) vmware/notary-photon:$(NOTARYSIGNERVERSION) mariadb:$(MARIADBVERSION); \ - else \ - $(DOCKERSAVE) -o $(HARBORPKG)/$(DOCKERIMGFILE).$(VERSIONTAG).tgz \ - $(DOCKERIMAGENAME_ADMINSERVER):$(VERSIONTAG) \ - $(DOCKERIMAGENAME_UI):$(VERSIONTAG) \ - $(DOCKERIMAGENAME_LOG):$(VERSIONTAG) \ - $(DOCKERIMAGENAME_DB):$(VERSIONTAG) \ - $(DOCKERIMAGENAME_JOBSERVICE):$(VERSIONTAG) \ - nginx:$(NGINXVERSION) registry:$(REGISTRYVERSION) photon:$(PHOTONVERSION) ; \ - fi - - @if [ "$(NOTARYFLAG)" = "true" ] ; then \ - $(TARCMD) -zcvf harbor-offline-installer-$(VERSIONTAG).tgz \ - $(HARBORPKG)/common/templates $(HARBORPKG)/$(DOCKERIMGFILE).$(VERSIONTAG).tgz \ - $(HARBORPKG)/prepare $(HARBORPKG)/NOTICE \ - $(HARBORPKG)/LICENSE $(HARBORPKG)/install.sh \ - $(HARBORPKG)/harbor.cfg $(HARBORPKG)/$(DOCKERCOMPOSEFILENAME) \ - $(HARBORPKG)/$(DOCKERCOMPOSENOTARYFILENAME) ; \ - else \ - $(TARCMD) -zcvf harbor-offline-installer-$(VERSIONTAG).tgz \ - $(HARBORPKG)/common/templates $(HARBORPKG)/$(DOCKERIMGFILE).$(VERSIONTAG).tgz \ - $(HARBORPKG)/prepare $(HARBORPKG)/NOTICE \ - $(HARBORPKG)/LICENSE $(HARBORPKG)/install.sh \ - $(HARBORPKG)/harbor.cfg $(HARBORPKG)/$(DOCKERCOMPOSEFILENAME) ; \ - fi - - @rm -rf $(HARBORPKG) - @echo "Done." - -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_UI):$(VERSIONTAG) $(REGISTRYSERVER)$(DOCKERIMAGENAME_UI):$(VERSIONTAG) - @$(PUSHSCRIPTPATH)/$(PUSHSCRIPTNAME) $(REGISTRYSERVER)$(DOCKERIMAGENAME_UI):$(VERSIONTAG) \ - $(REGISTRYUSER) $(REGISTRYPASSWORD) $(REGISTRYSERVER) - @$(DOCKERRMIMAGE) $(REGISTRYSERVER)$(DOCKERIMAGENAME_UI):$(VERSIONTAG) - - @$(DOCKERTAG) $(DOCKERIMAGENAME_JOBSERVICE):$(VERSIONTAG) $(REGISTRYSERVER)$(DOCKERIMAGENAME_JOBSERVICE):$(VERSIONTAG) - @$(PUSHSCRIPTPATH)/$(PUSHSCRIPTNAME) $(REGISTRYSERVER)$(DOCKERIMAGENAME_JOBSERVICE):$(VERSIONTAG) \ - $(REGISTRYUSER) $(REGISTRYPASSWORD) $(REGISTRYSERVER) - @$(DOCKERRMIMAGE) $(REGISTRYSERVER)$(DOCKERIMAGENAME_JOBSERVICE):$(VERSIONTAG) - - @$(DOCKERTAG) $(DOCKERIMAGENAME_LOG):$(VERSIONTAG) $(REGISTRYSERVER)$(DOCKERIMAGENAME_LOG):$(VERSIONTAG) - @$(PUSHSCRIPTPATH)/$(PUSHSCRIPTNAME) $(REGISTRYSERVER)$(DOCKERIMAGENAME_LOG):$(VERSIONTAG) \ - $(REGISTRYUSER) $(REGISTRYPASSWORD) $(REGISTRYSERVER) - @$(DOCKERRMIMAGE) $(REGISTRYSERVER)$(DOCKERIMAGENAME_LOG):$(VERSIONTAG) - - @$(DOCKERTAG) $(DOCKERIMAGENAME_DB):$(VERSIONTAG) $(REGISTRYSERVER)$(DOCKERIMAGENAME_DB):$(VERSIONTAG) - @$(PUSHSCRIPTPATH)/$(PUSHSCRIPTNAME) $(REGISTRYSERVER)$(DOCKERIMAGENAME_DB):$(VERSIONTAG) \ - $(REGISTRYUSER) $(REGISTRYPASSWORD) $(REGISTRYSERVER) - @$(DOCKERRMIMAGE) $(REGISTRYSERVER)$(DOCKERIMAGENAME_DB):$(VERSIONTAG) - -start: - @echo "loading harbor images..." - @if [ "$(NOTARYFLAG)" = "true" ] ; then \ - $(DOCKERCOMPOSECMD) -f $(DOCKERCOMPOSEFILEPATH)/$(DOCKERCOMPOSEFILENAME) -f $(DOCKERCOMPOSEFILEPATH)/$(DOCKERCOMPOSENOTARYFILENAME) up -d ; \ - else \ - $(DOCKERCOMPOSECMD) -f $(DOCKERCOMPOSEFILEPATH)/$(DOCKERCOMPOSEFILENAME) up -d ; \ - fi - @echo "Start complete. You can visit harbor now." - -down: - @echo "Please make sure to set -e NOTARYFLAG=true if you are using Notary in Harbor, otherwise the Notary containers cannot be stop automaticlly." - @while [ -z "$$CONTINUE" ]; do \ - read -r -p "Type anything but Y or y to exit. [Y/N]: " CONTINUE; \ - done ; \ - [ $$CONTINUE = "y" ] || [ $$CONTINUE = "Y" ] || (echo "Exiting."; exit 1;) - @echo "stoping harbor instance..." - @if [ "$(NOTARYFLAG)" = "true" ] ; then \ - $(DOCKERCOMPOSECMD) -f $(DOCKERCOMPOSEFILEPATH)/$(DOCKERCOMPOSEFILENAME) -f $(DOCKERCOMPOSEFILEPATH)/$(DOCKERCOMPOSENOTARYFILENAME) down ; \ - else \ - $(DOCKERCOMPOSECMD) -f $(DOCKERCOMPOSEFILEPATH)/$(DOCKERCOMPOSEFILENAME) down ; \ - fi - @echo "Done." - -cleanbinary: - @echo "cleaning binary..." - @if [ -f $(ADMINSERVERBINARYPATH)/$(ADMINSERVERBINARYNAME) ] ; then rm $(ADMINSERVERBINARYPATH)/$(ADMINSERVERBINARYNAME) ; fi - @if [ -f $(UIBINARYPATH)/$(UIBINARYNAME) ] ; then rm $(UIBINARYPATH)/$(UIBINARYNAME) ; fi - @if [ -f $(JOBSERVICEBINARYPATH)/$(JOBSERVICEBINARYNAME) ] ; then rm $(JOBSERVICEBINARYPATH)/$(JOBSERVICEBINARYNAME) ; fi - -cleanimage: - @echo "cleaning image for photon..." - - $(DOCKERRMIMAGE) -f $(DOCKERIMAGENAME_ADMINSERVER):$(VERSIONTAG) - - $(DOCKERRMIMAGE) -f $(DOCKERIMAGENAME_UI):$(VERSIONTAG) - - $(DOCKERRMIMAGE) -f $(DOCKERIMAGENAME_DB):$(VERSIONTAG) - - $(DOCKERRMIMAGE) -f $(DOCKERIMAGENAME_JOBSERVICE):$(VERSIONTAG) - - $(DOCKERRMIMAGE) -f $(DOCKERIMAGENAME_LOG):$(VERSIONTAG) -# - $(DOCKERRMIMAGE) -f registry:$(REGISTRYVERSION) -# - $(DOCKERRMIMAGE) -f nginx:1.11.5 - -cleandockercomposefile: - @echo "cleaning $(DOCKERCOMPOSEFILEPATH)/docker-compose.yml" - @if [ -f $(DOCKERCOMPOSEFILEPATH)/docker-compose.yml ] ; then rm $(DOCKERCOMPOSEFILEPATH)/docker-compose.yml ; fi - -cleanversiontag: - @echo "cleaning version TAG" - @$(SEDCMD) -i 's/version=\"$(VERSIONTAG)\"/version=\"{{.Version}}\"/' -i $(VERSIONFILEPATH)/$(VERSIONFILENAME) - -cleanpackage: - @echo "cleaning harbor install package" - @if [ -d $(BUILDPATH)/harbor ] ; then rm -rf $(BUILDPATH)/harbor ; fi - @if [ -f $(BUILDPATH)/harbor-online-installer-$(VERSIONTAG).tgz ] ; \ - then rm $(BUILDPATH)/harbor-online-installer-$(VERSIONTAG).tgz ; fi - @if [ -f $(BUILDPATH)/harbor-offline-installer-$(VERSIONTAG).tgz ] ; \ - then rm $(BUILDPATH)/harbor-offline-installer-$(VERSIONTAG).tgz ; fi - -.PHONY: cleanall -cleanall: cleanbinary cleanimage cleandockercomposefile cleanversiontag cleanpackage - -clean: - @echo " make cleanall: remove binary, Harbor images, specific version docker-compose" - @echo " file, specific version tag, online and offline install package" - @echo " make cleanbinary: remove ui and jobservice binary" - @echo " make cleanimage: remove Harbor images" - @echo " make cleandockercomposefile: remove specific version docker-compose" - @echo " make cleanversiontag: cleanpackageremove specific version tag" - @echo " make cleanpackage: remove online and offline install package" - -all: install +# Makefile for Harbor project +# +# Targets: +# +# all: prepare env, compile binarys, build images and install images +# prepare: prepare env +# compile: compile adminserver, ui and jobservice code +# +# compile_golangimage: +# compile from golang image +# for example: make compile_golangimage -e GOBUILDIMAGE= \ +# golang:1.7.3 +# compile_adminserver, compile_ui, compile_jobservice: compile specific binary +# +# build: build Harbor docker images (defuault: build_photon) +# for example: make build -e BASEIMAGE=photon +# build_photon: build Harbor docker images from photon baseimage +# +# install: include compile binarys, build images, prepare specific \ +# version composefile and startup Harbor instance +# +# start: startup Harbor instance +# +# down: shutdown Harbor instance +# +# package_online: +# prepare online install package +# for example: make package_online -e DEVFLAG=false\ +# REGISTRYSERVER=reg-bj.eng.vmware.com \ +# REGISTRYPROJECTNAME=harborrelease +# +# package_offline: +# prepare offline install package +# +# pushimage: push Harbor images to specific registry server +# for example: make pushimage -e DEVFLAG=false REGISTRYUSER=admin \ +# REGISTRYPASSWORD=***** \ +# REGISTRYSERVER=reg-bj.eng.vmware.com/ \ +# REGISTRYPROJECTNAME=harborrelease +# note**: need add "/" on end of REGISTRYSERVER. If not setting \ +# this value will push images directly to dockerhub. +# make pushimage -e DEVFLAG=false REGISTRYUSER=vmware \ +# REGISTRYPASSWORD=***** \ +# REGISTRYPROJECTNAME=vmware +# +# 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 +# cleanimage: remove Harbor images +# cleandockercomposefile: +# remove specific version docker-compose +# cleanversiontag: +# cleanpackageremove specific version tag +# cleanpackage: remove online/offline install package +# +# other example: +# clean specific version binarys and images: +# make clean -e VERSIONTAG=[TAG] +# note**: If commit new code to github, the git commit TAG will \ +# change. Better use this commond clean previous images and \ +# files with specific TAG. +# By default DEVFLAG=true, if you want to release new version of Harbor, \ +# should setting the flag to false. +# make XXXX -e DEVFLAG=false + +SHELL := /bin/bash +BUILDPATH=$(CURDIR) +MAKEPATH=$(BUILDPATH)/make +MAKEDEVPATH=$(MAKEPATH)/dev +SRCPATH=./src +TOOLSPATH=$(BUILDPATH)/tools +UIPATH=$(BUILDPATH)/src/ui +UINGPATH=$(BUILDPATH)/src/ui_ng +GOBASEPATH=/go/src/github.com/vmware +CHECKENVCMD=checkenv.sh +BASEIMAGE=photon +COMPILETAG=compile_normal +REGISTRYSERVER= +REGISTRYPROJECTNAME=vmware +DEVFLAG=true +NOTARYFLAG=false +REGISTRYVERSION=photon-2.6.0 +NGINXVERSION=1.11.5 +PHOTONVERSION=1.0 +NOTARYVERSION=server-0.5.0 +NOTARYSIGNERVERSION=signer-0.5.0 +MARIADBVERSION=mariadb-10.1.10 +HTTPPROXY= + +#clarity parameters +CLARITYIMAGE=danieljt/harbor-clarity-base[:tag] +CLARITYSEEDPATH=/clarity-seed +CLARITYBUILDSCRIPT=/entrypoint.sh + +# docker parameters +DOCKERCMD=$(shell which docker) +DOCKERBUILD=$(DOCKERCMD) build +DOCKERRMIMAGE=$(DOCKERCMD) rmi +DOCKERPULL=$(DOCKERCMD) pull +DOCKERIMASES=$(DOCKERCMD) images +DOCKERSAVE=$(DOCKERCMD) save +DOCKERCOMPOSECMD=$(shell which docker-compose) +DOCKERTAG=$(DOCKERCMD) tag + +# go parameters +GOCMD=$(shell which go) +GOBUILD=$(GOCMD) build +GOCLEAN=$(GOCMD) clean +GOINSTALL=$(GOCMD) install +GOTEST=$(GOCMD) test +GODEP=$(GOTEST) -i +GOFMT=gofmt -w +GOBUILDIMAGE=reg.mydomain.com/library/harborgo[:tag] +GOBUILDPATH=$(GOBASEPATH)/harbor +GOIMAGEBUILDCMD=/usr/local/go/bin/go +GOIMAGEBUILD=$(GOIMAGEBUILDCMD) build +GOBUILDPATH_ADMINSERVER=$(GOBUILDPATH)/src/adminserver +GOBUILDPATH_UI=$(GOBUILDPATH)/src/ui +GOBUILDPATH_JOBSERVICE=$(GOBUILDPATH)/src/jobservice +GOBUILDMAKEPATH=$(GOBUILDPATH)/make +GOBUILDMAKEPATH_ADMINSERVER=$(GOBUILDMAKEPATH)/dev/adminserver +GOBUILDMAKEPATH_UI=$(GOBUILDMAKEPATH)/dev/ui +GOBUILDMAKEPATH_JOBSERVICE=$(GOBUILDMAKEPATH)/dev/jobservice +GOLANGDOCKERFILENAME=Dockerfile.golang + +# binary +ADMINSERVERSOURCECODE=$(SRCPATH)/adminserver +ADMINSERVERBINARYPATH=$(MAKEDEVPATH)/adminserver +ADMINSERVERBINARYNAME=harbor_adminserver +UISOURCECODE=$(SRCPATH)/ui +UIBINARYPATH=$(MAKEDEVPATH)/ui +UIBINARYNAME=harbor_ui +JOBSERVICESOURCECODE=$(SRCPATH)/jobservice +JOBSERVICEBINARYPATH=$(MAKEDEVPATH)/jobservice +JOBSERVICEBINARYNAME=harbor_jobservice + +# prepare parameters +PREPAREPATH=$(TOOLSPATH) +PREPARECMD=prepare + +# configfile +CONFIGPATH=$(MAKEPATH) +CONFIGFILE=harbor.cfg + +# makefile +MAKEFILEPATH_PHOTON=$(MAKEPATH)/photon + +# common dockerfile +DOCKERFILEPATH_COMMON=$(MAKEPATH)/common +DOCKERFILEPATH_DB=$(DOCKERFILEPATH_COMMON)/db +DOCKERFILENAME_DB=Dockerfile + +# docker image name +DOCKERIMAGENAME_ADMINSERVER=vmware/harbor-adminserver +DOCKERIMAGENAME_UI=vmware/harbor-ui +DOCKERIMAGENAME_JOBSERVICE=vmware/harbor-jobservice +DOCKERIMAGENAME_LOG=vmware/harbor-log +DOCKERIMAGENAME_DB=vmware/harbor-db + +# docker-compose files +DOCKERCOMPOSEFILEPATH=$(MAKEPATH) +DOCKERCOMPOSETPLFILENAME=docker-compose.tpl +DOCKERCOMPOSEFILENAME=docker-compose.yml +DOCKERCOMPOSENOTARYFILENAME=docker-compose.notary.yml + +# version prepare +VERSIONFILEPATH=$(CURDIR) +VERSIONFILENAME=VERSION +GITCMD=$(shell which git) +GITTAG=$(GITCMD) describe --tags +ifeq ($(DEVFLAG), true) + VERSIONTAG=dev +else + VERSIONTAG=$(shell $(GITTAG)) +endif + +SEDCMD=$(shell which sed) + +# package +TARCMD=$(shell which tar) +ZIPCMD=$(shell which gzip) +DOCKERIMGFILE=harbor +HARBORPKG=harbor + +# pushimage +PUSHSCRIPTPATH=$(MAKEPATH) +PUSHSCRIPTNAME=pushimage.sh +REGISTRYUSER=user +REGISTRYPASSWORD=default + +version: + @printf $(VERSIONTAG) > $(VERSIONFILEPATH)/$(VERSIONFILENAME); + +check_environment: + @$(MAKEPATH)/$(CHECKENVCMD) + +compile_adminserver: + @echo "compiling binary for adminserver..." + @$(GOBUILD) -o $(ADMINSERVERBINARYPATH)/$(ADMINSERVERBINARYNAME) $(ADMINSERVERSOURCECODE) + @echo "Done." + +compile_ui: + @echo "compiling binary for ui..." + @$(GOBUILD) -o $(UIBINARYPATH)/$(UIBINARYNAME) $(UISOURCECODE) + @echo "Done." + +compile_jobservice: + @echo "compiling binary for jobservice..." + @$(GOBUILD) -o $(JOBSERVICEBINARYPATH)/$(JOBSERVICEBINARYNAME) $(JOBSERVICESOURCECODE) + @echo "Done." + +compile_clarity: + @echo "compiling binary for clarity ui..." + @if [ "$(HTTPPROXY)" != "" ] ; then \ + $(DOCKERCMD) run --rm -v $(UIPATH)/static:$(CLARITYSEEDPATH)/dist -v $(UINGPATH)/src:$(CLARITYSEEDPATH)/src $(CLARITYIMAGE) $(SHELL) $(CLARITYBUILDSCRIPT) -p $(HTTPPROXY); \ + else \ + $(DOCKERCMD) run --rm -v $(UIPATH)/static:$(CLARITYSEEDPATH)/dist -v $(UINGPATH)/src:$(CLARITYSEEDPATH)/src $(CLARITYIMAGE) $(SHELL) $(CLARITYBUILDSCRIPT); \ + fi + @echo "Done." + +compile_normal: compile_clarity compile_adminserver compile_ui compile_jobservice + +compile_golangimage: compile_clarity + @echo "compiling binary for adminserver (golang image)..." + @echo $(GOBASEPATH) + @echo $(GOBUILDPATH) + @$(DOCKERCMD) run --rm -v $(BUILDPATH):$(GOBUILDPATH) -w $(GOBUILDPATH_ADMINSERVER) $(GOBUILDIMAGE) $(GOIMAGEBUILD) -v -o $(GOBUILDMAKEPATH_ADMINSERVER)/$(ADMINSERVERBINARYNAME) + @echo "Done." + + @echo "compiling binary for ui (golang image)..." + @echo $(GOBASEPATH) + @echo $(GOBUILDPATH) + @$(DOCKERCMD) run --rm -v $(BUILDPATH):$(GOBUILDPATH) -w $(GOBUILDPATH_UI) $(GOBUILDIMAGE) $(GOIMAGEBUILD) -v -o $(GOBUILDMAKEPATH_UI)/$(UIBINARYNAME) + @echo "Done." + + @echo "compiling binary for jobservice (golang image)..." + @$(DOCKERCMD) run --rm -v $(BUILDPATH):$(GOBUILDPATH) -w $(GOBUILDPATH_JOBSERVICE) $(GOBUILDIMAGE) $(GOIMAGEBUILD) -v -o $(GOBUILDMAKEPATH_JOBSERVICE)/$(JOBSERVICEBINARYNAME) + @echo "Done." + +compile:check_environment $(COMPILETAG) + +prepare: + @echo "preparing..." + @if [ "$(NOTARYFLAG)" = "true" ] ; then \ + $(MAKEPATH)/$(PREPARECMD) --conf $(CONFIGPATH)/$(CONFIGFILE) --with-notary; \ + else \ + $(MAKEPATH)/$(PREPARECMD) --conf $(CONFIGPATH)/$(CONFIGFILE) ; \ + fi + +build_common: version + @echo "buildging db container for photon..." + @cd $(DOCKERFILEPATH_DB) && $(DOCKERBUILD) -f $(DOCKERFILENAME_DB) -t $(DOCKERIMAGENAME_DB):$(VERSIONTAG) . + @echo "Done." + +build_photon: build_common + make -f $(MAKEFILEPATH_PHOTON)/Makefile build -e DEVFLAG=$(DEVFLAG) + +build: build_$(BASEIMAGE) + +modify_composefile: + @echo "preparing docker-compose file..." + @cp $(DOCKERCOMPOSEFILEPATH)/$(DOCKERCOMPOSETPLFILENAME) $(DOCKERCOMPOSEFILEPATH)/$(DOCKERCOMPOSEFILENAME) + @$(SEDCMD) -i 's/__version__/$(VERSIONTAG)/g' $(DOCKERCOMPOSEFILEPATH)/$(DOCKERCOMPOSEFILENAME) + +install: compile build prepare modify_composefile start + +package_online: modify_composefile + @echo "packing online package ..." + @cp -r make $(HARBORPKG) + @if [ -n "$(REGISTRYSERVER)" ] ; then \ + $(SEDCMD) -i 's/image\: vmware/image\: $(REGISTRYSERVER)\/$(REGISTRYPROJECTNAME)/' \ + $(HARBORPKG)/docker-compose.yml ; \ + fi + @cp LICENSE $(HARBORPKG)/LICENSE + @cp NOTICE $(HARBORPKG)/NOTICE + + @if [ "$(NOTARYFLAG)" = "true" ] ; then \ + $(TARCMD) -zcvf harbor-online-installer-$(VERSIONTAG).tgz \ + $(HARBORPKG)/common/templates $(HARBORPKG)/prepare \ + $(HARBORPKG)/LICENSE $(HARBORPKG)/NOTICE \ + $(HARBORPKG)/install.sh $(HARBORPKG)/$(DOCKERCOMPOSEFILENAME) \ + $(HARBORPKG)/harbor.cfg $(HARBORPKG)/$(DOCKERCOMPOSENOTARYFILENAME); \ + else \ + $(TARCMD) -zcvf harbor-online-installer-$(VERSIONTAG).tgz \ + $(HARBORPKG)/common/templates $(HARBORPKG)/prepare \ + $(HARBORPKG)/LICENSE $(HARBORPKG)/NOTICE \ + $(HARBORPKG)/install.sh $(HARBORPKG)/$(DOCKERCOMPOSEFILENAME) \ + $(HARBORPKG)/harbor.cfg ; \ + fi + + @rm -rf $(HARBORPKG) + @echo "Done." + +package_offline: compile build modify_composefile + @echo "packing offline package ..." + @cp -r make $(HARBORPKG) + + @cp LICENSE $(HARBORPKG)/LICENSE + @cp NOTICE $(HARBORPKG)/NOTICE + + @echo "pulling nginx and registry..." + @$(DOCKERPULL) registry:$(REGISTRYVERSION) + @$(DOCKERPULL) nginx:$(NGINXVERSION) + @if [ "$(NOTARYFLAG)" = "true" ] ; then \ + echo "pulling notary and harbor-notary-db..."; \ + $(DOCKERPULL) vmware/notary-photon:$(NOTARYVERSION); \ + $(DOCKERPULL) vmware/notary-photon:$(NOTARYSIGNERVERSION); \ + $(DOCKERPULL) vmware/harbor-notary-db:$(MARIADBVERSION); \ + fi + + @echo "saving harbor docker image" + @if [ "$(NOTARYFLAG)" = "true" ] ; then \ + $(DOCKERSAVE) -o $(HARBORPKG)/$(DOCKERIMGFILE).$(VERSIONTAG).tgz \ + $(DOCKERIMAGENAME_ADMINSERVER):$(VERSIONTAG) \ + $(DOCKERIMAGENAME_UI):$(VERSIONTAG) \ + $(DOCKERIMAGENAME_LOG):$(VERSIONTAG) \ + $(DOCKERIMAGENAME_DB):$(VERSIONTAG) \ + $(DOCKERIMAGENAME_JOBSERVICE):$(VERSIONTAG) \ + nginx:$(NGINXVERSION) vmware/registry:$(REGISTRYVERSION) photon:$(PHOTONVERSION) \ + vmware/notary-photon:$(NOTARYVERSION) vmware/notary-photon:$(NOTARYSIGNERVERSION) vmware/harbor-notary-db:$(MARIADBVERSION); \ + else \ + $(DOCKERSAVE) -o $(HARBORPKG)/$(DOCKERIMGFILE).$(VERSIONTAG).tgz \ + $(DOCKERIMAGENAME_ADMINSERVER):$(VERSIONTAG) \ + $(DOCKERIMAGENAME_UI):$(VERSIONTAG) \ + $(DOCKERIMAGENAME_LOG):$(VERSIONTAG) \ + $(DOCKERIMAGENAME_DB):$(VERSIONTAG) \ + $(DOCKERIMAGENAME_JOBSERVICE):$(VERSIONTAG) \ + nginx:$(NGINXVERSION) vmware/registry:$(REGISTRYVERSION) photon:$(PHOTONVERSION) ; \ + fi + + @if [ "$(NOTARYFLAG)" = "true" ] ; then \ + $(TARCMD) -zcvf harbor-offline-installer-$(VERSIONTAG).tgz \ + $(HARBORPKG)/common/templates $(HARBORPKG)/$(DOCKERIMGFILE).$(VERSIONTAG).tgz \ + $(HARBORPKG)/prepare $(HARBORPKG)/NOTICE \ + $(HARBORPKG)/LICENSE $(HARBORPKG)/install.sh \ + $(HARBORPKG)/harbor.cfg $(HARBORPKG)/$(DOCKERCOMPOSEFILENAME) \ + $(HARBORPKG)/$(DOCKERCOMPOSENOTARYFILENAME) ; \ + else \ + $(TARCMD) -zcvf harbor-offline-installer-$(VERSIONTAG).tgz \ + $(HARBORPKG)/common/templates $(HARBORPKG)/$(DOCKERIMGFILE).$(VERSIONTAG).tgz \ + $(HARBORPKG)/prepare $(HARBORPKG)/NOTICE \ + $(HARBORPKG)/LICENSE $(HARBORPKG)/install.sh \ + $(HARBORPKG)/harbor.cfg $(HARBORPKG)/$(DOCKERCOMPOSEFILENAME) ; \ + fi + + @rm -rf $(HARBORPKG) + @echo "Done." + +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_UI):$(VERSIONTAG) $(REGISTRYSERVER)$(DOCKERIMAGENAME_UI):$(VERSIONTAG) + @$(PUSHSCRIPTPATH)/$(PUSHSCRIPTNAME) $(REGISTRYSERVER)$(DOCKERIMAGENAME_UI):$(VERSIONTAG) \ + $(REGISTRYUSER) $(REGISTRYPASSWORD) $(REGISTRYSERVER) + @$(DOCKERRMIMAGE) $(REGISTRYSERVER)$(DOCKERIMAGENAME_UI):$(VERSIONTAG) + + @$(DOCKERTAG) $(DOCKERIMAGENAME_JOBSERVICE):$(VERSIONTAG) $(REGISTRYSERVER)$(DOCKERIMAGENAME_JOBSERVICE):$(VERSIONTAG) + @$(PUSHSCRIPTPATH)/$(PUSHSCRIPTNAME) $(REGISTRYSERVER)$(DOCKERIMAGENAME_JOBSERVICE):$(VERSIONTAG) \ + $(REGISTRYUSER) $(REGISTRYPASSWORD) $(REGISTRYSERVER) + @$(DOCKERRMIMAGE) $(REGISTRYSERVER)$(DOCKERIMAGENAME_JOBSERVICE):$(VERSIONTAG) + + @$(DOCKERTAG) $(DOCKERIMAGENAME_LOG):$(VERSIONTAG) $(REGISTRYSERVER)$(DOCKERIMAGENAME_LOG):$(VERSIONTAG) + @$(PUSHSCRIPTPATH)/$(PUSHSCRIPTNAME) $(REGISTRYSERVER)$(DOCKERIMAGENAME_LOG):$(VERSIONTAG) \ + $(REGISTRYUSER) $(REGISTRYPASSWORD) $(REGISTRYSERVER) + @$(DOCKERRMIMAGE) $(REGISTRYSERVER)$(DOCKERIMAGENAME_LOG):$(VERSIONTAG) + + @$(DOCKERTAG) $(DOCKERIMAGENAME_DB):$(VERSIONTAG) $(REGISTRYSERVER)$(DOCKERIMAGENAME_DB):$(VERSIONTAG) + @$(PUSHSCRIPTPATH)/$(PUSHSCRIPTNAME) $(REGISTRYSERVER)$(DOCKERIMAGENAME_DB):$(VERSIONTAG) \ + $(REGISTRYUSER) $(REGISTRYPASSWORD) $(REGISTRYSERVER) + @$(DOCKERRMIMAGE) $(REGISTRYSERVER)$(DOCKERIMAGENAME_DB):$(VERSIONTAG) + +start: + @echo "loading harbor images..." + @if [ "$(NOTARYFLAG)" = "true" ] ; then \ + $(DOCKERCOMPOSECMD) -f $(DOCKERCOMPOSEFILEPATH)/$(DOCKERCOMPOSEFILENAME) -f $(DOCKERCOMPOSEFILEPATH)/$(DOCKERCOMPOSENOTARYFILENAME) up -d ; \ + else \ + $(DOCKERCOMPOSECMD) -f $(DOCKERCOMPOSEFILEPATH)/$(DOCKERCOMPOSEFILENAME) up -d ; \ + fi + @echo "Start complete. You can visit harbor now." + +down: + @echo "Please make sure to set -e NOTARYFLAG=true if you are using Notary in Harbor, otherwise the Notary containers cannot be stop automaticlly." + @while [ -z "$$CONTINUE" ]; do \ + read -r -p "Type anything but Y or y to exit. [Y/N]: " CONTINUE; \ + done ; \ + [ $$CONTINUE = "y" ] || [ $$CONTINUE = "Y" ] || (echo "Exiting."; exit 1;) + @echo "stoping harbor instance..." + @if [ "$(NOTARYFLAG)" = "true" ] ; then \ + $(DOCKERCOMPOSECMD) -f $(DOCKERCOMPOSEFILEPATH)/$(DOCKERCOMPOSEFILENAME) -f $(DOCKERCOMPOSEFILEPATH)/$(DOCKERCOMPOSENOTARYFILENAME) down ; \ + else \ + $(DOCKERCOMPOSECMD) -f $(DOCKERCOMPOSEFILEPATH)/$(DOCKERCOMPOSEFILENAME) down ; \ + fi + @echo "Done." + +cleanbinary: + @echo "cleaning binary..." + @if [ -f $(ADMINSERVERBINARYPATH)/$(ADMINSERVERBINARYNAME) ] ; then rm $(ADMINSERVERBINARYPATH)/$(ADMINSERVERBINARYNAME) ; fi + @if [ -f $(UIBINARYPATH)/$(UIBINARYNAME) ] ; then rm $(UIBINARYPATH)/$(UIBINARYNAME) ; fi + @if [ -f $(JOBSERVICEBINARYPATH)/$(JOBSERVICEBINARYNAME) ] ; then rm $(JOBSERVICEBINARYPATH)/$(JOBSERVICEBINARYNAME) ; fi + +cleanimage: + @echo "cleaning image for photon..." + - $(DOCKERRMIMAGE) -f $(DOCKERIMAGENAME_ADMINSERVER):$(VERSIONTAG) + - $(DOCKERRMIMAGE) -f $(DOCKERIMAGENAME_UI):$(VERSIONTAG) + - $(DOCKERRMIMAGE) -f $(DOCKERIMAGENAME_DB):$(VERSIONTAG) + - $(DOCKERRMIMAGE) -f $(DOCKERIMAGENAME_JOBSERVICE):$(VERSIONTAG) + - $(DOCKERRMIMAGE) -f $(DOCKERIMAGENAME_LOG):$(VERSIONTAG) +# - $(DOCKERRMIMAGE) -f registry:$(REGISTRYVERSION) +# - $(DOCKERRMIMAGE) -f nginx:1.11.5 + +cleandockercomposefile: + @echo "cleaning $(DOCKERCOMPOSEFILEPATH)/docker-compose.yml" + @if [ -f $(DOCKERCOMPOSEFILEPATH)/docker-compose.yml ] ; then rm $(DOCKERCOMPOSEFILEPATH)/docker-compose.yml ; fi + +cleanversiontag: + @echo "cleaning version TAG" + @rm -rf $(VERSIONFILEPATH)/$(VERSIONFILENAME) + +cleanpackage: + @echo "cleaning harbor install package" + @if [ -d $(BUILDPATH)/harbor ] ; then rm -rf $(BUILDPATH)/harbor ; fi + @if [ -f $(BUILDPATH)/harbor-online-installer-$(VERSIONTAG).tgz ] ; \ + then rm $(BUILDPATH)/harbor-online-installer-$(VERSIONTAG).tgz ; fi + @if [ -f $(BUILDPATH)/harbor-offline-installer-$(VERSIONTAG).tgz ] ; \ + then rm $(BUILDPATH)/harbor-offline-installer-$(VERSIONTAG).tgz ; fi + +.PHONY: cleanall +cleanall: cleanbinary cleanimage cleandockercomposefile cleanversiontag cleanpackage + +clean: + @echo " make cleanall: remove binary, Harbor images, specific version docker-compose" + @echo " file, specific version tag, online and offline install package" + @echo " make cleanbinary: remove ui and jobservice binary" + @echo " make cleanimage: remove Harbor images" + @echo " make cleandockercomposefile: remove specific version docker-compose" + @echo " make cleanversiontag: cleanpackageremove specific version tag" + @echo " make cleanpackage: remove online and offline install package" + +all: install diff --git a/VERSION b/VERSION new file mode 100644 index 000000000..90012116c --- /dev/null +++ b/VERSION @@ -0,0 +1 @@ +dev \ No newline at end of file diff --git a/docs/swagger.yaml b/docs/swagger.yaml index 293894750..213ad9b03 100644 --- a/docs/swagger.yaml +++ b/docs/swagger.yaml @@ -2028,6 +2028,9 @@ definitions: has_ca_root: type: boolean description: Indicate whether there is a ca root cert file ready for download in the file system. + harbor_version: + type: string + description: The build version of Harbor. SystemInfo: type: object properties: diff --git a/make/dev/docker-compose.yml b/make/dev/docker-compose.yml index 3b2f555a9..3079e5c1b 100644 --- a/make/dev/docker-compose.yml +++ b/make/dev/docker-compose.yml @@ -50,6 +50,7 @@ services: volumes: - /data/config/:/etc/adminserver/ - /data/secretkey:/etc/adminserver/key + - /data/:/data/ depends_on: - log logging: diff --git a/make/docker-compose.tpl b/make/docker-compose.tpl index 9ffaca049..decf381ee 100644 --- a/make/docker-compose.tpl +++ b/make/docker-compose.tpl @@ -56,6 +56,7 @@ services: volumes: - /data/config/:/etc/adminserver/ - /data/secretkey:/etc/adminserver/key + - /data/:/data/ networks: - harbor depends_on: @@ -74,7 +75,6 @@ services: volumes: - ./common/config/ui/app.conf:/etc/ui/app.conf - ./common/config/ui/private_key.pem:/etc/ui/private_key.pem - - /data:/harbor_storage - /data/secretkey:/etc/ui/key networks: - harbor diff --git a/make/photon/ui/Dockerfile b/make/photon/ui/Dockerfile index 039d1603a..6b1e067c1 100644 --- a/make/photon/ui/Dockerfile +++ b/make/photon/ui/Dockerfile @@ -7,6 +7,7 @@ COPY ./make/dev/ui/harbor_ui /harbor/ COPY ./src/ui/views /harbor/views COPY ./src/ui/static /harbor/static COPY ./src/favicon.ico /harbor/favicon.ico +COPY ./VERSION /harbor/VERSION RUN chmod u+x /harbor/harbor_ui diff --git a/src/adminserver/api/cfg_test.go b/src/adminserver/api/cfg_test.go index bfaf2fd7f..5e07c4b1e 100644 --- a/src/adminserver/api/cfg_test.go +++ b/src/adminserver/api/cfg_test.go @@ -26,7 +26,7 @@ import ( "testing" "github.com/vmware/harbor/src/adminserver/systemcfg" - comcfg "github.com/vmware/harbor/src/common/config" + "github.com/vmware/harbor/src/common" "github.com/vmware/harbor/src/common/utils/test" ) @@ -43,7 +43,7 @@ func TestConfigAPI(t *testing.T) { secret := "secret" envs := map[string]string{ - "AUTH_MODE": comcfg.DBAuth, + "AUTH_MODE": common.DBAuth, "JSON_CFG_STORE_PATH": configPath, "KEY_PATH": secretKeyPath, "UI_SECRET": secret, @@ -97,7 +97,7 @@ func TestConfigAPI(t *testing.T) { return } - scope := int(m[comcfg.LDAPScope].(float64)) + scope := int(m[common.LDAPScope].(float64)) if scope != 3 { t.Errorf("unexpected ldap scope: %d != %d", scope, 3) return @@ -105,7 +105,7 @@ func TestConfigAPI(t *testing.T) { // modify configurations c := map[string]interface{}{ - comcfg.AUTHMode: comcfg.LDAPAuth, + common.AUTHMode: common.LDAPAuth, } b, err := json.Marshal(c) @@ -155,9 +155,9 @@ func TestConfigAPI(t *testing.T) { return } - mode := m[comcfg.AUTHMode].(string) - if mode != comcfg.LDAPAuth { - t.Errorf("unexpected auth mode: %s != %s", mode, comcfg.LDAPAuth) + mode := m[common.AUTHMode].(string) + if mode != common.LDAPAuth { + t.Errorf("unexpected auth mode: %s != %s", mode, common.LDAPAuth) return } @@ -203,9 +203,9 @@ func TestConfigAPI(t *testing.T) { return } - mode = m[comcfg.AUTHMode].(string) - if mode != comcfg.DBAuth { - t.Errorf("unexpected auth mode: %s != %s", mode, comcfg.LDAPAuth) + mode = m[common.AUTHMode].(string) + if mode != common.DBAuth { + t.Errorf("unexpected auth mode: %s != %s", mode, common.LDAPAuth) return } } diff --git a/src/adminserver/client/auth/auth.go b/src/adminserver/client/auth/auth.go new file mode 100644 index 000000000..3779f8e7f --- /dev/null +++ b/src/adminserver/client/auth/auth.go @@ -0,0 +1,51 @@ +/* + Copyright (c) 2016 VMware, Inc. All Rights Reserved. + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. +*/ + +package auth + +import ( + "net/http" +) + +// Authorizer authorizes request +type Authorizer interface { + Authorize(*http.Request) error +} + +// NewSecretAuthorizer returns an instance of secretAuthorizer +func NewSecretAuthorizer(cookieName, secret string) Authorizer { + return &secretAuthorizer{ + cookieName: cookieName, + secret: secret, + } +} + +type secretAuthorizer struct { + cookieName string + secret string +} + +func (s *secretAuthorizer) Authorize(req *http.Request) error { + if req == nil { + return nil + } + + req.AddCookie(&http.Cookie{ + Name: s.cookieName, + Value: s.secret, + }) + + return nil +} diff --git a/src/adminserver/client/auth/auth_test.go b/src/adminserver/client/auth/auth_test.go new file mode 100644 index 000000000..62cb79dec --- /dev/null +++ b/src/adminserver/client/auth/auth_test.go @@ -0,0 +1,44 @@ +/* + Copyright (c) 2016 VMware, Inc. All Rights Reserved. + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. +*/ + +package auth + +import ( + "net/http" + "testing" + + "github.com/stretchr/testify/assert" +) + +func TestAuthorize(t *testing.T) { + cookieName := "secret" + secret := "secret" + authorizer := NewSecretAuthorizer(cookieName, secret) + req, err := http.NewRequest("", "", nil) + if !assert.Nil(t, err, "unexpected error") { + return + } + + err = authorizer.Authorize(req) + if !assert.Nil(t, err, "unexpected error") { + return + } + + cookie, err := req.Cookie(cookieName) + if !assert.Nil(t, err, "unexpected error") { + return + } + assert.Equal(t, secret, cookie.Value, "unexpected cookie") +} diff --git a/src/adminserver/client/client.go b/src/adminserver/client/client.go new file mode 100644 index 000000000..afe70d339 --- /dev/null +++ b/src/adminserver/client/client.go @@ -0,0 +1,184 @@ +/* + Copyright (c) 2016 VMware, Inc. All Rights Reserved. + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. +*/ + +package client + +import ( + "bytes" + "encoding/json" + "fmt" + "io" + "io/ioutil" + "net/http" + "strings" + + "github.com/vmware/harbor/src/adminserver/client/auth" + "github.com/vmware/harbor/src/adminserver/systeminfo/imagestorage" + "github.com/vmware/harbor/src/common/utils" +) + +// Client defines methods that an Adminserver client should implement +type Client interface { + // Ping tests the connection with server + Ping() error + // GetCfgs returns system configurations + GetCfgs() (map[string]interface{}, error) + // UpdateCfgs updates system configurations + UpdateCfgs(map[string]interface{}) error + // ResetCfgs resets system configuratoins form environment variables + ResetCfgs() error + // Capacity returns the capacity of image storage + Capacity() (*imagestorage.Capacity, error) +} + +// NewClient return an instance of Adminserver client +func NewClient(baseURL string, authorizer auth.Authorizer) Client { + baseURL = strings.TrimRight(baseURL, "/") + if !strings.Contains(baseURL, "://") { + baseURL = "http://" + baseURL + } + return &client{ + baseURL: baseURL, + client: &http.Client{}, + authorizer: authorizer, + } +} + +type client struct { + baseURL string + client *http.Client + authorizer auth.Authorizer +} + +// do creates request and authorizes it if authorizer is not nil +func (c *client) do(method, relativePath string, body io.Reader) (*http.Response, error) { + url := c.baseURL + relativePath + req, err := http.NewRequest(method, url, body) + if err != nil { + return nil, err + } + + if c.authorizer != nil { + if err := c.authorizer.Authorize(req); err != nil { + return nil, err + } + } + return c.client.Do(req) +} + +func (c *client) Ping() error { + addr := strings.Split(c.baseURL, "://")[1] + if !strings.Contains(addr, ":") { + addr = addr + ":80" + } + + return utils.TestTCPConn(addr, 60, 2) +} + +// GetCfgs ... +func (c *client) GetCfgs() (map[string]interface{}, error) { + resp, err := c.do(http.MethodGet, "/api/configurations", nil) + if err != nil { + return nil, err + } + defer resp.Body.Close() + + b, err := ioutil.ReadAll(resp.Body) + if err != nil { + return nil, err + } + + if resp.StatusCode != http.StatusOK { + return nil, fmt.Errorf("failed to get configurations: %d %s", + resp.StatusCode, b) + } + + cfgs := map[string]interface{}{} + if err = json.Unmarshal(b, &cfgs); err != nil { + return nil, err + } + + return cfgs, nil +} + +// UpdateCfgs ... +func (c *client) UpdateCfgs(cfgs map[string]interface{}) error { + data, err := json.Marshal(cfgs) + if err != nil { + return err + } + + resp, err := c.do(http.MethodPut, "/api/configurations", bytes.NewReader(data)) + if err != nil { + return err + } + defer resp.Body.Close() + + if resp.StatusCode != http.StatusOK { + b, err := ioutil.ReadAll(resp.Body) + if err != nil { + return err + } + return fmt.Errorf("failed to update configurations: %d %s", + resp.StatusCode, b) + } + + return nil +} + +// ResetCfgs ... +func (c *client) ResetCfgs() error { + resp, err := c.do(http.MethodPost, "/api/configurations/reset", nil) + if err != nil { + return err + } + + if resp.StatusCode != http.StatusOK { + b, err := ioutil.ReadAll(resp.Body) + if err != nil { + return err + } + return fmt.Errorf("failed to reset configurations: %d %s", + resp.StatusCode, b) + } + + return nil +} + +// Capacity ... +func (c *client) Capacity() (*imagestorage.Capacity, error) { + resp, err := c.do(http.MethodGet, "/api/systeminfo/capacity", nil) + if err != nil { + return nil, err + } + defer resp.Body.Close() + + b, err := ioutil.ReadAll(resp.Body) + if err != nil { + return nil, err + } + + if resp.StatusCode != http.StatusOK { + return nil, fmt.Errorf("failed to get capacity: %d %s", + resp.StatusCode, b) + } + + capacity := &imagestorage.Capacity{} + if err = json.Unmarshal(b, capacity); err != nil { + return nil, err + } + + return capacity, nil +} diff --git a/src/adminserver/client/client_test.go b/src/adminserver/client/client_test.go new file mode 100644 index 000000000..7c82c231c --- /dev/null +++ b/src/adminserver/client/client_test.go @@ -0,0 +1,82 @@ +/* + Copyright (c) 2016 VMware, Inc. All Rights Reserved. + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. +*/ + +package client + +import ( + "fmt" + "os" + "testing" + + "github.com/stretchr/testify/assert" + "github.com/vmware/harbor/src/common" + "github.com/vmware/harbor/src/common/utils/test" +) + +var c Client + +func TestMain(m *testing.M) { + + server, err := test.NewAdminserver(nil) + if err != nil { + fmt.Printf("failed to create adminserver: %v", err) + os.Exit(1) + } + + c = NewClient(server.URL, nil) + + os.Exit(m.Run()) +} + +func TestPing(t *testing.T) { + err := c.Ping() + assert.Nil(t, err, "unexpected error") +} + +func TestGetCfgs(t *testing.T) { + cfgs, err := c.GetCfgs() + if !assert.Nil(t, err, "unexpected error") { + return + } + + assert.Equal(t, common.DBAuth, cfgs[common.AUTHMode], "unexpected configuration") +} + +func TestUpdateCfgs(t *testing.T) { + cfgs := map[string]interface{}{ + common.AUTHMode: common.LDAPAuth, + } + err := c.UpdateCfgs(cfgs) + if !assert.Nil(t, err, "unexpected error") { + return + } +} + +func TestResetCfgs(t *testing.T) { + err := c.ResetCfgs() + if !assert.Nil(t, err, "unexpected error") { + return + } +} + +func TestCapacity(t *testing.T) { + capacity, err := c.Capacity() + if !assert.Nil(t, err, "unexpected error") { + return + } + + assert.Equal(t, uint64(100), capacity.Total) + assert.Equal(t, uint64(90), capacity.Free) +} diff --git a/src/adminserver/systemcfg/systemcfg.go b/src/adminserver/systemcfg/systemcfg.go index 2d33b063e..51de78d34 100644 --- a/src/adminserver/systemcfg/systemcfg.go +++ b/src/adminserver/systemcfg/systemcfg.go @@ -23,6 +23,7 @@ import ( "github.com/vmware/harbor/src/adminserver/systemcfg/store" "github.com/vmware/harbor/src/adminserver/systemcfg/store/json" + "github.com/vmware/harbor/src/common" comcfg "github.com/vmware/harbor/src/common/config" "github.com/vmware/harbor/src/common/utils" "github.com/vmware/harbor/src/common/utils/log" @@ -40,82 +41,82 @@ var ( // attrs need to be encrypted or decrypted attrs = []string{ - comcfg.EmailPassword, - comcfg.LDAPSearchPwd, - comcfg.MySQLPassword, - comcfg.AdminInitialPassword, + common.EmailPassword, + common.LDAPSearchPwd, + common.MySQLPassword, + common.AdminInitialPassword, } // all configurations need read from environment variables allEnvs = map[string]interface{}{ - comcfg.ExtEndpoint: "EXT_ENDPOINT", - comcfg.AUTHMode: "AUTH_MODE", - comcfg.SelfRegistration: &parser{ + common.ExtEndpoint: "EXT_ENDPOINT", + common.AUTHMode: "AUTH_MODE", + common.SelfRegistration: &parser{ env: "SELF_REGISTRATION", parse: parseStringToBool, }, - comcfg.DatabaseType: "DATABASE_TYPE", - comcfg.MySQLHost: "MYSQL_HOST", - comcfg.MySQLPort: &parser{ + common.DatabaseType: "DATABASE_TYPE", + common.MySQLHost: "MYSQL_HOST", + common.MySQLPort: &parser{ env: "MYSQL_PORT", parse: parseStringToInt, }, - comcfg.MySQLUsername: "MYSQL_USR", - comcfg.MySQLPassword: "MYSQL_PWD", - comcfg.MySQLDatabase: "MYSQL_DATABASE", - comcfg.SQLiteFile: "SQLITE_FILE", - comcfg.LDAPURL: "LDAP_URL", - comcfg.LDAPSearchDN: "LDAP_SEARCH_DN", - comcfg.LDAPSearchPwd: "LDAP_SEARCH_PWD", - comcfg.LDAPBaseDN: "LDAP_BASE_DN", - comcfg.LDAPFilter: "LDAP_FILTER", - comcfg.LDAPUID: "LDAP_UID", - comcfg.LDAPScope: &parser{ + common.MySQLUsername: "MYSQL_USR", + common.MySQLPassword: "MYSQL_PWD", + common.MySQLDatabase: "MYSQL_DATABASE", + common.SQLiteFile: "SQLITE_FILE", + 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, }, - comcfg.LDAPTimeout: &parser{ + common.LDAPTimeout: &parser{ env: "LDAP_TIMEOUT", parse: parseStringToInt, }, - comcfg.EmailHost: "EMAIL_HOST", - comcfg.EmailPort: &parser{ + common.EmailHost: "EMAIL_HOST", + common.EmailPort: &parser{ env: "EMAIL_PORT", parse: parseStringToInt, }, - comcfg.EmailUsername: "EMAIL_USR", - comcfg.EmailPassword: "EMAIL_PWD", - comcfg.EmailSSL: &parser{ + common.EmailUsername: "EMAIL_USR", + common.EmailPassword: "EMAIL_PWD", + common.EmailSSL: &parser{ env: "EMAIL_SSL", parse: parseStringToBool, }, - comcfg.EmailFrom: "EMAIL_FROM", - comcfg.EmailIdentity: "EMAIL_IDENTITY", - comcfg.RegistryURL: "REGISTRY_URL", - comcfg.TokenExpiration: &parser{ + common.EmailFrom: "EMAIL_FROM", + common.EmailIdentity: "EMAIL_IDENTITY", + common.RegistryURL: "REGISTRY_URL", + common.TokenExpiration: &parser{ env: "TOKEN_EXPIRATION", parse: parseStringToInt, }, - comcfg.UseCompressedJS: &parser{ + common.UseCompressedJS: &parser{ env: "USE_COMPRESSED_JS", parse: parseStringToBool, }, - comcfg.CfgExpiration: &parser{ + common.CfgExpiration: &parser{ env: "CFG_EXPIRATION", parse: parseStringToInt, }, - comcfg.MaxJobWorkers: &parser{ + common.MaxJobWorkers: &parser{ env: "MAX_JOB_WORKERS", parse: parseStringToInt, }, - comcfg.VerifyRemoteCert: &parser{ + common.VerifyRemoteCert: &parser{ env: "VERIFY_REMOTE_CERT", parse: parseStringToBool, }, - comcfg.ProjectCreationRestriction: "PROJECT_CREATION_RESTRICTION", - comcfg.AdminInitialPassword: "HARBOR_ADMIN_PASSWORD", - comcfg.AdmiralEndpoint: "ADMIRAL_URL", - comcfg.WithNotary: &parser{ + common.ProjectCreationRestriction: "PROJECT_CREATION_RESTRICTION", + common.AdminInitialPassword: "HARBOR_ADMIN_PASSWORD", + common.AdmiralEndpoint: "ADMIRAL_URL", + common.WithNotary: &parser{ env: "WITH_NOTARY", parse: parseStringToBool, }, @@ -124,23 +125,23 @@ var ( // configurations need read from environment variables // every time the system startup repeatLoadEnvs = map[string]interface{}{ - comcfg.ExtEndpoint: "EXT_ENDPOINT", - comcfg.MySQLPassword: "MYSQL_PWD", - comcfg.MaxJobWorkers: &parser{ + common.ExtEndpoint: "EXT_ENDPOINT", + common.MySQLPassword: "MYSQL_PWD", + common.MaxJobWorkers: &parser{ env: "MAX_JOB_WORKERS", parse: parseStringToInt, }, // TODO remove this config? - comcfg.UseCompressedJS: &parser{ + common.UseCompressedJS: &parser{ env: "USE_COMPRESSED_JS", parse: parseStringToBool, }, - comcfg.CfgExpiration: &parser{ + common.CfgExpiration: &parser{ env: "CFG_EXPIRATION", parse: parseStringToInt, }, - comcfg.AdmiralEndpoint: "ADMIRAL_URL", - comcfg.WithNotary: &parser{ + common.AdmiralEndpoint: "ADMIRAL_URL", + common.WithNotary: &parser{ env: "WITH_NOTARY", parse: parseStringToBool, }, diff --git a/src/adminserver/systemcfg/systemcfg_test.go b/src/adminserver/systemcfg/systemcfg_test.go index d65e19ea3..1d41fe7bf 100644 --- a/src/adminserver/systemcfg/systemcfg_test.go +++ b/src/adminserver/systemcfg/systemcfg_test.go @@ -19,7 +19,7 @@ import ( "os" "testing" - comcfg "github.com/vmware/harbor/src/common/config" + "github.com/vmware/harbor/src/common" "github.com/vmware/harbor/src/common/utils/test" ) @@ -54,7 +54,7 @@ func TestSystemcfg(t *testing.T) { } m := map[string]string{ - "AUTH_MODE": comcfg.DBAuth, + "AUTH_MODE": common.DBAuth, "LDAP_SCOPE": "1", "LDAP_TIMEOUT": "30", "MYSQL_PORT": "3306", @@ -93,13 +93,13 @@ func TestSystemcfg(t *testing.T) { return } - if cfg[comcfg.AUTHMode] != comcfg.DBAuth { + if cfg[common.AUTHMode] != common.DBAuth { t.Errorf("unexpected auth mode: %s != %s", - cfg[comcfg.AUTHMode], comcfg.DBAuth) + cfg[common.AUTHMode], common.DBAuth) return } - cfg[comcfg.AUTHMode] = comcfg.LDAPAuth + cfg[common.AUTHMode] = common.LDAPAuth if err = UpdateSystemCfg(cfg); err != nil { t.Errorf("failed to update system configurations: %v", err) @@ -112,9 +112,9 @@ func TestSystemcfg(t *testing.T) { return } - if cfg[comcfg.AUTHMode] != comcfg.LDAPAuth { + if cfg[common.AUTHMode] != common.LDAPAuth { t.Errorf("unexpected auth mode: %s != %s", - cfg[comcfg.AUTHMode], comcfg.DBAuth) + cfg[common.AUTHMode], common.DBAuth) return } @@ -129,9 +129,9 @@ func TestSystemcfg(t *testing.T) { return } - if cfg[comcfg.AUTHMode] != comcfg.DBAuth { + if cfg[common.AUTHMode] != common.DBAuth { t.Errorf("unexpected auth mode: %s != %s", - cfg[comcfg.AUTHMode], comcfg.DBAuth) + cfg[common.AUTHMode], common.DBAuth) return } } diff --git a/src/common/config/config.go b/src/common/config/config.go index 55baccd6d..3e8faeecf 100644 --- a/src/common/config/config.go +++ b/src/common/config/config.go @@ -17,83 +17,26 @@ package config import ( - "bytes" - "encoding/json" "fmt" - "io/ioutil" - "net/http" - "strings" "time" "github.com/astaxie/beego/cache" - "github.com/vmware/harbor/src/common/utils" - "github.com/vmware/harbor/src/common/utils/log" -) - -// const variables -const ( - DBAuth = "db_auth" - LDAPAuth = "ldap_auth" - ProCrtRestrEveryone = "everyone" - ProCrtRestrAdmOnly = "adminonly" - LDAPScopeBase = "1" - LDAPScopeOnelevel = "2" - LDAPScopeSubtree = "3" - - ExtEndpoint = "ext_endpoint" - AUTHMode = "auth_mode" - DatabaseType = "database_type" - MySQLHost = "mysql_host" - MySQLPort = "mysql_port" - MySQLUsername = "mysql_username" - MySQLPassword = "mysql_password" - MySQLDatabase = "mysql_database" - SQLiteFile = "sqlite_file" - SelfRegistration = "self_registration" - LDAPURL = "ldap_url" - LDAPSearchDN = "ldap_search_dn" - LDAPSearchPwd = "ldap_search_password" - LDAPBaseDN = "ldap_base_dn" - LDAPUID = "ldap_uid" - LDAPFilter = "ldap_filter" - LDAPScope = "ldap_scope" - LDAPTimeout = "ldap_timeout" - TokenServiceURL = "token_service_url" - RegistryURL = "registry_url" - EmailHost = "email_host" - EmailPort = "email_port" - EmailUsername = "email_username" - EmailPassword = "email_password" - EmailFrom = "email_from" - EmailSSL = "email_ssl" - EmailIdentity = "email_identity" - ProjectCreationRestriction = "project_creation_restriction" - VerifyRemoteCert = "verify_remote_cert" - MaxJobWorkers = "max_job_workers" - TokenExpiration = "token_expiration" - CfgExpiration = "cfg_expiration" - JobLogDir = "job_log_dir" - UseCompressedJS = "use_compressed_js" - AdminInitialPassword = "admin_initial_password" - AdmiralEndpoint = "admiral_url" - WithNotary = "with_notary" + "github.com/vmware/harbor/src/adminserver/client" + "github.com/vmware/harbor/src/common" ) // Manager manages configurations type Manager struct { - Loader *Loader - Parser *Parser + client client.Client Cache bool cache cache.Cache key string } // NewManager returns an instance of Manager -// url: the url from which loader loads configurations -func NewManager(url, secret string, enableCache bool) *Manager { +func NewManager(client client.Client, enableCache bool) *Manager { m := &Manager{ - Loader: NewLoader(url, secret), - Parser: &Parser{}, + client: client, } if enableCache { @@ -105,19 +48,9 @@ func NewManager(url, secret string, enableCache bool) *Manager { return m } -// Init loader -func (m *Manager) Init() error { - return m.Loader.Init() -} - // Load configurations, if cache is enabled, cache the configurations func (m *Manager) Load() (map[string]interface{}, error) { - b, err := m.Loader.Load() - if err != nil { - return nil, err - } - - c, err := m.Parser.Parse(b) + c, err := m.client.GetCfgs() if err != nil { return nil, err } @@ -127,7 +60,15 @@ func (m *Manager) Load() (map[string]interface{}, error) { if err != nil { return nil, err } - if err = m.cache.Put(m.key, c, + + // copy the configuration map so that later modification to the + // map does not effect the cached value + cachedCfgs := map[string]interface{}{} + for k, v := range c { + cachedCfgs[k] = v + } + + if err = m.cache.Put(m.key, cachedCfgs, time.Duration(expi)*time.Second); err != nil { return nil, err } @@ -138,7 +79,7 @@ func (m *Manager) Load() (map[string]interface{}, error) { // Reset configurations func (m *Manager) Reset() error { - return m.Loader.Reset() + return m.client.ResetCfgs() } func getCfgExpiration(m map[string]interface{}) (int, error) { @@ -146,7 +87,7 @@ func getCfgExpiration(m map[string]interface{}) (int, error) { return 0, fmt.Errorf("can not get cfg expiration as configurations are null") } - expi, ok := m[CfgExpiration] + expi, ok := m[common.CfgExpiration] if !ok { return 0, fmt.Errorf("cfg expiration is not set") } @@ -167,133 +108,6 @@ func (m *Manager) Get() (map[string]interface{}, error) { } // Upload configurations -func (m *Manager) Upload(b []byte) error { - return m.Loader.Upload(b) -} - -// Loader loads and uploads configurations -type Loader struct { - url string - secret string - client *http.Client -} - -// NewLoader ... -func NewLoader(url, secret string) *Loader { - return &Loader{ - url: url, - secret: secret, - client: &http.Client{}, - } -} - -// Init waits remote server to be ready by testing connections with it -func (l *Loader) Init() error { - addr := l.url - if strings.Contains(addr, "://") { - addr = strings.Split(addr, "://")[1] - } - - if !strings.Contains(addr, ":") { - addr = addr + ":80" - } - - return utils.TestTCPConn(addr, 60, 2) -} - -// Load configurations from remote server -func (l *Loader) Load() ([]byte, error) { - log.Debug("loading configurations...") - req, err := http.NewRequest("GET", l.url+"/api/configurations", nil) - if err != nil { - return nil, err - } - - req.AddCookie(&http.Cookie{ - Name: "secret", - Value: l.secret, - }) - - resp, err := l.client.Do(req) - if err != nil { - return nil, err - } - defer resp.Body.Close() - - if resp.StatusCode != http.StatusOK { - return nil, fmt.Errorf("%d", resp.StatusCode) - } - - b, err := ioutil.ReadAll(resp.Body) - if err != nil { - return nil, err - } - log.Debug("configurations load completed") - return b, nil -} - -// Upload configurations to remote server -func (l *Loader) Upload(b []byte) error { - req, err := http.NewRequest("PUT", l.url+"/api/configurations", bytes.NewReader(b)) - if err != nil { - return err - } - - req.AddCookie(&http.Cookie{ - Name: "secret", - Value: l.secret, - }) - - resp, err := l.client.Do(req) - if err != nil { - return err - } - - if resp.StatusCode != http.StatusOK { - return fmt.Errorf("unexpected http status code: %d", resp.StatusCode) - } - - log.Debug("configurations uploaded") - - return nil -} - -// Reset sends configurations resetting command to -// remote server -func (l *Loader) Reset() error { - req, err := http.NewRequest("POST", l.url+"/api/configurations/reset", nil) - if err != nil { - return err - } - - req.AddCookie(&http.Cookie{ - Name: "secret", - Value: l.secret, - }) - - resp, err := l.client.Do(req) - if err != nil { - return err - } - - if resp.StatusCode != http.StatusOK { - return fmt.Errorf("unexpected http status code: %d", resp.StatusCode) - } - - log.Debug("configurations resetted") - - return nil -} - -// Parser parses configurations -type Parser struct { -} - -// Parse parses []byte to a map configuration -func (p *Parser) Parse(b []byte) (map[string]interface{}, error) { - c := map[string]interface{}{} - if err := json.Unmarshal(b, &c); err != nil { - return nil, err - } - return c, nil +func (m *Manager) Upload(cfgs map[string]interface{}) error { + return m.client.UpdateCfgs(cfgs) } diff --git a/src/common/const.go b/src/common/const.go new file mode 100644 index 000000000..6c18fc58d --- /dev/null +++ b/src/common/const.go @@ -0,0 +1,65 @@ +/* + Copyright (c) 2016 VMware, Inc. All Rights Reserved. + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. +*/ + +package common + +// const variables +const ( + DBAuth = "db_auth" + LDAPAuth = "ldap_auth" + ProCrtRestrEveryone = "everyone" + ProCrtRestrAdmOnly = "adminonly" + LDAPScopeBase = "1" + LDAPScopeOnelevel = "2" + LDAPScopeSubtree = "3" + + ExtEndpoint = "ext_endpoint" + AUTHMode = "auth_mode" + DatabaseType = "database_type" + MySQLHost = "mysql_host" + MySQLPort = "mysql_port" + MySQLUsername = "mysql_username" + MySQLPassword = "mysql_password" + MySQLDatabase = "mysql_database" + SQLiteFile = "sqlite_file" + SelfRegistration = "self_registration" + LDAPURL = "ldap_url" + LDAPSearchDN = "ldap_search_dn" + LDAPSearchPwd = "ldap_search_password" + LDAPBaseDN = "ldap_base_dn" + LDAPUID = "ldap_uid" + LDAPFilter = "ldap_filter" + LDAPScope = "ldap_scope" + LDAPTimeout = "ldap_timeout" + TokenServiceURL = "token_service_url" + RegistryURL = "registry_url" + EmailHost = "email_host" + EmailPort = "email_port" + EmailUsername = "email_username" + EmailPassword = "email_password" + EmailFrom = "email_from" + EmailSSL = "email_ssl" + EmailIdentity = "email_identity" + ProjectCreationRestriction = "project_creation_restriction" + VerifyRemoteCert = "verify_remote_cert" + MaxJobWorkers = "max_job_workers" + TokenExpiration = "token_expiration" + CfgExpiration = "cfg_expiration" + JobLogDir = "job_log_dir" + UseCompressedJS = "use_compressed_js" + AdminInitialPassword = "admin_initial_password" + AdmiralEndpoint = "admiral_url" + WithNotary = "with_notary" +) diff --git a/src/common/utils/ldap/ldap_test.go b/src/common/utils/ldap/ldap_test.go index 7fb3f3143..60cf7c34a 100644 --- a/src/common/utils/ldap/ldap_test.go +++ b/src/common/utils/ldap/ldap_test.go @@ -21,7 +21,7 @@ import ( "os" "testing" - "github.com/vmware/harbor/src/common/config" + "github.com/vmware/harbor/src/common" "github.com/vmware/harbor/src/common/dao" "github.com/vmware/harbor/src/common/models" "github.com/vmware/harbor/src/common/utils/log" @@ -30,24 +30,24 @@ import ( ) var adminServerLdapTestConfig = map[string]interface{}{ - config.ExtEndpoint: "host01.com", - config.AUTHMode: "ldap_auth", - config.DatabaseType: "mysql", - config.MySQLHost: "127.0.0.1", - config.MySQLPort: 3306, - config.MySQLUsername: "root", - config.MySQLPassword: "root123", - config.MySQLDatabase: "registry", - config.SQLiteFile: "/tmp/registry.db", + common.ExtEndpoint: "host01.com", + common.AUTHMode: "ldap_auth", + common.DatabaseType: "mysql", + common.MySQLHost: "127.0.0.1", + common.MySQLPort: 3306, + common.MySQLUsername: "root", + common.MySQLPassword: "root123", + common.MySQLDatabase: "registry", + common.SQLiteFile: "/tmp/registry.db", //config.SelfRegistration: true, - config.LDAPURL: "ldap://127.0.0.1", - config.LDAPSearchDN: "cn=admin,dc=example,dc=com", - config.LDAPSearchPwd: "admin", - config.LDAPBaseDN: "dc=example,dc=com", - config.LDAPUID: "uid", - config.LDAPFilter: "", - config.LDAPScope: 3, - config.LDAPTimeout: 30, + common.LDAPURL: "ldap://127.0.0.1", + common.LDAPSearchDN: "cn=admin,dc=example,dc=com", + common.LDAPSearchPwd: "admin", + common.LDAPBaseDN: "dc=example,dc=com", + common.LDAPUID: "uid", + common.LDAPFilter: "", + common.LDAPScope: 3, + common.LDAPTimeout: 30, // config.TokenServiceURL: "", // config.RegistryURL: "", // config.EmailHost: "", @@ -61,10 +61,10 @@ var adminServerLdapTestConfig = map[string]interface{}{ // config.VerifyRemoteCert: false, // config.MaxJobWorkers: 3, // config.TokenExpiration: 30, - config.CfgExpiration: 5, + common.CfgExpiration: 5, // config.JobLogDir: "/var/log/jobs", // config.UseCompressedJS: true, - config.AdminInitialPassword: "password", + common.AdminInitialPassword: "password", } func TestMain(t *testing.T) { diff --git a/src/common/utils/test/adminserver.go b/src/common/utils/test/adminserver.go index 3b683b5d8..0a0741d1d 100644 --- a/src/common/utils/test/adminserver.go +++ b/src/common/utils/test/adminserver.go @@ -20,46 +20,47 @@ import ( "net/http" "net/http/httptest" - "github.com/vmware/harbor/src/common/config" + "github.com/vmware/harbor/src/adminserver/systeminfo/imagestorage" + "github.com/vmware/harbor/src/common" ) var adminServerDefaultConfig = map[string]interface{}{ - config.ExtEndpoint: "https://host01.com", - config.AUTHMode: config.DBAuth, - config.DatabaseType: "mysql", - config.MySQLHost: "127.0.0.1", - config.MySQLPort: 3306, - config.MySQLUsername: "user01", - config.MySQLPassword: "password", - config.MySQLDatabase: "registry", - config.SQLiteFile: "/tmp/registry.db", - config.SelfRegistration: true, - config.LDAPURL: "ldap://127.0.0.1", - config.LDAPSearchDN: "uid=searchuser,ou=people,dc=mydomain,dc=com", - config.LDAPSearchPwd: "password", - config.LDAPBaseDN: "ou=people,dc=mydomain,dc=com", - config.LDAPUID: "uid", - config.LDAPFilter: "", - config.LDAPScope: 3, - config.LDAPTimeout: 30, - config.TokenServiceURL: "http://token_service", - config.RegistryURL: "http://registry", - config.EmailHost: "127.0.0.1", - config.EmailPort: 25, - config.EmailUsername: "user01", - config.EmailPassword: "password", - config.EmailFrom: "from", - config.EmailSSL: true, - config.EmailIdentity: "", - config.ProjectCreationRestriction: config.ProCrtRestrAdmOnly, - config.VerifyRemoteCert: false, - config.MaxJobWorkers: 3, - config.TokenExpiration: 30, - config.CfgExpiration: 5, - config.UseCompressedJS: true, - config.AdminInitialPassword: "password", - config.AdmiralEndpoint: "http://www.vmware.com", - config.WithNotary: false, + common.ExtEndpoint: "https://host01.com", + common.AUTHMode: common.DBAuth, + common.DatabaseType: "mysql", + common.MySQLHost: "127.0.0.1", + common.MySQLPort: 3306, + common.MySQLUsername: "user01", + common.MySQLPassword: "password", + common.MySQLDatabase: "registry", + common.SQLiteFile: "/tmp/registry.db", + common.SelfRegistration: true, + common.LDAPURL: "ldap://127.0.0.1", + common.LDAPSearchDN: "uid=searchuser,ou=people,dc=mydomain,dc=com", + common.LDAPSearchPwd: "password", + common.LDAPBaseDN: "ou=people,dc=mydomain,dc=com", + common.LDAPUID: "uid", + common.LDAPFilter: "", + common.LDAPScope: 3, + common.LDAPTimeout: 30, + common.TokenServiceURL: "http://token_service", + common.RegistryURL: "http://registry", + common.EmailHost: "127.0.0.1", + common.EmailPort: 25, + common.EmailUsername: "user01", + common.EmailPassword: "password", + common.EmailFrom: "from", + common.EmailSSL: true, + common.EmailIdentity: "", + common.ProjectCreationRestriction: common.ProCrtRestrAdmOnly, + common.VerifyRemoteCert: false, + common.MaxJobWorkers: 3, + common.TokenExpiration: 30, + common.CfgExpiration: 5, + common.UseCompressedJS: true, + common.AdminInitialPassword: "password", + common.AdmiralEndpoint: "http://www.vmware.com", + common.WithNotary: false, } // NewAdminserver returns a mock admin server @@ -101,5 +102,32 @@ func NewAdminserver(config map[string]interface{}) (*httptest.Server, error) { }), }) + capacityHandler, err := NewCapacityHandle() + if err != nil { + return nil, err + } + m = append(m, &RequestHandlerMapping{ + Method: "GET", + Pattern: "/api/systeminfo/capacity", + Handler: capacityHandler, + }) + return NewServer(m...), nil } + +// NewCapacityHandle ... +func NewCapacityHandle() (func(http.ResponseWriter, *http.Request), error) { + capacity := imagestorage.Capacity{ + Total: 100, + Free: 90, + } + b, err := json.Marshal(capacity) + if err != nil { + return nil, err + } + resp := &Response{ + StatusCode: http.StatusOK, + Body: b, + } + return Handler(resp), nil +} diff --git a/src/jobservice/config/config.go b/src/jobservice/config/config.go index d93871bf7..4cc10c45b 100644 --- a/src/jobservice/config/config.go +++ b/src/jobservice/config/config.go @@ -16,21 +16,28 @@ package config import ( + "fmt" "os" + "github.com/vmware/harbor/src/adminserver/client" + "github.com/vmware/harbor/src/adminserver/client/auth" + "github.com/vmware/harbor/src/common" comcfg "github.com/vmware/harbor/src/common/config" "github.com/vmware/harbor/src/common/models" "github.com/vmware/harbor/src/common/utils/log" ) const ( - defaultKeyPath string = "/etc/jobservice/key" - defaultLogDir string = "/var/log/jobs" + defaultKeyPath string = "/etc/jobservice/key" + defaultLogDir string = "/var/log/jobs" + secretCookieName string = "secret" ) var ( - mg *comcfg.Manager - keyProvider comcfg.KeyProvider + // AdminserverClient is a client for adminserver + AdminserverClient client.Client + mg *comcfg.Manager + keyProvider comcfg.KeyProvider ) // Init configurations @@ -42,12 +49,15 @@ func Init() error { if len(adminServerURL) == 0 { adminServerURL = "http://adminserver" } - mg = comcfg.NewManager(adminServerURL, JobserviceSecret(), true) - - if err := mg.Init(); err != nil { - return err + log.Infof("initializing client for adminserver %s ...", adminServerURL) + authorizer := auth.NewSecretAuthorizer(secretCookieName, UISecret()) + AdminserverClient = client.NewClient(adminServerURL, authorizer) + if err := AdminserverClient.Ping(); err != nil { + return fmt.Errorf("failed to ping adminserver: %v", err) } + mg = comcfg.NewManager(AdminserverClient, true) + if _, err := mg.Load(); err != nil { return err } @@ -71,7 +81,7 @@ func VerifyRemoteCert() (bool, error) { if err != nil { return true, err } - return cfg[comcfg.VerifyRemoteCert].(bool), nil + return cfg[common.VerifyRemoteCert].(bool), nil } // Database ... @@ -81,16 +91,16 @@ func Database() (*models.Database, error) { return nil, err } database := &models.Database{} - database.Type = cfg[comcfg.DatabaseType].(string) + database.Type = cfg[common.DatabaseType].(string) mysql := &models.MySQL{} - mysql.Host = cfg[comcfg.MySQLHost].(string) - mysql.Port = int(cfg[comcfg.MySQLPort].(float64)) - mysql.Username = cfg[comcfg.MySQLUsername].(string) - mysql.Password = cfg[comcfg.MySQLPassword].(string) - mysql.Database = cfg[comcfg.MySQLDatabase].(string) + mysql.Host = cfg[common.MySQLHost].(string) + mysql.Port = int(cfg[common.MySQLPort].(float64)) + mysql.Username = cfg[common.MySQLUsername].(string) + mysql.Password = cfg[common.MySQLPassword].(string) + mysql.Database = cfg[common.MySQLDatabase].(string) database.MySQL = mysql sqlite := &models.SQLite{} - sqlite.File = cfg[comcfg.SQLiteFile].(string) + sqlite.File = cfg[common.SQLiteFile].(string) database.SQLite = sqlite return database, nil @@ -102,7 +112,7 @@ func MaxJobWorkers() (int, error) { if err != nil { return 0, err } - return int(cfg[comcfg.MaxJobWorkers].(float64)), nil + return int(cfg[common.MaxJobWorkers].(float64)), nil } // LocalUIURL returns the local ui url, job service will use this URL to call API hosted on ui process @@ -116,7 +126,7 @@ func LocalRegURL() (string, error) { if err != nil { return "", err } - return cfg[comcfg.RegistryURL].(string), nil + return cfg[common.RegistryURL].(string), nil } // LogDir returns the absolute path to which the log file will be written @@ -151,7 +161,7 @@ func ExtEndpoint() (string, error) { if err != nil { return "", err } - return cfg[comcfg.ExtEndpoint].(string), nil + return cfg[common.ExtEndpoint].(string), nil } // InternalTokenServiceEndpoint ... diff --git a/src/ui/api/config.go b/src/ui/api/config.go index 3411f1749..22ccea112 100644 --- a/src/ui/api/config.go +++ b/src/ui/api/config.go @@ -20,8 +20,8 @@ import ( "net/http" "strconv" + "github.com/vmware/harbor/src/common" "github.com/vmware/harbor/src/common/api" - comcfg "github.com/vmware/harbor/src/common/config" "github.com/vmware/harbor/src/common/dao" "github.com/vmware/harbor/src/common/utils/log" "github.com/vmware/harbor/src/ui/config" @@ -30,65 +30,65 @@ import ( var ( // valid keys of configurations which user can modify validKeys = []string{ - comcfg.ExtEndpoint, - comcfg.AUTHMode, - comcfg.DatabaseType, - comcfg.MySQLHost, - comcfg.MySQLPort, - comcfg.MySQLUsername, - comcfg.MySQLPassword, - comcfg.MySQLDatabase, - comcfg.SQLiteFile, - comcfg.SelfRegistration, - comcfg.LDAPURL, - comcfg.LDAPSearchDN, - comcfg.LDAPSearchPwd, - comcfg.LDAPBaseDN, - comcfg.LDAPUID, - comcfg.LDAPFilter, - comcfg.LDAPScope, - comcfg.LDAPTimeout, - comcfg.TokenServiceURL, - comcfg.RegistryURL, - comcfg.EmailHost, - comcfg.EmailPort, - comcfg.EmailUsername, - comcfg.EmailPassword, - comcfg.EmailFrom, - comcfg.EmailSSL, - comcfg.EmailIdentity, - comcfg.ProjectCreationRestriction, - comcfg.VerifyRemoteCert, - comcfg.MaxJobWorkers, - comcfg.TokenExpiration, - comcfg.CfgExpiration, - comcfg.JobLogDir, - comcfg.UseCompressedJS, - comcfg.AdminInitialPassword, + common.ExtEndpoint, + common.AUTHMode, + common.DatabaseType, + common.MySQLHost, + common.MySQLPort, + common.MySQLUsername, + common.MySQLPassword, + common.MySQLDatabase, + common.SQLiteFile, + common.SelfRegistration, + common.LDAPURL, + common.LDAPSearchDN, + common.LDAPSearchPwd, + common.LDAPBaseDN, + common.LDAPUID, + common.LDAPFilter, + common.LDAPScope, + common.LDAPTimeout, + common.TokenServiceURL, + common.RegistryURL, + common.EmailHost, + common.EmailPort, + common.EmailUsername, + common.EmailPassword, + common.EmailFrom, + common.EmailSSL, + common.EmailIdentity, + common.ProjectCreationRestriction, + common.VerifyRemoteCert, + common.MaxJobWorkers, + common.TokenExpiration, + common.CfgExpiration, + common.JobLogDir, + common.UseCompressedJS, + common.AdminInitialPassword, } numKeys = []string{ - comcfg.EmailPort, - comcfg.LDAPScope, - comcfg.LDAPTimeout, - comcfg.MySQLPort, - comcfg.MaxJobWorkers, - comcfg.TokenExpiration, - comcfg.CfgExpiration, + common.EmailPort, + common.LDAPScope, + common.LDAPTimeout, + common.MySQLPort, + common.MaxJobWorkers, + common.TokenExpiration, + common.CfgExpiration, } boolKeys = []string{ - comcfg.EmailSSL, - comcfg.SelfRegistration, - comcfg.VerifyRemoteCert, - comcfg.UseCompressedJS, + common.EmailSSL, + common.SelfRegistration, + common.VerifyRemoteCert, + common.UseCompressedJS, } passwordKeys = []string{ - comcfg.AdminInitialPassword, - comcfg.EmailPassword, - comcfg.LDAPSearchPwd, - comcfg.MySQLPassword, + common.AdminInitialPassword, + common.EmailPassword, + common.LDAPSearchPwd, + common.MySQLPassword, } ) @@ -157,7 +157,7 @@ func (c *ConfigAPI) Put() { c.CustomAbort(http.StatusBadRequest, err.Error()) } - if value, ok := cfg[comcfg.AUTHMode]; ok { + if value, ok := cfg[common.AUTHMode]; ok { mode, err := config.AuthMode() if err != nil { log.Errorf("failed to get auth mode: %v", err) @@ -174,7 +174,7 @@ func (c *ConfigAPI) Put() { if !flag { c.CustomAbort(http.StatusBadRequest, fmt.Sprintf("%s can not be modified as new users have been inserted into database", - comcfg.AUTHMode)) + common.AUTHMode)) } } } @@ -213,14 +213,14 @@ func validateCfg(c map[string]string) (bool, error) { return isSysErr, err } - if value, ok := c[comcfg.AUTHMode]; ok { - if value != comcfg.DBAuth && value != comcfg.LDAPAuth { - return isSysErr, fmt.Errorf("invalid %s, shoud be %s or %s", comcfg.AUTHMode, comcfg.DBAuth, comcfg.LDAPAuth) + if value, ok := c[common.AUTHMode]; ok { + if value != common.DBAuth && value != common.LDAPAuth { + return isSysErr, fmt.Errorf("invalid %s, shoud be %s or %s", common.AUTHMode, common.DBAuth, common.LDAPAuth) } mode = value } - if mode == comcfg.LDAPAuth { + if mode == common.LDAPAuth { ldap, err := config.LDAP() if err != nil { isSysErr = true @@ -228,46 +228,46 @@ func validateCfg(c map[string]string) (bool, error) { } if len(ldap.URL) == 0 { - if _, ok := c[comcfg.LDAPURL]; !ok { - return isSysErr, fmt.Errorf("%s is missing", comcfg.LDAPURL) + if _, ok := c[common.LDAPURL]; !ok { + return isSysErr, fmt.Errorf("%s is missing", common.LDAPURL) } } if len(ldap.BaseDN) == 0 { - if _, ok := c[comcfg.LDAPBaseDN]; !ok { - return isSysErr, fmt.Errorf("%s is missing", comcfg.LDAPBaseDN) + if _, ok := c[common.LDAPBaseDN]; !ok { + return isSysErr, fmt.Errorf("%s is missing", common.LDAPBaseDN) } } if len(ldap.UID) == 0 { - if _, ok := c[comcfg.LDAPUID]; !ok { - return isSysErr, fmt.Errorf("%s is missing", comcfg.LDAPUID) + if _, ok := c[common.LDAPUID]; !ok { + return isSysErr, fmt.Errorf("%s is missing", common.LDAPUID) } } if ldap.Scope == 0 { - if _, ok := c[comcfg.LDAPScope]; !ok { - return isSysErr, fmt.Errorf("%s is missing", comcfg.LDAPScope) + if _, ok := c[common.LDAPScope]; !ok { + return isSysErr, fmt.Errorf("%s is missing", common.LDAPScope) } } } - if ldapURL, ok := c[comcfg.LDAPURL]; ok && len(ldapURL) == 0 { - return isSysErr, fmt.Errorf("%s is empty", comcfg.LDAPURL) + if ldapURL, ok := c[common.LDAPURL]; ok && len(ldapURL) == 0 { + return isSysErr, fmt.Errorf("%s is empty", common.LDAPURL) } - if baseDN, ok := c[comcfg.LDAPBaseDN]; ok && len(baseDN) == 0 { - return isSysErr, fmt.Errorf("%s is empty", comcfg.LDAPBaseDN) + if baseDN, ok := c[common.LDAPBaseDN]; ok && len(baseDN) == 0 { + return isSysErr, fmt.Errorf("%s is empty", common.LDAPBaseDN) } - if uID, ok := c[comcfg.LDAPUID]; ok && len(uID) == 0 { - return isSysErr, fmt.Errorf("%s is empty", comcfg.LDAPUID) + if uID, ok := c[common.LDAPUID]; ok && len(uID) == 0 { + return isSysErr, fmt.Errorf("%s is empty", common.LDAPUID) } - if scope, ok := c[comcfg.LDAPScope]; ok && - scope != comcfg.LDAPScopeBase && - scope != comcfg.LDAPScopeOnelevel && - scope != comcfg.LDAPScopeSubtree { + if scope, ok := c[common.LDAPScope]; ok && + scope != common.LDAPScopeBase && + scope != common.LDAPScopeOnelevel && + scope != common.LDAPScopeSubtree { return isSysErr, fmt.Errorf("invalid %s, should be %s, %s or %s", - comcfg.LDAPScope, - comcfg.LDAPScopeBase, - comcfg.LDAPScopeOnelevel, - comcfg.LDAPScopeSubtree) + common.LDAPScope, + common.LDAPScopeBase, + common.LDAPScopeOnelevel, + common.LDAPScopeSubtree) } for _, k := range boolKeys { @@ -293,19 +293,19 @@ func validateCfg(c map[string]string) (bool, error) { return isSysErr, fmt.Errorf("invalid %s: %s", k, v) } - if (k == comcfg.EmailPort || - k == comcfg.MySQLPort) && n > 65535 { + if (k == common.EmailPort || + k == common.MySQLPort) && n > 65535 { return isSysErr, fmt.Errorf("invalid %s: %s", k, v) } } - if crt, ok := c[comcfg.ProjectCreationRestriction]; ok && - crt != comcfg.ProCrtRestrEveryone && - crt != comcfg.ProCrtRestrAdmOnly { + if crt, ok := c[common.ProjectCreationRestriction]; ok && + crt != common.ProCrtRestrEveryone && + crt != common.ProCrtRestrAdmOnly { return isSysErr, fmt.Errorf("invalid %s, should be %s or %s", - comcfg.ProjectCreationRestriction, - comcfg.ProCrtRestrAdmOnly, - comcfg.ProCrtRestrEveryone) + common.ProjectCreationRestriction, + common.ProCrtRestrAdmOnly, + common.ProCrtRestrEveryone) } return isSysErr, nil @@ -363,7 +363,7 @@ func convertForGet(cfg map[string]interface{}) (map[string]*value, error) { if err != nil { return nil, err } - result[comcfg.AUTHMode].Editable = flag + result[common.AUTHMode].Editable = flag return result, nil } diff --git a/src/ui/api/config_test.go b/src/ui/api/config_test.go index b4016e224..e6f3a6c10 100644 --- a/src/ui/api/config_test.go +++ b/src/ui/api/config_test.go @@ -20,7 +20,8 @@ import ( "testing" "github.com/stretchr/testify/assert" - "github.com/vmware/harbor/src/common/config" + "github.com/vmware/harbor/src/common" + "github.com/vmware/harbor/src/ui/config" ) func TestGetConfig(t *testing.T) { @@ -46,8 +47,13 @@ func TestGetConfig(t *testing.T) { return } - mode := cfg[config.AUTHMode].Value.(string) - assert.Equal(config.DBAuth, mode, fmt.Sprintf("the auth mode should be %s", config.DBAuth)) + mode := cfg[common.AUTHMode].Value.(string) + assert.Equal(common.DBAuth, mode, fmt.Sprintf("the auth mode should be %s", common.DBAuth)) + ccc, err := config.GetSystemCfg() + if err != nil { + t.Logf("failed to get system configurations: %v", err) + } + t.Logf("%v", ccc) } func TestPutConfig(t *testing.T) { @@ -56,7 +62,7 @@ func TestPutConfig(t *testing.T) { apiTest := newHarborAPI() cfg := map[string]string{ - config.VerifyRemoteCert: "0", + common.VerifyRemoteCert: "0", } code, err := apiTest.PutConfig(*admin, cfg) @@ -67,6 +73,11 @@ func TestPutConfig(t *testing.T) { if !assert.Equal(200, code, "the status code of modifying configurations with admin user should be 200") { return } + ccc, err := config.GetSystemCfg() + if err != nil { + t.Logf("failed to get system configurations: %v", err) + } + t.Logf("%v", ccc) } func TestResetConfig(t *testing.T) { @@ -94,11 +105,17 @@ func TestResetConfig(t *testing.T) { return } - value, ok := cfgs[config.VerifyRemoteCert] + value, ok := cfgs[common.VerifyRemoteCert] if !ok { - t.Errorf("%s not found", config.VerifyRemoteCert) + t.Errorf("%s not found", common.VerifyRemoteCert) return } assert.Equal(value.Value.(bool), true, "unexpected value") + + ccc, err := config.GetSystemCfg() + if err != nil { + t.Logf("failed to get system configurations: %v", err) + } + t.Logf("%v", ccc) } diff --git a/src/ui/api/systeminfo.go b/src/ui/api/systeminfo.go index 5748d0d00..88dff92bc 100644 --- a/src/ui/api/systeminfo.go +++ b/src/ui/api/systeminfo.go @@ -1,14 +1,13 @@ package api import ( + "io/ioutil" "net/http" "os" - "path/filepath" "strings" - "syscall" + "github.com/vmware/harbor/src/common" "github.com/vmware/harbor/src/common/api" - comcfg "github.com/vmware/harbor/src/common/config" "github.com/vmware/harbor/src/common/dao" "github.com/vmware/harbor/src/common/utils/log" "github.com/vmware/harbor/src/ui/config" @@ -21,8 +20,8 @@ type SystemInfoAPI struct { isAdmin bool } -const harborStoragePath = "/harbor_storage" const defaultRootCert = "/harbor_storage/ca_download/ca.crt" +const harborVersionFile = "/harbor/VERSION" //SystemInfo models for system info. type SystemInfo struct { @@ -45,6 +44,7 @@ type GeneralInfo struct { ProjectCreationRestrict string `json:"project_creation_restriction"` SelfRegistration bool `json:"self_registration"` HasCARoot bool `json:"has_ca_root"` + HarborVersion string `json:"harbor_version"` } // validate for validating user if an admin. @@ -66,18 +66,16 @@ func (sia *SystemInfoAPI) GetVolumeInfo() { sia.RenderError(http.StatusForbidden, "User does not have admin role.") return } - var stat syscall.Statfs_t - err := syscall.Statfs(filepath.Join("/", harborStoragePath), &stat) - if err != nil { - log.Errorf("Error occurred in syscall.Statfs: %v", err) - sia.CustomAbort(http.StatusInternalServerError, "Internal error.") - return - } + capacity, err := config.AdminserverClient.Capacity() + if err != nil { + log.Errorf("failed to get capacity: %v", err) + sia.CustomAbort(http.StatusInternalServerError, http.StatusText(http.StatusInternalServerError)) + } systemInfo := SystemInfo{ HarborStorage: Storage{ - Total: stat.Blocks * uint64(stat.Bsize), - Free: stat.Bavail * uint64(stat.Bsize), + Total: capacity.Total, + Free: capacity.Free, }, } @@ -112,22 +110,34 @@ func (sia *SystemInfoAPI) GetGeneralInfo() { sia.CustomAbort(http.StatusInternalServerError, "Unexpected error") } var registryURL string - if l := strings.Split(cfg[comcfg.ExtEndpoint].(string), "://"); len(l) > 1 { + if l := strings.Split(cfg[common.ExtEndpoint].(string), "://"); len(l) > 1 { registryURL = l[1] } else { registryURL = l[0] } _, caStatErr := os.Stat(defaultRootCert) + harborVersion := sia.getVersion() info := GeneralInfo{ - AdmiralEndpoint: cfg[comcfg.AdmiralEndpoint].(string), + AdmiralEndpoint: cfg[common.AdmiralEndpoint].(string), WithAdmiral: config.WithAdmiral(), WithNotary: config.WithNotary(), - AuthMode: cfg[comcfg.AUTHMode].(string), - ProjectCreationRestrict: cfg[comcfg.ProjectCreationRestriction].(string), - SelfRegistration: cfg[comcfg.SelfRegistration].(bool), + AuthMode: cfg[common.AUTHMode].(string), + ProjectCreationRestrict: cfg[common.ProjectCreationRestriction].(string), + SelfRegistration: cfg[common.SelfRegistration].(bool), RegistryURL: registryURL, HasCARoot: caStatErr == nil, + HarborVersion: harborVersion, } sia.Data["json"] = info sia.ServeJSON() } + +// GetVersion gets harbor version. +func (sia *SystemInfoAPI) getVersion() string { + version, err := ioutil.ReadFile(harborVersionFile) + if err != nil { + log.Errorf("Error occured getting harbor version: %v", err) + return "" + } + return string(version[:]) +} diff --git a/src/ui/api/systeminfo_test.go b/src/ui/api/systeminfo_test.go index 80892fd84..26a259ba4 100644 --- a/src/ui/api/systeminfo_test.go +++ b/src/ui/api/systeminfo_test.go @@ -3,8 +3,9 @@ package api import ( "encoding/json" "fmt" - "github.com/stretchr/testify/assert" "testing" + + "github.com/stretchr/testify/assert" ) func TestGetVolumeInfo(t *testing.T) { @@ -49,6 +50,7 @@ func TestGetGeneralInfo(t *testing.T) { assert.Nil(err, fmt.Sprintf("Unexpected Error: %v", err)) assert.Equal(false, g.WithNotary, "with notary should be false") assert.Equal(true, g.HasCARoot, "has ca root should be true") + assert.NotEmpty(g.HarborVersion, "harbor version should not be empty") } func TestGetCert(t *testing.T) { diff --git a/src/ui/auth/ldap/ldap_test.go b/src/ui/auth/ldap/ldap_test.go index 11bb924ff..9b001ae55 100644 --- a/src/ui/auth/ldap/ldap_test.go +++ b/src/ui/auth/ldap/ldap_test.go @@ -6,7 +6,7 @@ import ( "os" "testing" - "github.com/vmware/harbor/src/common/config" + "github.com/vmware/harbor/src/common" "github.com/vmware/harbor/src/common/dao" "github.com/vmware/harbor/src/common/models" "github.com/vmware/harbor/src/common/utils/log" @@ -15,24 +15,24 @@ import ( ) var adminServerLdapTestConfig = map[string]interface{}{ - config.ExtEndpoint: "host01.com", - config.AUTHMode: "ldap_auth", - config.DatabaseType: "mysql", - config.MySQLHost: "127.0.0.1", - config.MySQLPort: 3306, - config.MySQLUsername: "root", - config.MySQLPassword: "root123", - config.MySQLDatabase: "registry", - config.SQLiteFile: "/tmp/registry.db", + common.ExtEndpoint: "host01.com", + common.AUTHMode: "ldap_auth", + common.DatabaseType: "mysql", + common.MySQLHost: "127.0.0.1", + common.MySQLPort: 3306, + common.MySQLUsername: "root", + common.MySQLPassword: "root123", + common.MySQLDatabase: "registry", + common.SQLiteFile: "/tmp/registry.db", //config.SelfRegistration: true, - config.LDAPURL: "ldap://127.0.0.1", - config.LDAPSearchDN: "cn=admin,dc=example,dc=com", - config.LDAPSearchPwd: "admin", - config.LDAPBaseDN: "dc=example,dc=com", - config.LDAPUID: "uid", - config.LDAPFilter: "", - config.LDAPScope: 3, - config.LDAPTimeout: 30, + common.LDAPURL: "ldap://127.0.0.1", + common.LDAPSearchDN: "cn=admin,dc=example,dc=com", + common.LDAPSearchPwd: "admin", + common.LDAPBaseDN: "dc=example,dc=com", + common.LDAPUID: "uid", + common.LDAPFilter: "", + common.LDAPScope: 3, + common.LDAPTimeout: 30, // config.TokenServiceURL: "", // config.RegistryURL: "", // config.EmailHost: "", @@ -46,10 +46,10 @@ var adminServerLdapTestConfig = map[string]interface{}{ // config.VerifyRemoteCert: false, // config.MaxJobWorkers: 3, // config.TokenExpiration: 30, - config.CfgExpiration: 5, + common.CfgExpiration: 5, // config.JobLogDir: "/var/log/jobs", // config.UseCompressedJS: true, - config.AdminInitialPassword: "password", + common.AdminInitialPassword: "password", } func TestMain(t *testing.T) { diff --git a/src/ui/config/config.go b/src/ui/config/config.go index da9095e73..f500f9966 100644 --- a/src/ui/config/config.go +++ b/src/ui/config/config.go @@ -16,20 +16,28 @@ package config import ( - "encoding/json" + "fmt" "os" "strings" + "github.com/vmware/harbor/src/adminserver/client" + "github.com/vmware/harbor/src/adminserver/client/auth" + "github.com/vmware/harbor/src/common" comcfg "github.com/vmware/harbor/src/common/config" "github.com/vmware/harbor/src/common/models" "github.com/vmware/harbor/src/common/utils/log" ) -const defaultKeyPath string = "/etc/ui/key" +const ( + defaultKeyPath string = "/etc/ui/key" + secretCookieName string = "secret" +) var ( - mg *comcfg.Manager - keyProvider comcfg.KeyProvider + // AdminserverClient is a client for adminserver + AdminserverClient client.Client + mg *comcfg.Manager + keyProvider comcfg.KeyProvider ) // Init configurations @@ -41,14 +49,17 @@ func Init() error { if len(adminServerURL) == 0 { adminServerURL = "http://adminserver" } - log.Debugf("admin server URL: %s", adminServerURL) - mg = comcfg.NewManager(adminServerURL, UISecret(), true) - if err := mg.Init(); err != nil { - return err + log.Infof("initializing client for adminserver %s ...", adminServerURL) + authorizer := auth.NewSecretAuthorizer(secretCookieName, UISecret()) + AdminserverClient = client.NewClient(adminServerURL, authorizer) + if err := AdminserverClient.Ping(); err != nil { + return fmt.Errorf("failed to ping adminserver: %v", err) } - if _, err := mg.Load(); err != nil { + mg = comcfg.NewManager(AdminserverClient, true) + + if err := Load(); err != nil { return err } @@ -78,26 +89,12 @@ func Reset() error { // Upload uploads all system configutations to admin server func Upload(cfg map[string]interface{}) error { - b, err := json.Marshal(cfg) - if err != nil { - return err - } - return mg.Upload(b) + return mg.Upload(cfg) } // GetSystemCfg returns the system configurations func GetSystemCfg() (map[string]interface{}, error) { - raw, err := mg.Loader.Load() - if err != nil { - return nil, err - } - - c, err := mg.Parser.Parse(raw) - if err != nil { - return nil, err - } - - return c, nil + return mg.Load() } // AuthMode ... @@ -106,7 +103,7 @@ func AuthMode() (string, error) { if err != nil { return "", err } - return cfg[comcfg.AUTHMode].(string), nil + return cfg[common.AUTHMode].(string), nil } // LDAP returns the setting of ldap server @@ -117,14 +114,14 @@ func LDAP() (*models.LDAP, error) { } ldap := &models.LDAP{} - ldap.URL = cfg[comcfg.LDAPURL].(string) - ldap.SearchDN = cfg[comcfg.LDAPSearchDN].(string) - ldap.SearchPassword = cfg[comcfg.LDAPSearchPwd].(string) - ldap.BaseDN = cfg[comcfg.LDAPBaseDN].(string) - ldap.UID = cfg[comcfg.LDAPUID].(string) - ldap.Filter = cfg[comcfg.LDAPFilter].(string) - ldap.Scope = int(cfg[comcfg.LDAPScope].(float64)) - ldap.Timeout = int(cfg[comcfg.LDAPTimeout].(float64)) + ldap.URL = cfg[common.LDAPURL].(string) + ldap.SearchDN = cfg[common.LDAPSearchDN].(string) + ldap.SearchPassword = cfg[common.LDAPSearchPwd].(string) + ldap.BaseDN = cfg[common.LDAPBaseDN].(string) + ldap.UID = cfg[common.LDAPUID].(string) + ldap.Filter = cfg[common.LDAPFilter].(string) + ldap.Scope = int(cfg[common.LDAPScope].(float64)) + ldap.Timeout = int(cfg[common.LDAPTimeout].(float64)) return ldap, nil } @@ -135,7 +132,7 @@ func TokenExpiration() (int, error) { if err != nil { return 0, err } - return int(cfg[comcfg.TokenExpiration].(float64)), nil + return int(cfg[common.TokenExpiration].(float64)), nil } // ExtEndpoint returns the external URL of Harbor: protocol://host:port @@ -144,7 +141,7 @@ func ExtEndpoint() (string, error) { if err != nil { return "", err } - return cfg[comcfg.ExtEndpoint].(string), nil + return cfg[common.ExtEndpoint].(string), nil } // ExtURL returns the external URL: host:port @@ -171,7 +168,7 @@ func SelfRegistration() (bool, error) { if err != nil { return false, err } - return cfg[comcfg.SelfRegistration].(bool), nil + return cfg[common.SelfRegistration].(bool), nil } // RegistryURL ... @@ -180,7 +177,7 @@ func RegistryURL() (string, error) { if err != nil { return "", err } - return cfg[comcfg.RegistryURL].(string), nil + return cfg[common.RegistryURL].(string), nil } // InternalJobServiceURL returns jobservice URL for internal communication between Harbor containers @@ -205,7 +202,7 @@ func InitialAdminPassword() (string, error) { if err != nil { return "", err } - return cfg[comcfg.AdminInitialPassword].(string), nil + return cfg[common.AdminInitialPassword].(string), nil } // OnlyAdminCreateProject returns the flag to restrict that only sys admin can create project @@ -214,7 +211,7 @@ func OnlyAdminCreateProject() (bool, error) { if err != nil { return true, err } - return cfg[comcfg.ProjectCreationRestriction].(string) == comcfg.ProCrtRestrAdmOnly, nil + return cfg[common.ProjectCreationRestriction].(string) == common.ProCrtRestrAdmOnly, nil } // VerifyRemoteCert returns bool value. @@ -223,7 +220,7 @@ func VerifyRemoteCert() (bool, error) { if err != nil { return true, err } - return cfg[comcfg.VerifyRemoteCert].(bool), nil + return cfg[common.VerifyRemoteCert].(bool), nil } // Email returns email server settings @@ -234,13 +231,13 @@ func Email() (*models.Email, error) { } email := &models.Email{} - email.Host = cfg[comcfg.EmailHost].(string) - email.Port = int(cfg[comcfg.EmailPort].(float64)) - email.Username = cfg[comcfg.EmailUsername].(string) - email.Password = cfg[comcfg.EmailPassword].(string) - email.SSL = cfg[comcfg.EmailSSL].(bool) - email.From = cfg[comcfg.EmailFrom].(string) - email.Identity = cfg[comcfg.EmailIdentity].(string) + email.Host = cfg[common.EmailHost].(string) + email.Port = int(cfg[common.EmailPort].(float64)) + email.Username = cfg[common.EmailUsername].(string) + email.Password = cfg[common.EmailPassword].(string) + email.SSL = cfg[common.EmailSSL].(bool) + email.From = cfg[common.EmailFrom].(string) + email.Identity = cfg[common.EmailIdentity].(string) return email, nil } @@ -252,16 +249,16 @@ func Database() (*models.Database, error) { return nil, err } database := &models.Database{} - database.Type = cfg[comcfg.DatabaseType].(string) + database.Type = cfg[common.DatabaseType].(string) mysql := &models.MySQL{} - mysql.Host = cfg[comcfg.MySQLHost].(string) - mysql.Port = int(cfg[comcfg.MySQLPort].(float64)) - mysql.Username = cfg[comcfg.MySQLUsername].(string) - mysql.Password = cfg[comcfg.MySQLPassword].(string) - mysql.Database = cfg[comcfg.MySQLDatabase].(string) + mysql.Host = cfg[common.MySQLHost].(string) + mysql.Port = int(cfg[common.MySQLPort].(float64)) + mysql.Username = cfg[common.MySQLUsername].(string) + mysql.Password = cfg[common.MySQLPassword].(string) + mysql.Database = cfg[common.MySQLDatabase].(string) database.MySQL = mysql sqlite := &models.SQLite{} - sqlite.File = cfg[comcfg.SQLiteFile].(string) + sqlite.File = cfg[common.SQLiteFile].(string) database.SQLite = sqlite return database, nil @@ -286,7 +283,7 @@ func WithNotary() bool { log.Errorf("Failed to get configuration, will return WithNotary == false") return false } - return cfg[comcfg.WithNotary].(bool) + return cfg[common.WithNotary].(bool) } // AdmiralEndpoint returns the URL of admiral, if Harbor is not deployed with admiral it should return an empty string. @@ -297,10 +294,10 @@ func AdmiralEndpoint() string { return "" } - if e, ok := cfg[comcfg.AdmiralEndpoint].(string); !ok || e == "NA" { - cfg[comcfg.AdmiralEndpoint] = "" + if e, ok := cfg[common.AdmiralEndpoint].(string); !ok || e == "NA" { + cfg[common.AdmiralEndpoint] = "" } - return cfg[comcfg.AdmiralEndpoint].(string) + return cfg[common.AdmiralEndpoint].(string) } // WithAdmiral returns a bool to indicate if Harbor's deployed with admiral. diff --git a/src/ui/controllers/base.go b/src/ui/controllers/base.go index a5b41ac57..4e8ddc9e9 100644 --- a/src/ui/controllers/base.go +++ b/src/ui/controllers/base.go @@ -9,7 +9,7 @@ import ( "github.com/astaxie/beego" "github.com/beego/i18n" - "github.com/vmware/harbor/src/common/config" + "github.com/vmware/harbor/src/common" "github.com/vmware/harbor/src/common/dao" "github.com/vmware/harbor/src/common/models" "github.com/vmware/harbor/src/common/utils" @@ -113,7 +113,7 @@ func (cc *CommonController) SendEmail() { message := new(bytes.Buffer) - harborURL := config.ExtEndpoint + harborURL := common.ExtEndpoint if harborURL == "" { harborURL = "localhost" } diff --git a/src/ui/router.go b/src/ui/router.go index 501956f3a..3ad7008a9 100644 --- a/src/ui/router.go +++ b/src/ui/router.go @@ -62,7 +62,7 @@ func initRouters() { //API: beego.Router("/api/search", &api.SearchAPI{}) beego.Router("/api/projects/:pid([0-9]+)/members/?:mid", &api.ProjectMemberAPI{}) - beego.Router("/api/projects/", &api.ProjectAPI{}, "get:List;post:Post") + beego.Router("/api/projects/", &api.ProjectAPI{}, "get:List;post:Post;head:Head") beego.Router("/api/projects/:id", &api.ProjectAPI{}) beego.Router("/api/projects/:id/publicity", &api.ProjectAPI{}, "put:ToggleProjectPublic") beego.Router("/api/statistics", &api.StatisticAPI{}) diff --git a/src/ui_ng/src/app/account/password/password-setting.component.html b/src/ui_ng/src/app/account/password/password-setting.component.html index 8a48f6d47..0ecc01d30 100644 --- a/src/ui_ng/src/app/account/password/password-setting.component.html +++ b/src/ui_ng/src/app/account/password/password-setting.component.html @@ -7,11 +7,11 @@
- +
-
Management - Registry + Registry
diff --git a/src/ui_ng/src/app/base/navigator/navigator.component.ts b/src/ui_ng/src/app/base/navigator/navigator.component.ts index dde259aa8..2a12419ce 100644 --- a/src/ui_ng/src/app/base/navigator/navigator.component.ts +++ b/src/ui_ng/src/app/base/navigator/navigator.component.ts @@ -9,9 +9,11 @@ import { SessionUser } from '../../shared/session-user'; import { SessionService } from '../../shared/session.service'; import { CookieService } from 'angular2-cookie/core'; -import { supportedLangs, enLang, languageNames, CommonRoutes } from '../../shared/shared.const'; - +import { supportedLangs, enLang, languageNames, CommonRoutes, AlertType } from '../../shared/shared.const'; +import { errorHandler } from '../../shared/shared.utils'; import { AppConfigService } from '../../app-config.service'; +import { SearchTriggerService } from '../global-search/search-trigger.service'; +import { MessageService } from '../../global-message/message.service'; @Component({ selector: 'navigator', @@ -31,7 +33,9 @@ export class NavigatorComponent implements OnInit { private router: Router, private translate: TranslateService, private cookie: CookieService, - private appConfigService: AppConfigService) { } + private appConfigService: AppConfigService, + private msgService: MessageService, + private searchTrigger: SearchTriggerService) { } ngOnInit(): void { this.selectedLang = this.translate.currentLang; @@ -98,8 +102,10 @@ export class NavigatorComponent implements OnInit { this.router.navigate([CommonRoutes.EMBEDDED_SIGN_IN]); }) .catch(error => { - console.error("Log out with error: ", error); + this.msgService.announceMessage(error.status | 500, errorHandler(error), AlertType.WARNING); }); + //Confirm search result panel is close + this.searchTrigger.closeSearch(false); } //Switch languages @@ -124,5 +130,12 @@ export class NavigatorComponent implements OnInit { //Naviagte to signin page this.router.navigate([CommonRoutes.HARBOR_ROOT]); } + + //Confirm search result panel is close + this.searchTrigger.closeSearch(false); + } + + registryAction(): void { + this.searchTrigger.closeSearch(false); } } \ No newline at end of file diff --git a/src/ui_ng/src/app/common.css b/src/ui_ng/src/app/common.css index ee49bf9fd..f657dee85 100644 --- a/src/ui_ng/src/app/common.css +++ b/src/ui_ng/src/app/common.css @@ -5,4 +5,12 @@ .form-group-label-override { font-size: 14px; font-weight: 400; +} + +.sub-label-for-input { + position: absolute; + top: 26px; + font-size: 10px; + font-weight: 400; + line-height: 12px; } \ No newline at end of file diff --git a/src/ui_ng/src/app/project/create-project/create-project.component.html b/src/ui_ng/src/app/project/create-project/create-project.component.html index 5f78a1b16..725f538a6 100644 --- a/src/ui_ng/src/app/project/create-project/create-project.component.html +++ b/src/ui_ng/src/app/project/create-project/create-project.component.html @@ -1,23 +1,26 @@ - +