mirror of
https://github.com/goharbor/harbor
synced 2024-09-21 12:17:53 +00:00
Merge branch 'master' of https://github.com/steven-zou/harbor into fork_master
This commit is contained in:
commit
410908d863
21
.gitmessage
Normal file
21
.gitmessage
Normal file
|
@ -0,0 +1,21 @@
|
||||||
|
SUBJECT HERE
|
||||||
|
#
|
||||||
|
# Please provide your commit's subject above this line.
|
||||||
|
#
|
||||||
|
# Guideline (http://chris.beams.io/posts/git-commit/)
|
||||||
|
#
|
||||||
|
# 1. Separate subject from body with a blank line
|
||||||
|
# 2. Limit the subject line to 50 characters
|
||||||
|
# 3. Capitalize the first letter subject line
|
||||||
|
# 4. Do not end the subject line with a period
|
||||||
|
# 5. Use the imperative mood in the subject line
|
||||||
|
# 6. Wrap the body at 72 characters
|
||||||
|
# 7. Use the body to explain what and why vs. how
|
||||||
|
#
|
||||||
|
# Description of your commit should go below. Make sure to leave
|
||||||
|
# one empty line after your description.
|
||||||
|
#
|
||||||
|
#
|
||||||
|
BODY LINE1
|
||||||
|
BODY LINE2
|
||||||
|
|
|
@ -96,7 +96,6 @@ script:
|
||||||
- go test -race -i ./src/ui ./src/adminserver ./src/jobservice
|
- go test -race -i ./src/ui ./src/adminserver ./src/jobservice
|
||||||
- sudo -E env "PATH=$PATH" ./tests/coverage4gotest.sh
|
- sudo -E env "PATH=$PATH" ./tests/coverage4gotest.sh
|
||||||
- goveralls -coverprofile=profile.cov -service=travis-ci
|
- goveralls -coverprofile=profile.cov -service=travis-ci
|
||||||
|
|
||||||
- docker-compose -f make/docker-compose.test.yml down
|
- docker-compose -f make/docker-compose.test.yml down
|
||||||
- sudo rm -rf /data/config/*
|
- sudo rm -rf /data/config/*
|
||||||
- ls /data/cert
|
- ls /data/cert
|
||||||
|
|
6
Makefile
6
Makefile
|
@ -86,7 +86,7 @@ NGINXVERSION=1.11.13
|
||||||
PHOTONVERSION=1.0
|
PHOTONVERSION=1.0
|
||||||
NOTARYVERSION=server-0.5.0
|
NOTARYVERSION=server-0.5.0
|
||||||
NOTARYSIGNERVERSION=signer-0.5.0
|
NOTARYSIGNERVERSION=signer-0.5.0
|
||||||
MARIADBVERSION=mariadb-10.1.10
|
MARIADBVERSION=10.2.8
|
||||||
HTTPPROXY=
|
HTTPPROXY=
|
||||||
REBUILDCLARITYFLAG=false
|
REBUILDCLARITYFLAG=false
|
||||||
NEWCLARITYVERSION=
|
NEWCLARITYVERSION=
|
||||||
|
@ -241,7 +241,7 @@ DOCKERCOMPOSE_LIST=-f $(DOCKERCOMPOSEFILEPATH)/$(DOCKERCOMPOSEFILENAME)
|
||||||
|
|
||||||
ifeq ($(NOTARYFLAG), true)
|
ifeq ($(NOTARYFLAG), true)
|
||||||
DOCKERSAVE_PARA+= vmware/notary-photon:$(NOTARYVERSION) vmware/notary-photon:$(NOTARYSIGNERVERSION) \
|
DOCKERSAVE_PARA+= vmware/notary-photon:$(NOTARYVERSION) vmware/notary-photon:$(NOTARYSIGNERVERSION) \
|
||||||
vmware/harbor-notary-db:$(MARIADBVERSION)
|
vmware/mariadb-photon:$(MARIADBVERSION)
|
||||||
PACKAGE_OFFLINE_PARA+= $(HARBORPKG)/$(DOCKERCOMPOSENOTARYFILENAME)
|
PACKAGE_OFFLINE_PARA+= $(HARBORPKG)/$(DOCKERCOMPOSENOTARYFILENAME)
|
||||||
PACKAGE_ONLINE_PARA+= $(HARBORPKG)/$(DOCKERCOMPOSENOTARYFILENAME)
|
PACKAGE_ONLINE_PARA+= $(HARBORPKG)/$(DOCKERCOMPOSENOTARYFILENAME)
|
||||||
DOCKERCOMPOSE_LIST+= -f $(DOCKERCOMPOSEFILEPATH)/$(DOCKERCOMPOSENOTARYFILENAME)
|
DOCKERCOMPOSE_LIST+= -f $(DOCKERCOMPOSEFILEPATH)/$(DOCKERCOMPOSENOTARYFILENAME)
|
||||||
|
@ -371,7 +371,7 @@ package_offline: compile build modify_sourcefiles modify_composefile
|
||||||
echo "pulling notary and harbor-notary-db..."; \
|
echo "pulling notary and harbor-notary-db..."; \
|
||||||
$(DOCKERPULL) vmware/notary-photon:$(NOTARYVERSION); \
|
$(DOCKERPULL) vmware/notary-photon:$(NOTARYVERSION); \
|
||||||
$(DOCKERPULL) vmware/notary-photon:$(NOTARYSIGNERVERSION); \
|
$(DOCKERPULL) vmware/notary-photon:$(NOTARYSIGNERVERSION); \
|
||||||
$(DOCKERPULL) vmware/harbor-notary-db:$(MARIADBVERSION); \
|
$(DOCKERPULL) vmware/mariadb-photon:$(MARIADBVERSION); \
|
||||||
fi
|
fi
|
||||||
@if [ "$(CLAIRFLAG)" = "true" ] ; then \
|
@if [ "$(CLAIRFLAG)" = "true" ] ; then \
|
||||||
echo "pulling claiy and postgres..."; \
|
echo "pulling claiy and postgres..."; \
|
||||||
|
|
|
@ -34,11 +34,11 @@ Refer to **[User Guide](docs/user_guide.md)** for more details on how to use Har
|
||||||
|
|
||||||
### Community
|
### Community
|
||||||
**Slack:** Join Harbor's community for discussion and ask questions: [VMware {code}](https://code.vmware.com/join/), Channel: #harbor.
|
**Slack:** Join Harbor's community for discussion and ask questions: [VMware {code}](https://code.vmware.com/join/), Channel: #harbor.
|
||||||
**Email:** harbor@ vmware.com .
|
**Email:** harbor@vmware.com .
|
||||||
More info on [partners and users](partners.md).
|
More info on [partners and users](partners.md).
|
||||||
|
|
||||||
### Contribution
|
### Contribution
|
||||||
We welcome contributions from the community. If you wish to contribute code and you have not signed our contributor license agreement (CLA), our bot will update the issue when you open a pull request. For any questions about the CLA process, please refer to our [FAQ](https://cla.vmware.com/faq). Contact us for any questions: harbor @vmware.com .
|
We welcome contributions from the community. If you wish to contribute code and you have not signed our contributor license agreement (CLA), our bot will update the issue when you open a pull request. For any questions about the CLA process, please refer to our [FAQ](https://cla.vmware.com/faq). Contact us for any questions: harbor@vmware.com .
|
||||||
|
|
||||||
### Demos
|
### Demos
|
||||||
* ![play](docs/img/video.png) **Content Trust** ( [youtube](https://www.youtube.com/watch?v=pPklSTJZY2E) , [Tencent Video](https://v.qq.com/x/page/n0553fzzrnf.html) )
|
* ![play](docs/img/video.png) **Content Trust** ( [youtube](https://www.youtube.com/watch?v=pPklSTJZY2E) , [Tencent Video](https://v.qq.com/x/page/n0553fzzrnf.html) )
|
||||||
|
@ -56,7 +56,7 @@ This project uses open source components which have additional licensing terms.
|
||||||
* MySQL 5.6: [docker image](https://hub.docker.com/_/mysql/), [license](https://github.com/docker-library/mysql/blob/master/LICENSE)
|
* MySQL 5.6: [docker image](https://hub.docker.com/_/mysql/), [license](https://github.com/docker-library/mysql/blob/master/LICENSE)
|
||||||
|
|
||||||
### Commercial Support
|
### Commercial Support
|
||||||
If you need commercial support of Harbor, please contact us for more information: harbor@ vmware.com .
|
If you need commercial support of Harbor, please contact us for more information: harbor@vmware.com .
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
|
@ -4,7 +4,7 @@ import json
|
||||||
import logging
|
import logging
|
||||||
import requests
|
import requests
|
||||||
|
|
||||||
logging.basicConfig(level=logging.WARNING)
|
logger = logging.getLogger(__name__)
|
||||||
|
|
||||||
class HarborClient(object):
|
class HarborClient(object):
|
||||||
def __init__(self, host, user, password, protocol="http"):
|
def __init__(self, host, user, password, protocol="http"):
|
||||||
|
@ -23,15 +23,15 @@ class HarborClient(object):
|
||||||
if login_data.status_code == 200:
|
if login_data.status_code == 200:
|
||||||
session_id = login_data.cookies.get('beegosessionID')
|
session_id = login_data.cookies.get('beegosessionID')
|
||||||
self.session_id = session_id
|
self.session_id = session_id
|
||||||
logging.debug("Successfully login, session id: {}".format(
|
logger.debug("Successfully login, session id: {}".format(
|
||||||
session_id))
|
session_id))
|
||||||
else:
|
else:
|
||||||
logging.error("Fail to login, please try again")
|
logger.error("Fail to login, please try again")
|
||||||
|
|
||||||
def logout(self):
|
def logout(self):
|
||||||
requests.get('%s://%s/log_out' % (self.protocol, self.host),
|
requests.get('%s://%s/log_out' % (self.protocol, self.host),
|
||||||
cookies={'beegosessionID': self.session_id}, verify=False)
|
cookies={'beegosessionID': self.session_id}, verify=False)
|
||||||
logging.debug("Successfully logout")
|
logger.debug("Successfully logout")
|
||||||
|
|
||||||
# GET /search
|
# GET /search
|
||||||
def search(self, query_string):
|
def search(self, query_string):
|
||||||
|
@ -42,9 +42,9 @@ class HarborClient(object):
|
||||||
cookies={'beegosessionID': self.session_id}, verify=False)
|
cookies={'beegosessionID': self.session_id}, verify=False)
|
||||||
if response.status_code == 200:
|
if response.status_code == 200:
|
||||||
result = response.json()
|
result = response.json()
|
||||||
logging.debug("Successfully get search result: {}".format(result))
|
logger.debug("Successfully get search result: {}".format(result))
|
||||||
else:
|
else:
|
||||||
logging.error("Fail to get search result")
|
logger.error("Fail to get search result")
|
||||||
return result
|
return result
|
||||||
|
|
||||||
# GET /projects
|
# GET /projects
|
||||||
|
@ -55,10 +55,10 @@ class HarborClient(object):
|
||||||
cookies={'beegosessionID': self.session_id}, verify=False)
|
cookies={'beegosessionID': self.session_id}, verify=False)
|
||||||
if response.status_code == 200:
|
if response.status_code == 200:
|
||||||
result = response.json()
|
result = response.json()
|
||||||
logging.debug("Successfully get projects result: {}".format(
|
logger.debug("Successfully get projects result: {}".format(
|
||||||
result))
|
result))
|
||||||
else:
|
else:
|
||||||
logging.error("Fail to get projects result")
|
logger.error("Fail to get projects result")
|
||||||
return result
|
return result
|
||||||
|
|
||||||
# HEAD /projects
|
# HEAD /projects
|
||||||
|
@ -70,14 +70,14 @@ class HarborClient(object):
|
||||||
cookies={'beegosessionID': self.session_id}, verify=False)
|
cookies={'beegosessionID': self.session_id}, verify=False)
|
||||||
if response.status_code == 200:
|
if response.status_code == 200:
|
||||||
result = True
|
result = True
|
||||||
logging.debug(
|
logger.debug(
|
||||||
"Successfully check project exist, result: {}".format(result))
|
"Successfully check project exist, result: {}".format(result))
|
||||||
elif response.status_code == 404:
|
elif response.status_code == 404:
|
||||||
result = False
|
result = False
|
||||||
logging.debug(
|
logger.debug(
|
||||||
"Successfully check project exist, result: {}".format(result))
|
"Successfully check project exist, result: {}".format(result))
|
||||||
else:
|
else:
|
||||||
logging.error("Fail to check project exist")
|
logger.error("Fail to check project exist")
|
||||||
return result
|
return result
|
||||||
|
|
||||||
# POST /projects
|
# POST /projects
|
||||||
|
@ -91,11 +91,11 @@ class HarborClient(object):
|
||||||
data=request_body, verify=False)
|
data=request_body, verify=False)
|
||||||
if response.status_code == 201:
|
if response.status_code == 201:
|
||||||
result = True
|
result = True
|
||||||
logging.debug(
|
logger.debug(
|
||||||
"Successfully create project with project name: {}".format(
|
"Successfully create project with project name: {}".format(
|
||||||
project_name))
|
project_name))
|
||||||
else:
|
else:
|
||||||
logging.error(
|
logger.error(
|
||||||
"Fail to create project with project name: {}, response code: {}".format(
|
"Fail to create project with project name: {}, response code: {}".format(
|
||||||
project_name, response.status_code))
|
project_name, response.status_code))
|
||||||
return result
|
return result
|
||||||
|
@ -108,11 +108,11 @@ class HarborClient(object):
|
||||||
cookies={'beegosessionID': self.session_id}, verify=False)
|
cookies={'beegosessionID': self.session_id}, verify=False)
|
||||||
if response.status_code == 200:
|
if response.status_code == 200:
|
||||||
result = response.json()
|
result = response.json()
|
||||||
logging.debug(
|
logger.debug(
|
||||||
"Successfully create project with project id: {}".format(
|
"Successfully create project with project id: {}".format(
|
||||||
project_id))
|
project_id))
|
||||||
else:
|
else:
|
||||||
logging.error(
|
logger.error(
|
||||||
"Fail to create project with project id: {}, response code: {}".format(
|
"Fail to create project with project id: {}, response code: {}".format(
|
||||||
project_id, response.status_code))
|
project_id, response.status_code))
|
||||||
return result
|
return result
|
||||||
|
@ -128,11 +128,11 @@ class HarborClient(object):
|
||||||
data=request_body, verify=False)
|
data=request_body, verify=False)
|
||||||
if response.status_code == 200:
|
if response.status_code == 200:
|
||||||
result = True
|
result = True
|
||||||
logging.debug(
|
logger.debug(
|
||||||
"Successfully add project member with project id: {}".format(
|
"Successfully add project member with project id: {}".format(
|
||||||
project_id))
|
project_id))
|
||||||
else:
|
else:
|
||||||
logging.error(
|
logger.error(
|
||||||
"Fail to add project member with project id: {}, response code: {}".format(
|
"Fail to add project member with project id: {}, response code: {}".format(
|
||||||
project_id, response.status_code))
|
project_id, response.status_code))
|
||||||
return result
|
return result
|
||||||
|
@ -146,10 +146,10 @@ class HarborClient(object):
|
||||||
cookies={'beegosessionID': self.session_id}, verify=False)
|
cookies={'beegosessionID': self.session_id}, verify=False)
|
||||||
if response.status_code == 200:
|
if response.status_code == 200:
|
||||||
result = True
|
result = True
|
||||||
logging.debug("Successfully delete member with id: {}".format(
|
logger.debug("Successfully delete member with id: {}".format(
|
||||||
user_id))
|
user_id))
|
||||||
else:
|
else:
|
||||||
logging.error("Fail to delete member with id: {}, response code: {}"
|
logger.error("Fail to delete member with id: {}, response code: {}"
|
||||||
.format(user_id, response.status_code))
|
.format(user_id, response.status_code))
|
||||||
return result
|
return result
|
||||||
|
|
||||||
|
@ -164,11 +164,11 @@ class HarborClient(object):
|
||||||
data=request_body, verify=False)
|
data=request_body, verify=False)
|
||||||
if response.status_code == 200:
|
if response.status_code == 200:
|
||||||
result = True
|
result = True
|
||||||
logging.debug(
|
logger.debug(
|
||||||
"Success to set project id: {} with publicity: {}".format(
|
"Success to set project id: {} with publicity: {}".format(
|
||||||
project_id, is_public))
|
project_id, is_public))
|
||||||
else:
|
else:
|
||||||
logging.error(
|
logger.error(
|
||||||
"Fail to set publicity to project id: {} with status code: {}".format(
|
"Fail to set publicity to project id: {} with status code: {}".format(
|
||||||
project_id, response.status_code))
|
project_id, response.status_code))
|
||||||
return result
|
return result
|
||||||
|
@ -181,9 +181,9 @@ class HarborClient(object):
|
||||||
cookies={'beegosessionID': self.session_id}, verify=False)
|
cookies={'beegosessionID': self.session_id}, verify=False)
|
||||||
if response.status_code == 200:
|
if response.status_code == 200:
|
||||||
result = response.json()
|
result = response.json()
|
||||||
logging.debug("Successfully get statistics: {}".format(result))
|
logger.debug("Successfully get statistics: {}".format(result))
|
||||||
else:
|
else:
|
||||||
logging.error("Fail to get statistics result with status code: {}"
|
logger.error("Fail to get statistics result with status code: {}"
|
||||||
.format(response.status_code))
|
.format(response.status_code))
|
||||||
return result
|
return result
|
||||||
|
|
||||||
|
@ -196,9 +196,9 @@ class HarborClient(object):
|
||||||
cookies={'beegosessionID': self.session_id}, verify=False)
|
cookies={'beegosessionID': self.session_id}, verify=False)
|
||||||
if response.status_code == 200:
|
if response.status_code == 200:
|
||||||
result = response.json()
|
result = response.json()
|
||||||
logging.debug("Successfully get users result: {}".format(result))
|
logger.debug("Successfully get users result: {}".format(result))
|
||||||
else:
|
else:
|
||||||
logging.error("Fail to get users result with status code: {}"
|
logger.error("Fail to get users result with status code: {}"
|
||||||
.format(response.status_code))
|
.format(response.status_code))
|
||||||
return result
|
return result
|
||||||
|
|
||||||
|
@ -210,9 +210,9 @@ class HarborClient(object):
|
||||||
cookies={'beegosessionID': self.session_id}, verify=False)
|
cookies={'beegosessionID': self.session_id}, verify=False)
|
||||||
if response.status_code == 200:
|
if response.status_code == 200:
|
||||||
result = response.json()
|
result = response.json()
|
||||||
logging.debug("Successfully get users result: {}".format(result))
|
logger.debug("Successfully get users result: {}".format(result))
|
||||||
else:
|
else:
|
||||||
logging.error("Fail to get users result with status code: {}"
|
logger.error("Fail to get users result with status code: {}"
|
||||||
.format(response.status_code))
|
.format(response.status_code))
|
||||||
return result
|
return result
|
||||||
|
|
||||||
|
@ -230,10 +230,10 @@ class HarborClient(object):
|
||||||
data=request_body, verify=False)
|
data=request_body, verify=False)
|
||||||
if response.status_code == 201:
|
if response.status_code == 201:
|
||||||
result = True
|
result = True
|
||||||
logging.debug("Successfully create user with username: {}".format(
|
logger.debug("Successfully create user with username: {}".format(
|
||||||
username))
|
username))
|
||||||
else:
|
else:
|
||||||
logging.error(
|
logger.error(
|
||||||
"Fail to create user with username: {}, response code: {}".format(
|
"Fail to create user with username: {}, response code: {}".format(
|
||||||
username, response.status_code))
|
username, response.status_code))
|
||||||
return result
|
return result
|
||||||
|
@ -252,11 +252,11 @@ class HarborClient(object):
|
||||||
data=request_body, verify=False)
|
data=request_body, verify=False)
|
||||||
if response.status_code == 200:
|
if response.status_code == 200:
|
||||||
result = True
|
result = True
|
||||||
logging.debug(
|
logger.debug(
|
||||||
"Successfully update user profile with user id: {}".format(
|
"Successfully update user profile with user id: {}".format(
|
||||||
user_id))
|
user_id))
|
||||||
else:
|
else:
|
||||||
logging.error(
|
logger.error(
|
||||||
"Fail to update user profile with user id: {}, response code: {}".format(
|
"Fail to update user profile with user id: {}, response code: {}".format(
|
||||||
user_id, response.status_code))
|
user_id, response.status_code))
|
||||||
return result
|
return result
|
||||||
|
@ -270,10 +270,10 @@ class HarborClient(object):
|
||||||
cookies={'beegosessionID': self.session_id}, verify=False)
|
cookies={'beegosessionID': self.session_id}, verify=False)
|
||||||
if response.status_code == 200:
|
if response.status_code == 200:
|
||||||
result = True
|
result = True
|
||||||
logging.debug("Successfully delete user with id: {}".format(
|
logger.debug("Successfully delete user with id: {}".format(
|
||||||
user_id))
|
user_id))
|
||||||
else:
|
else:
|
||||||
logging.error("Fail to delete user with id: {}, response code: {}"
|
logger.error("Fail to delete user with id: {}, response code: {}"
|
||||||
.format(user_id, response.status_code))
|
.format(user_id, response.status_code))
|
||||||
return result
|
return result
|
||||||
|
|
||||||
|
@ -289,10 +289,10 @@ class HarborClient(object):
|
||||||
data=request_body, verify=False)
|
data=request_body, verify=False)
|
||||||
if response.status_code == 200:
|
if response.status_code == 200:
|
||||||
result = True
|
result = True
|
||||||
logging.debug(
|
logger.debug(
|
||||||
"Successfully change password for user id: {}".format(user_id))
|
"Successfully change password for user id: {}".format(user_id))
|
||||||
else:
|
else:
|
||||||
logging.error("Fail to change password for user id: {}".format(
|
logger.error("Fail to change password for user id: {}".format(
|
||||||
user_id))
|
user_id))
|
||||||
return result
|
return result
|
||||||
|
|
||||||
|
@ -308,11 +308,11 @@ class HarborClient(object):
|
||||||
data=request_body, verify=False)
|
data=request_body, verify=False)
|
||||||
if response.status_code == 200:
|
if response.status_code == 200:
|
||||||
result = True
|
result = True
|
||||||
logging.debug(
|
logger.debug(
|
||||||
"Successfully promote user as admin with user id: {}".format(
|
"Successfully promote user as admin with user id: {}".format(
|
||||||
user_id))
|
user_id))
|
||||||
else:
|
else:
|
||||||
logging.error(
|
logger.error(
|
||||||
"Fail to promote user as admin with user id: {}, response code: {}".format(
|
"Fail to promote user as admin with user id: {}, response code: {}".format(
|
||||||
user_id, response.status_code))
|
user_id, response.status_code))
|
||||||
return result
|
return result
|
||||||
|
@ -327,11 +327,11 @@ class HarborClient(object):
|
||||||
cookies={'beegosessionID': self.session_id}, verify=False)
|
cookies={'beegosessionID': self.session_id}, verify=False)
|
||||||
if response.status_code == 200:
|
if response.status_code == 200:
|
||||||
result = response.json()
|
result = response.json()
|
||||||
logging.debug(
|
logger.debug(
|
||||||
"Successfully get repositories with id: {}, result: {}".format(
|
"Successfully get repositories with id: {}, result: {}".format(
|
||||||
project_id, result))
|
project_id, result))
|
||||||
else:
|
else:
|
||||||
logging.error("Fail to get repositories result with id: {}, response code: {}".format(
|
logger.error("Fail to get repositories result with id: {}, response code: {}".format(
|
||||||
project_id, response.status_code))
|
project_id, response.status_code))
|
||||||
return result
|
return result
|
||||||
|
|
||||||
|
@ -344,10 +344,10 @@ class HarborClient(object):
|
||||||
cookies={'beegosessionID': self.session_id}, verify=False)
|
cookies={'beegosessionID': self.session_id}, verify=False)
|
||||||
if response.status_code == 200:
|
if response.status_code == 200:
|
||||||
result = True
|
result = True
|
||||||
logging.debug("Successfully delete a tag of repository: {}".format(
|
logger.debug("Successfully delete a tag of repository: {}".format(
|
||||||
repo_name))
|
repo_name))
|
||||||
else:
|
else:
|
||||||
logging.error("Fail to delete repository with name: {}, response code: {}".format(
|
logger.error("Fail to delete repository with name: {}, response code: {}".format(
|
||||||
repo_name, response.status_code))
|
repo_name, response.status_code))
|
||||||
return result
|
return result
|
||||||
|
|
||||||
|
@ -360,10 +360,10 @@ class HarborClient(object):
|
||||||
cookies={'beegosessionID': self.session_id}, verify=False)
|
cookies={'beegosessionID': self.session_id}, verify=False)
|
||||||
if response.status_code == 200:
|
if response.status_code == 200:
|
||||||
result = True
|
result = True
|
||||||
logging.debug("Successfully delete repository: {}".format(
|
logger.debug("Successfully delete repository: {}".format(
|
||||||
repo_name))
|
repo_name))
|
||||||
else:
|
else:
|
||||||
logging.error("Fail to delete repository with name: {}, response code: {}".format(
|
logger.error("Fail to delete repository with name: {}, response code: {}".format(
|
||||||
repo_name, response.status_code))
|
repo_name, response.status_code))
|
||||||
return result
|
return result
|
||||||
|
|
||||||
|
@ -376,11 +376,11 @@ class HarborClient(object):
|
||||||
cookies={'beegosessionID': self.session_id}, verify=False)
|
cookies={'beegosessionID': self.session_id}, verify=False)
|
||||||
if response.status_code == 200:
|
if response.status_code == 200:
|
||||||
result = response.json()
|
result = response.json()
|
||||||
logging.debug(
|
logger.debug(
|
||||||
"Successfully get tag with repo name: {}, result: {}".format(
|
"Successfully get tag with repo name: {}, result: {}".format(
|
||||||
repo_name, result))
|
repo_name, result))
|
||||||
else:
|
else:
|
||||||
logging.error("Fail to get tags with repo name: {}, response code: {}".format(
|
logger.error("Fail to get tags with repo name: {}, response code: {}".format(
|
||||||
repo_name, response.status_code))
|
repo_name, response.status_code))
|
||||||
return result
|
return result
|
||||||
|
|
||||||
|
@ -393,11 +393,11 @@ class HarborClient(object):
|
||||||
cookies={'beegosessionID': self.session_id}, verify=False)
|
cookies={'beegosessionID': self.session_id}, verify=False)
|
||||||
if response.status_code == 200:
|
if response.status_code == 200:
|
||||||
result = response.json()
|
result = response.json()
|
||||||
logging.debug(
|
logger.debug(
|
||||||
"Successfully get manifests with repo name: {}, tag: {}, result: {}".format(
|
"Successfully get manifests with repo name: {}, tag: {}, result: {}".format(
|
||||||
repo_name, tag, result))
|
repo_name, tag, result))
|
||||||
else:
|
else:
|
||||||
logging.error(
|
logger.error(
|
||||||
"Fail to get manifests with repo name: {}, tag: {}".format(
|
"Fail to get manifests with repo name: {}, tag: {}".format(
|
||||||
repo_name, tag))
|
repo_name, tag))
|
||||||
return result
|
return result
|
||||||
|
@ -412,11 +412,11 @@ class HarborClient(object):
|
||||||
cookies={'beegosessionID': self.session_id}, verify=False)
|
cookies={'beegosessionID': self.session_id}, verify=False)
|
||||||
if response.status_code == 200:
|
if response.status_code == 200:
|
||||||
result = response.json()
|
result = response.json()
|
||||||
logging.debug(
|
logger.debug(
|
||||||
"Successfully get top accessed repositories, result: {}".format(
|
"Successfully get top accessed repositories, result: {}".format(
|
||||||
result))
|
result))
|
||||||
else:
|
else:
|
||||||
logging.error("Fail to get top accessed repositories")
|
logger.error("Fail to get top accessed repositories")
|
||||||
return result
|
return result
|
||||||
|
|
||||||
# GET /logs
|
# GET /logs
|
||||||
|
@ -427,9 +427,9 @@ class HarborClient(object):
|
||||||
cookies={'beegosessionID': self.session_id}, verify=False)
|
cookies={'beegosessionID': self.session_id}, verify=False)
|
||||||
if response.status_code == 200:
|
if response.status_code == 200:
|
||||||
result = response.json()
|
result = response.json()
|
||||||
logging.debug("Successfully get logs")
|
logger.debug("Successfully get logs")
|
||||||
else:
|
else:
|
||||||
logging.error("Fail to get logs and response code: {}".format(
|
logger.error("Fail to get logs and response code: {}".format(
|
||||||
response.status_code))
|
response.status_code))
|
||||||
return result
|
return result
|
||||||
|
|
||||||
|
@ -441,10 +441,10 @@ class HarborClient(object):
|
||||||
cookies={'beegosessionID': self.session_id}, verify=False)
|
cookies={'beegosessionID': self.session_id}, verify=False)
|
||||||
if response.status_code == 200:
|
if response.status_code == 200:
|
||||||
result = response.json()
|
result = response.json()
|
||||||
logging.debug(
|
logger.debug(
|
||||||
"Successfully get systeminfo, result: {}".format(result))
|
"Successfully get systeminfo, result: {}".format(result))
|
||||||
else:
|
else:
|
||||||
logging.error("Fail to get systeminfo, response code: {}".format(response.status_code))
|
logger.error("Fail to get systeminfo, response code: {}".format(response.status_code))
|
||||||
return result
|
return result
|
||||||
|
|
||||||
# Get /configurations
|
# Get /configurations
|
||||||
|
@ -455,8 +455,8 @@ class HarborClient(object):
|
||||||
cookies={'beegosessionID': self.session_id}, verify=False)
|
cookies={'beegosessionID': self.session_id}, verify=False)
|
||||||
if response.status_code == 200:
|
if response.status_code == 200:
|
||||||
result = response.json()
|
result = response.json()
|
||||||
logging.debug(
|
logger.debug(
|
||||||
"Successfully get configurations, result: {}".format(result))
|
"Successfully get configurations, result: {}".format(result))
|
||||||
else:
|
else:
|
||||||
logging.error("Fail to get configurations, response code: {}".format(response.status_code))
|
logger.error("Fail to get configurations, response code: {}".format(response.status_code))
|
||||||
return result
|
return result
|
||||||
|
|
|
@ -2339,6 +2339,9 @@ definitions:
|
||||||
type: integer
|
type: integer
|
||||||
format: int
|
format: int
|
||||||
description: Reserved field.
|
description: Reserved field.
|
||||||
|
insecure:
|
||||||
|
type: boolean
|
||||||
|
description: Whether or not the certificate will be verified when Harbor tries to access the server.
|
||||||
creation_time:
|
creation_time:
|
||||||
type: string
|
type: string
|
||||||
description: The create time of the policy.
|
description: The create time of the policy.
|
||||||
|
@ -2360,6 +2363,9 @@ definitions:
|
||||||
password:
|
password:
|
||||||
type: string
|
type: string
|
||||||
description: The target server password.
|
description: The target server password.
|
||||||
|
insecure:
|
||||||
|
type: boolean
|
||||||
|
description: Whether or not the certificate will be verified when Harbor tries to access the server.
|
||||||
PingTarget:
|
PingTarget:
|
||||||
type: object
|
type: object
|
||||||
properties:
|
properties:
|
||||||
|
@ -2372,6 +2378,9 @@ definitions:
|
||||||
password:
|
password:
|
||||||
type: string
|
type: string
|
||||||
description: The target server password.
|
description: The target server password.
|
||||||
|
insecure:
|
||||||
|
type: boolean
|
||||||
|
description: Whether or not the certificate will be verified when Harbor tries to access the server.
|
||||||
PutTarget:
|
PutTarget:
|
||||||
type: object
|
type: object
|
||||||
properties:
|
properties:
|
||||||
|
@ -2387,6 +2396,9 @@ definitions:
|
||||||
password:
|
password:
|
||||||
type: string
|
type: string
|
||||||
description: The target server password.
|
description: The target server password.
|
||||||
|
insecure:
|
||||||
|
type: boolean
|
||||||
|
description: Whether or not the certificate will be verified when Harbor tries to access the server.
|
||||||
HasAdminRole:
|
HasAdminRole:
|
||||||
type: object
|
type: object
|
||||||
properties:
|
properties:
|
||||||
|
|
|
@ -1,8 +1,6 @@
|
||||||
FROM mysql:5.6.35
|
FROM vmware/mariadb-photon:10.2.8
|
||||||
|
|
||||||
WORKDIR /tmp
|
COPY registry.sql /docker-entrypoint-initdb.d/
|
||||||
|
COPY registry-flag.sh /docker-entrypoint-initdb.d/
|
||||||
|
COPY upgrade.sh /docker-entrypoint-updatedb.d/
|
||||||
|
|
||||||
ADD registry.sql r.sql
|
|
||||||
|
|
||||||
ADD docker-entrypoint.sh /entrypoint.sh
|
|
||||||
RUN chmod u+x /entrypoint.sh
|
|
||||||
|
|
|
@ -1,44 +0,0 @@
|
||||||
#!/bin/bash
|
|
||||||
set -e
|
|
||||||
|
|
||||||
if [ ! -d '/var/lib/mysql/mysql' -a "${1%_safe}" = 'mysqld' ]; then
|
|
||||||
if [ -z "$MYSQL_ROOT_PASSWORD" -a -z "$MYSQL_ALLOW_EMPTY_PASSWORD" ]; then
|
|
||||||
echo >&2 'error: database is uninitialized and MYSQL_ROOT_PASSWORD not set'
|
|
||||||
echo >&2 ' Did you forget to add -e MYSQL_ROOT_PASSWORD=... ? v2'
|
|
||||||
exit 1
|
|
||||||
fi
|
|
||||||
|
|
||||||
mysql_install_db --user=mysql --datadir=/var/lib/mysql
|
|
||||||
|
|
||||||
# These statements _must_ be on individual lines, and _must_ end with
|
|
||||||
# semicolons (no line breaks or comments are permitted).
|
|
||||||
# TODO proper SQL escaping on ALL the things D:
|
|
||||||
printf -v MYSQL_ROOT_PASSWORD "%q" ${MYSQL_ROOT_PASSWORD}
|
|
||||||
TEMP_FILE='/tmp/mysql-first-time.sql'
|
|
||||||
cat > "$TEMP_FILE" <<-EOSQL
|
|
||||||
DELETE FROM mysql.user ;
|
|
||||||
CREATE USER 'root'@'%' IDENTIFIED BY '${MYSQL_ROOT_PASSWORD}' ;
|
|
||||||
GRANT ALL ON *.* TO 'root'@'%' WITH GRANT OPTION ;
|
|
||||||
DROP DATABASE IF EXISTS test ;
|
|
||||||
EOSQL
|
|
||||||
|
|
||||||
if [ "$MYSQL_DATABASE" ]; then
|
|
||||||
echo "CREATE DATABASE IF NOT EXISTS $MYSQL_DATABASE ;" >> "$TEMP_FILE"
|
|
||||||
fi
|
|
||||||
|
|
||||||
if [ "$MYSQL_USER" -a "$MYSQL_PASSWORD" ]; then
|
|
||||||
echo "CREATE USER '$MYSQL_USER'@'%' IDENTIFIED BY '$MYSQL_PASSWORD' ;" >> "$TEMP_FILE"
|
|
||||||
|
|
||||||
if [ "$MYSQL_DATABASE" ]; then
|
|
||||||
echo "GRANT ALL ON $MYSQL_DATABASE.* TO '$MYSQL_USER'@'%' ;" >> "$TEMP_FILE"
|
|
||||||
fi
|
|
||||||
fi
|
|
||||||
|
|
||||||
echo 'FLUSH PRIVILEGES ;' >> "$TEMP_FILE"
|
|
||||||
cat /tmp/r.sql >> "$TEMP_FILE"
|
|
||||||
|
|
||||||
set -- "$@" --init-file="$TEMP_FILE"
|
|
||||||
fi
|
|
||||||
|
|
||||||
chown -R mysql:mysql /var/lib/mysql
|
|
||||||
exec "$@"
|
|
3
make/common/db/registry-flag.sh
Normal file
3
make/common/db/registry-flag.sh
Normal file
|
@ -0,0 +1,3 @@
|
||||||
|
#!/bin/sh
|
||||||
|
touch /var/lib/mysql/created_in_mariadb.flag
|
||||||
|
echo "dumped flag for MariaDB"
|
|
@ -163,6 +163,7 @@ create table replication_target (
|
||||||
1 means it's a regulart registry
|
1 means it's a regulart registry
|
||||||
*/
|
*/
|
||||||
target_type tinyint(1) NOT NULL DEFAULT 0,
|
target_type tinyint(1) NOT NULL DEFAULT 0,
|
||||||
|
insecure tinyint(1) NOT NULL DEFAULT 0,
|
||||||
creation_time timestamp default CURRENT_TIMESTAMP,
|
creation_time timestamp default CURRENT_TIMESTAMP,
|
||||||
update_time timestamp default CURRENT_TIMESTAMP on update CURRENT_TIMESTAMP,
|
update_time timestamp default CURRENT_TIMESTAMP on update CURRENT_TIMESTAMP,
|
||||||
PRIMARY KEY (id)
|
PRIMARY KEY (id)
|
||||||
|
|
|
@ -158,6 +158,7 @@ create table replication_target (
|
||||||
1 means it's a regulart registry
|
1 means it's a regulart registry
|
||||||
*/
|
*/
|
||||||
target_type tinyint(1) NOT NULL DEFAULT 0,
|
target_type tinyint(1) NOT NULL DEFAULT 0,
|
||||||
|
insecure tinyint(1) NOT NULL DEFAULT 0,
|
||||||
creation_time timestamp default CURRENT_TIMESTAMP,
|
creation_time timestamp default CURRENT_TIMESTAMP,
|
||||||
update_time timestamp default CURRENT_TIMESTAMP
|
update_time timestamp default CURRENT_TIMESTAMP
|
||||||
);
|
);
|
||||||
|
|
28
make/common/db/upgrade.sh
Executable file
28
make/common/db/upgrade.sh
Executable file
|
@ -0,0 +1,28 @@
|
||||||
|
#!/bin/bash
|
||||||
|
set +e
|
||||||
|
if [ ! -f /var/lib/mysql/created_in_mariadb.flag ]; then
|
||||||
|
echo "Maria DB flag not found, the DB was created in mysql image, running upgrade..."
|
||||||
|
mysqld >/dev/null 2>&1 &
|
||||||
|
pid="$!"
|
||||||
|
for i in {30..0}; do
|
||||||
|
mysqladmin -uroot -p$MYSQL_ROOT_PASSWORD processlist >/dev/null 2>&1
|
||||||
|
if [ $? = 0 ]; then
|
||||||
|
break
|
||||||
|
fi
|
||||||
|
echo 'Waiting for MySQL start...'
|
||||||
|
sleep 1
|
||||||
|
done
|
||||||
|
if [ "$i" = 0 ]; then
|
||||||
|
echo >&2 'MySQL failed to start.'
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
set -e
|
||||||
|
mysql_upgrade -p$MYSQL_ROOT_PASSWORD
|
||||||
|
echo 'Finished upgrading'
|
||||||
|
if ! kill -s TERM "$pid" || ! wait "$pid"; then
|
||||||
|
echo >&2 'Failed to stop MySQL for upgrading.'
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
else
|
||||||
|
echo "DB was created in Maria DB, skip upgrade."
|
||||||
|
fi
|
25
make/common/mariadb/Dockerfile
Normal file
25
make/common/mariadb/Dockerfile
Normal file
|
@ -0,0 +1,25 @@
|
||||||
|
FROM vmware/photon:1.0
|
||||||
|
|
||||||
|
#The Docker Daemon has to be running with storage backend btrfs when building the image
|
||||||
|
|
||||||
|
RUN tdnf distro-sync -y || echo \
|
||||||
|
&& tdnf install -y sed shadow procps-ng gawk gzip sudo net-tools \
|
||||||
|
&& groupadd -r -g 999 mysql && useradd --no-log-init -r -g 999 -u 999 mysql \
|
||||||
|
&& tdnf install -y mariadb-server mariadb \
|
||||||
|
&& mkdir /docker-entrypoint-initdb.d /docker-entrypoint-updatedb.d \
|
||||||
|
&& rm -fr /var/lib/mysql \
|
||||||
|
&& mkdir -p /var/lib/mysql /var/run/mysqld \
|
||||||
|
&& chown -R mysql:mysql /var/lib/mysql /var/run/mysqld \
|
||||||
|
&& chmod 777 /var/run/mysqld /docker-entrypoint-initdb.d /docker-entrypoint-updatedb.d \
|
||||||
|
&& tdnf clean all
|
||||||
|
|
||||||
|
COPY docker-entrypoint.sh /usr/local/bin/
|
||||||
|
RUN chmod +x /usr/local/bin/docker-entrypoint.sh
|
||||||
|
COPY my.cnf /etc/
|
||||||
|
RUN ln -s usr/local/bin/docker-entrypoint.sh /
|
||||||
|
|
||||||
|
VOLUME /var/lib/mysql
|
||||||
|
EXPOSE 3306
|
||||||
|
|
||||||
|
ENTRYPOINT ["/usr/local/bin/docker-entrypoint.sh"]
|
||||||
|
CMD ["mysqld"]
|
203
make/common/mariadb/docker-entrypoint.sh
Normal file
203
make/common/mariadb/docker-entrypoint.sh
Normal file
|
@ -0,0 +1,203 @@
|
||||||
|
#!/bin/bash
|
||||||
|
set -eo pipefail
|
||||||
|
shopt -s nullglob
|
||||||
|
|
||||||
|
# if command starts with an option, prepend mysqld
|
||||||
|
if [ "${1:0:1}" = '-' ]; then
|
||||||
|
set -- mysqld "$@"
|
||||||
|
fi
|
||||||
|
|
||||||
|
# skip setup if they want an option that stops mysqld
|
||||||
|
wantHelp=
|
||||||
|
for arg; do
|
||||||
|
case "$arg" in
|
||||||
|
-'?'|--help|--print-defaults|-V|--version)
|
||||||
|
wantHelp=1
|
||||||
|
break
|
||||||
|
;;
|
||||||
|
esac
|
||||||
|
done
|
||||||
|
|
||||||
|
# usage: file_env VAR [DEFAULT]
|
||||||
|
# ie: file_env 'XYZ_DB_PASSWORD' 'example'
|
||||||
|
# (will allow for "$XYZ_DB_PASSWORD_FILE" to fill in the value of
|
||||||
|
# "$XYZ_DB_PASSWORD" from a file, especially for Docker's secrets feature)
|
||||||
|
file_env() {
|
||||||
|
local var="$1"
|
||||||
|
local fileVar="${var}_FILE"
|
||||||
|
local def="${2:-}"
|
||||||
|
if [ "${!var:-}" ] && [ "${!fileVar:-}" ]; then
|
||||||
|
echo >&2 "error: both $var and $fileVar are set (but are exclusive)"
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
local val="$def"
|
||||||
|
if [ "${!var:-}" ]; then
|
||||||
|
val="${!var}"
|
||||||
|
elif [ "${!fileVar:-}" ]; then
|
||||||
|
val="$(< "${!fileVar}")"
|
||||||
|
fi
|
||||||
|
export "$var"="$val"
|
||||||
|
unset "$fileVar"
|
||||||
|
}
|
||||||
|
|
||||||
|
_check_config() {
|
||||||
|
toRun=( "$@" --verbose --help --log-bin-index="$(mktemp -u)" )
|
||||||
|
if ! errors="$("${toRun[@]}" 2>&1 >/dev/null)"; then
|
||||||
|
cat >&2 <<-EOM
|
||||||
|
|
||||||
|
ERROR: mysqld failed while attempting to check config
|
||||||
|
command was: "${toRun[*]}"
|
||||||
|
|
||||||
|
$errors
|
||||||
|
EOM
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
}
|
||||||
|
|
||||||
|
# Fetch value from server config
|
||||||
|
# We use mysqld --verbose --help instead of my_print_defaults because the
|
||||||
|
# latter only show values present in config files, and not server defaults
|
||||||
|
_get_config() {
|
||||||
|
local conf="$1"; shift
|
||||||
|
"$@" --verbose --help --log-bin-index="$(mktemp -u)" 2>/dev/null | awk '$1 == "'"$conf"'" { print $2; exit }'
|
||||||
|
}
|
||||||
|
|
||||||
|
# allow the container to be started with `--user`
|
||||||
|
if [ "$1" = 'mysqld' -a -z "$wantHelp" -a "$(id -u)" = '0' ]; then
|
||||||
|
_check_config "$@"
|
||||||
|
DATADIR="$(_get_config 'datadir' "$@")"
|
||||||
|
mkdir -p "$DATADIR"
|
||||||
|
chown -R mysql:mysql "$DATADIR"
|
||||||
|
if [ -d '/docker-entrypoint-initdb.d' ]; then
|
||||||
|
chmod -R +rx /docker-entrypoint-updatedb.d
|
||||||
|
fi
|
||||||
|
if [ -d '/docker-entrypoint-updatedb.d' ]; then
|
||||||
|
chmod -R +rx /docker-entrypoint-updatedb.d
|
||||||
|
fi
|
||||||
|
exec sudo -u mysql -E "$BASH_SOURCE" "$@"
|
||||||
|
fi
|
||||||
|
|
||||||
|
if [ "$1" = 'mysqld' -a -z "$wantHelp" ]; then
|
||||||
|
# still need to check config, container may have started with --user
|
||||||
|
_check_config "$@"
|
||||||
|
# Get config
|
||||||
|
DATADIR="$(_get_config 'datadir' "$@")"
|
||||||
|
|
||||||
|
if [ ! -d "$DATADIR/mysql" ]; then
|
||||||
|
file_env 'MYSQL_ROOT_PASSWORD'
|
||||||
|
if [ -z "$MYSQL_ROOT_PASSWORD" -a -z "$MYSQL_ALLOW_EMPTY_PASSWORD" ]; then
|
||||||
|
echo >&2 'error: database is uninitialized and password option is not specified '
|
||||||
|
echo >&2 ' You need to specify one of MYSQL_ROOT_PASSWORD, MYSQL_ALLOW_EMPTY_PASSWORD'
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
|
||||||
|
mkdir -p "$DATADIR"
|
||||||
|
echo 'Initializing database'
|
||||||
|
cd /usr
|
||||||
|
mysql_install_db --datadir="$DATADIR" --rpm
|
||||||
|
cd -
|
||||||
|
echo 'Database initialized'
|
||||||
|
|
||||||
|
SOCKET="$(_get_config 'socket' "$@")"
|
||||||
|
"$@" --skip-networking --socket="${SOCKET}" &
|
||||||
|
pid="$!"
|
||||||
|
|
||||||
|
mysql=( mysql --protocol=socket -uroot -hlocalhost --socket="${SOCKET}" )
|
||||||
|
|
||||||
|
for i in {30..0}; do
|
||||||
|
if echo 'SELECT 1' | "${mysql[@]}" &> /dev/null; then
|
||||||
|
break
|
||||||
|
fi
|
||||||
|
echo 'MySQL init process in progress...'
|
||||||
|
sleep 1
|
||||||
|
done
|
||||||
|
if [ "$i" = 0 ]; then
|
||||||
|
echo >&2 'MySQL init process failed.'
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
|
||||||
|
if [ -z "$MYSQL_INITDB_SKIP_TZINFO" ]; then
|
||||||
|
# sed is for https://bugs.mysql.com/bug.php?id=20545
|
||||||
|
mysql_tzinfo_to_sql /usr/share/zoneinfo | sed 's/Local time zone must be set--see zic manual page/FCTY/' | "${mysql[@]}" mysql
|
||||||
|
fi
|
||||||
|
|
||||||
|
rootCreate=
|
||||||
|
# default root to listen for connections from anywhere
|
||||||
|
file_env 'MYSQL_ROOT_HOST' '%'
|
||||||
|
if [ ! -z "$MYSQL_ROOT_HOST" -a "$MYSQL_ROOT_HOST" != 'localhost' ]; then
|
||||||
|
# no, we don't care if read finds a terminating character in this heredoc
|
||||||
|
# https://unix.stackexchange.com/questions/265149/why-is-set-o-errexit-breaking-this-read-heredoc-expression/265151#265151
|
||||||
|
read -r -d '' rootCreate <<-EOSQL || true
|
||||||
|
CREATE USER 'root'@'${MYSQL_ROOT_HOST}' IDENTIFIED BY '${MYSQL_ROOT_PASSWORD}' ;
|
||||||
|
GRANT ALL ON *.* TO 'root'@'${MYSQL_ROOT_HOST}' WITH GRANT OPTION ;
|
||||||
|
EOSQL
|
||||||
|
fi
|
||||||
|
|
||||||
|
"${mysql[@]}" <<-EOSQL
|
||||||
|
-- What's done in this file shouldn't be replicated
|
||||||
|
-- or products like mysql-fabric won't work
|
||||||
|
SET @@SESSION.SQL_LOG_BIN=0;
|
||||||
|
|
||||||
|
DELETE FROM mysql.user WHERE user NOT IN ('mysql.sys', 'mysqlxsys', 'root') OR host NOT IN ('localhost') ;
|
||||||
|
SET PASSWORD FOR 'root'@'localhost'=PASSWORD('${MYSQL_ROOT_PASSWORD}') ;
|
||||||
|
GRANT ALL ON *.* TO 'root'@'localhost' WITH GRANT OPTION ;
|
||||||
|
${rootCreate}
|
||||||
|
DROP DATABASE IF EXISTS test ;
|
||||||
|
FLUSH PRIVILEGES ;
|
||||||
|
EOSQL
|
||||||
|
|
||||||
|
if [ ! -z "$MYSQL_ROOT_PASSWORD" ]; then
|
||||||
|
mysql+=( -p"${MYSQL_ROOT_PASSWORD}" )
|
||||||
|
fi
|
||||||
|
|
||||||
|
file_env 'MYSQL_DATABASE'
|
||||||
|
if [ "$MYSQL_DATABASE" ]; then
|
||||||
|
echo "CREATE DATABASE IF NOT EXISTS \`$MYSQL_DATABASE\` ;" | "${mysql[@]}"
|
||||||
|
mysql+=( "$MYSQL_DATABASE" )
|
||||||
|
fi
|
||||||
|
|
||||||
|
file_env 'MYSQL_USER'
|
||||||
|
file_env 'MYSQL_PASSWORD'
|
||||||
|
if [ "$MYSQL_USER" -a "$MYSQL_PASSWORD" ]; then
|
||||||
|
echo "CREATE USER '$MYSQL_USER'@'%' IDENTIFIED BY '$MYSQL_PASSWORD' ;" | "${mysql[@]}"
|
||||||
|
|
||||||
|
if [ "$MYSQL_DATABASE" ]; then
|
||||||
|
echo "GRANT ALL ON \`$MYSQL_DATABASE\`.* TO '$MYSQL_USER'@'%' ;" | "${mysql[@]}"
|
||||||
|
fi
|
||||||
|
|
||||||
|
echo 'FLUSH PRIVILEGES ;' | "${mysql[@]}"
|
||||||
|
fi
|
||||||
|
|
||||||
|
echo
|
||||||
|
for f in /docker-entrypoint-initdb.d/*; do
|
||||||
|
case "$f" in
|
||||||
|
*.sh) echo "$0: running $f"; . "$f" ;;
|
||||||
|
*.sql) echo "$0: running $f"; "${mysql[@]}" < "$f"; echo ;;
|
||||||
|
*.sql.gz) echo "$0: running $f"; gunzip -c "$f" | "${mysql[@]}"; echo ;;
|
||||||
|
*) echo "$0: ignoring $f" ;;
|
||||||
|
esac
|
||||||
|
echo
|
||||||
|
done
|
||||||
|
|
||||||
|
if ! kill -s TERM "$pid" || ! wait "$pid"; then
|
||||||
|
echo >&2 'MySQL init process failed.'
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
|
||||||
|
echo
|
||||||
|
echo 'MySQL init process done. Ready for start up.'
|
||||||
|
echo
|
||||||
|
fi
|
||||||
|
for f in /docker-entrypoint-updatedb.d/*; do
|
||||||
|
case "$f" in
|
||||||
|
*.sh) echo "$0: running $f"; . "$f" ;;
|
||||||
|
### Not supported for now... until needed
|
||||||
|
# *.sql) echo "$0: running $f"; "${mysql[@]}" < "$f"; echo ;;
|
||||||
|
# *.sql.gz) echo "$0: running $f"; gunzip -c "$f" | "${mysql[@]}"; echo ;;
|
||||||
|
*) echo "$0: ignoring $f" ;;
|
||||||
|
esac
|
||||||
|
echo
|
||||||
|
done
|
||||||
|
fi
|
||||||
|
|
||||||
|
exec "$@"
|
191
make/common/mariadb/my.cnf
Normal file
191
make/common/mariadb/my.cnf
Normal file
|
@ -0,0 +1,191 @@
|
||||||
|
# MariaDB database server configuration file.
|
||||||
|
#
|
||||||
|
# You can copy this file to one of:
|
||||||
|
# - "/etc/mysql/my.cnf" to set global options,
|
||||||
|
# - "~/.my.cnf" to set user-specific options.
|
||||||
|
#
|
||||||
|
# One can use all long options that the program supports.
|
||||||
|
# Run program with --help to get a list of available options and with
|
||||||
|
# --print-defaults to see which it would actually understand and use.
|
||||||
|
#
|
||||||
|
# For explanations see
|
||||||
|
# http://dev.mysql.com/doc/mysql/en/server-system-variables.html
|
||||||
|
|
||||||
|
# This will be passed to all mysql clients
|
||||||
|
# It has been reported that passwords should be enclosed with ticks/quotes
|
||||||
|
# escpecially if they contain "#" chars...
|
||||||
|
# Remember to edit /etc/mysql/debian.cnf when changing the socket location.
|
||||||
|
[client]
|
||||||
|
port = 3306
|
||||||
|
socket = /var/run/mysqld/mysqld.sock
|
||||||
|
|
||||||
|
# Here is entries for some specific programs
|
||||||
|
# The following values assume you have at least 32M ram
|
||||||
|
|
||||||
|
# This was formally known as [safe_mysqld]. Both versions are currently parsed.
|
||||||
|
[mysqld_safe]
|
||||||
|
socket = /var/run/mysqld/mysqld.sock
|
||||||
|
nice = 0
|
||||||
|
|
||||||
|
[mysqld]
|
||||||
|
skip-host-cache
|
||||||
|
#skip-name-resolve
|
||||||
|
#
|
||||||
|
# * Basic Settings
|
||||||
|
#
|
||||||
|
user = mysql
|
||||||
|
pid-file = /var/run/mysqld/mysqld.pid
|
||||||
|
socket = /var/run/mysqld/mysqld.sock
|
||||||
|
port = 3306
|
||||||
|
basedir = /usr
|
||||||
|
datadir = /var/lib/mysql
|
||||||
|
tmpdir = /tmp
|
||||||
|
lc_messages_dir = /usr/share/mysql
|
||||||
|
lc_messages = en_US
|
||||||
|
skip-external-locking
|
||||||
|
#
|
||||||
|
# Instead of skip-networking the default is now to listen only on
|
||||||
|
# localhost which is more compatible and is not less secure.
|
||||||
|
#bind-address = 127.0.0.1
|
||||||
|
#
|
||||||
|
# * Fine Tuning
|
||||||
|
#
|
||||||
|
max_connections = 100
|
||||||
|
connect_timeout = 5
|
||||||
|
wait_timeout = 600
|
||||||
|
max_allowed_packet = 16M
|
||||||
|
thread_cache_size = 128
|
||||||
|
sort_buffer_size = 4M
|
||||||
|
bulk_insert_buffer_size = 16M
|
||||||
|
tmp_table_size = 32M
|
||||||
|
max_heap_table_size = 32M
|
||||||
|
#
|
||||||
|
# * MyISAM
|
||||||
|
#
|
||||||
|
# This replaces the startup script and checks MyISAM tables if needed
|
||||||
|
# the first time they are touched. On error, make copy and try a repair.
|
||||||
|
myisam_recover_options = BACKUP
|
||||||
|
key_buffer_size = 128M
|
||||||
|
#open-files-limit = 2000
|
||||||
|
table_open_cache = 400
|
||||||
|
myisam_sort_buffer_size = 512M
|
||||||
|
concurrent_insert = 2
|
||||||
|
read_buffer_size = 2M
|
||||||
|
read_rnd_buffer_size = 1M
|
||||||
|
#
|
||||||
|
# * Query Cache Configuration
|
||||||
|
#
|
||||||
|
# Cache only tiny result sets, so we can fit more in the query cache.
|
||||||
|
query_cache_limit = 128K
|
||||||
|
query_cache_size = 64M
|
||||||
|
# for more write intensive setups, set to DEMAND or OFF
|
||||||
|
#query_cache_type = DEMAND
|
||||||
|
#
|
||||||
|
# * Logging and Replication
|
||||||
|
#
|
||||||
|
# Both location gets rotated by the cronjob.
|
||||||
|
# Be aware that this log type is a performance killer.
|
||||||
|
# As of 5.1 you can enable the log at runtime!
|
||||||
|
#general_log_file = /var/log/mysql/mysql.log
|
||||||
|
#general_log = 1
|
||||||
|
#
|
||||||
|
# Error logging goes to syslog due to /etc/mysql/conf.d/mysqld_safe_syslog.cnf.
|
||||||
|
#
|
||||||
|
# we do want to know about network errors and such
|
||||||
|
#log_warnings = 2
|
||||||
|
#
|
||||||
|
# Enable the slow query log to see queries with especially long duration
|
||||||
|
#slow_query_log[={0|1}]
|
||||||
|
slow_query_log_file = /var/log/mysql/mariadb-slow.log
|
||||||
|
long_query_time = 10
|
||||||
|
#log_slow_rate_limit = 1000
|
||||||
|
#log_slow_verbosity = query_plan
|
||||||
|
|
||||||
|
#log-queries-not-using-indexes
|
||||||
|
#log_slow_admin_statements
|
||||||
|
#
|
||||||
|
# The following can be used as easy to replay backup logs or for replication.
|
||||||
|
# note: if you are setting up a replication slave, see README.Debian about
|
||||||
|
# other settings you may need to change.
|
||||||
|
#server-id = 1
|
||||||
|
#report_host = master1
|
||||||
|
#auto_increment_increment = 2
|
||||||
|
#auto_increment_offset = 1
|
||||||
|
#log_bin = /var/log/mysql/mariadb-bin
|
||||||
|
#log_bin_index = /var/log/mysql/mariadb-bin.index
|
||||||
|
# not fab for performance, but safer
|
||||||
|
#sync_binlog = 1
|
||||||
|
expire_logs_days = 10
|
||||||
|
max_binlog_size = 100M
|
||||||
|
# slaves
|
||||||
|
#relay_log = /var/log/mysql/relay-bin
|
||||||
|
#relay_log_index = /var/log/mysql/relay-bin.index
|
||||||
|
#relay_log_info_file = /var/log/mysql/relay-bin.info
|
||||||
|
#log_slave_updates
|
||||||
|
#read_only
|
||||||
|
#
|
||||||
|
# If applications support it, this stricter sql_mode prevents some
|
||||||
|
# mistakes like inserting invalid dates etc.
|
||||||
|
#sql_mode = NO_ENGINE_SUBSTITUTION,TRADITIONAL
|
||||||
|
#
|
||||||
|
# * InnoDB
|
||||||
|
#
|
||||||
|
# InnoDB is enabled by default with a 10MB datafile in /var/lib/mysql/.
|
||||||
|
# Read the manual for more InnoDB related options. There are many!
|
||||||
|
default_storage_engine = InnoDB
|
||||||
|
# you can't just change log file size, requires special procedure
|
||||||
|
#innodb_log_file_size = 50M
|
||||||
|
innodb_buffer_pool_size = 256M
|
||||||
|
innodb_log_buffer_size = 8M
|
||||||
|
innodb_file_per_table = 1
|
||||||
|
innodb_open_files = 400
|
||||||
|
innodb_io_capacity = 400
|
||||||
|
innodb_flush_method = O_DIRECT
|
||||||
|
#
|
||||||
|
# * Security Features
|
||||||
|
#
|
||||||
|
# Read the manual, too, if you want chroot!
|
||||||
|
# chroot = /var/lib/mysql/
|
||||||
|
#
|
||||||
|
# For generating SSL certificates I recommend the OpenSSL GUI "tinyca".
|
||||||
|
#
|
||||||
|
# ssl-ca=/etc/mysql/cacert.pem
|
||||||
|
# ssl-cert=/etc/mysql/server-cert.pem
|
||||||
|
# ssl-key=/etc/mysql/server-key.pem
|
||||||
|
|
||||||
|
#
|
||||||
|
# * Galera-related settings
|
||||||
|
#
|
||||||
|
[galera]
|
||||||
|
# Mandatory settings
|
||||||
|
#wsrep_on=ON
|
||||||
|
#wsrep_provider=
|
||||||
|
#wsrep_cluster_address=
|
||||||
|
#binlog_format=row
|
||||||
|
#default_storage_engine=InnoDB
|
||||||
|
#innodb_autoinc_lock_mode=2
|
||||||
|
#
|
||||||
|
# Allow server to accept connections on all interfaces.
|
||||||
|
#
|
||||||
|
#bind-address=0.0.0.0
|
||||||
|
#
|
||||||
|
# Optional setting
|
||||||
|
#wsrep_slave_threads=1
|
||||||
|
#innodb_flush_log_at_trx_commit=0
|
||||||
|
|
||||||
|
[mysqldump]
|
||||||
|
quick
|
||||||
|
quote-names
|
||||||
|
max_allowed_packet = 16M
|
||||||
|
|
||||||
|
[mysql]
|
||||||
|
#no-auto-rehash # faster start of mysql but no tab completition
|
||||||
|
|
||||||
|
[isamchk]
|
||||||
|
key_buffer = 16M
|
||||||
|
|
||||||
|
#
|
||||||
|
# * IMPORTANT: Additional settings that can override those from this file!
|
||||||
|
# The files must end with '.cnf', otherwise they'll be ignored.
|
||||||
|
#
|
||||||
|
!includedir /etc/my.cnf.d/
|
|
@ -1,6 +1,6 @@
|
||||||
FROM vmware/photon:1.0
|
FROM vmware/photon:1.0
|
||||||
|
|
||||||
RUN tdnf distro-sync -y \
|
RUN tdnf distro-sync -y || echo \
|
||||||
&& tdnf install -y nginx \
|
&& tdnf install -y nginx \
|
||||||
&& ln -sf /dev/stdout /var/log/nginx/access.log \
|
&& ln -sf /dev/stdout /var/log/nginx/access.log \
|
||||||
&& ln -sf /dev/stderr /var/log/nginx/error.log \
|
&& ln -sf /dev/stderr /var/log/nginx/error.log \
|
||||||
|
|
|
@ -3,7 +3,7 @@ FROM vmware/photon:1.0
|
||||||
ENV PGDATA /var/lib/postgresql/data
|
ENV PGDATA /var/lib/postgresql/data
|
||||||
|
|
||||||
RUN touch /etc/localtime.bak \
|
RUN touch /etc/localtime.bak \
|
||||||
&& tdnf distro-sync -y \
|
&& tdnf distro-sync -y || echo \
|
||||||
&& tdnf install -y sed shadow gzip postgresql\
|
&& tdnf install -y sed shadow gzip postgresql\
|
||||||
&& groupadd -r postgres --gid=999 \
|
&& groupadd -r postgres --gid=999 \
|
||||||
&& useradd -r -g postgres --uid=999 postgres \
|
&& useradd -r -g postgres --uid=999 postgres \
|
||||||
|
|
|
@ -1,9 +1,11 @@
|
||||||
FROM library/photon:1.0
|
FROM vmware/photon:1.0
|
||||||
|
|
||||||
#base image for rsyslog base on photon
|
#base image for rsyslog base on photon
|
||||||
|
|
||||||
RUN tdnf install -y cronie rsyslog shadow tar gzip \
|
RUN tdnf distro-sync -y || echo \
|
||||||
|
&& tdnf install -y cronie rsyslog shadow tar gzip \
|
||||||
&& mkdir /etc/rsyslog.d/ \
|
&& mkdir /etc/rsyslog.d/ \
|
||||||
&& mkdir /var/spool/rsyslog \
|
&& mkdir /var/spool/rsyslog \
|
||||||
&& groupadd syslog \
|
&& groupadd syslog \
|
||||||
&& useradd -g syslog syslog
|
&& useradd -g syslog syslog \
|
||||||
|
&& tdnf clean all
|
||||||
|
|
|
@ -27,7 +27,6 @@ EMAIL_FROM=$email_from
|
||||||
EMAIL_IDENTITY=$email_identity
|
EMAIL_IDENTITY=$email_identity
|
||||||
HARBOR_ADMIN_PASSWORD=$harbor_admin_password
|
HARBOR_ADMIN_PASSWORD=$harbor_admin_password
|
||||||
PROJECT_CREATION_RESTRICTION=$project_creation_restriction
|
PROJECT_CREATION_RESTRICTION=$project_creation_restriction
|
||||||
VERIFY_REMOTE_CERT=$verify_remote_cert
|
|
||||||
MAX_JOB_WORKERS=$max_job_workers
|
MAX_JOB_WORKERS=$max_job_workers
|
||||||
UI_SECRET=$ui_secret
|
UI_SECRET=$ui_secret
|
||||||
JOBSERVICE_SECRET=$jobservice_secret
|
JOBSERVICE_SECRET=$jobservice_secret
|
||||||
|
|
|
@ -49,7 +49,7 @@ services:
|
||||||
syslog-address: "tcp://127.0.0.1:1514"
|
syslog-address: "tcp://127.0.0.1:1514"
|
||||||
tag: "notary-signer"
|
tag: "notary-signer"
|
||||||
notary-db:
|
notary-db:
|
||||||
image: vmware/harbor-notary-db:mariadb-10.1.10
|
image: vmware/mariadb-photon:10.2.8
|
||||||
container_name: notary-db
|
container_name: notary-db
|
||||||
restart: always
|
restart: always
|
||||||
networks:
|
networks:
|
||||||
|
|
|
@ -1,7 +1,7 @@
|
||||||
FROM vmware/photon:1.0-20170928
|
FROM vmware/photon:1.0
|
||||||
|
|
||||||
RUN tdnf erase vim -y \
|
RUN tdnf erase vim -y \
|
||||||
&& tdnf distro-sync -y \
|
&& tdnf distro-sync -y || echo \
|
||||||
&& tdnf clean all \
|
&& tdnf clean all \
|
||||||
&& mkdir /harbor/
|
&& mkdir /harbor/
|
||||||
COPY ./make/dev/adminserver/harbor_adminserver /harbor/
|
COPY ./make/dev/adminserver/harbor_adminserver /harbor/
|
||||||
|
|
|
@ -1,6 +1,8 @@
|
||||||
FROM library/photon:1.0
|
FROM vmware/photon:1.0
|
||||||
|
|
||||||
RUN mkdir /harbor/
|
RUN mkdir /harbor/ \
|
||||||
|
&& tdnf distro-sync -y || echo \
|
||||||
|
&& tdnf clean all
|
||||||
COPY ./make/dev/jobservice/harbor_jobservice /harbor/
|
COPY ./make/dev/jobservice/harbor_jobservice /harbor/
|
||||||
|
|
||||||
RUN chmod u+x /harbor/harbor_jobservice
|
RUN chmod u+x /harbor/harbor_jobservice
|
||||||
|
|
|
@ -149,7 +149,6 @@ if protocol == "https":
|
||||||
customize_crt = rcp.get("configuration", "customize_crt")
|
customize_crt = rcp.get("configuration", "customize_crt")
|
||||||
max_job_workers = rcp.get("configuration", "max_job_workers")
|
max_job_workers = rcp.get("configuration", "max_job_workers")
|
||||||
token_expiration = rcp.get("configuration", "token_expiration")
|
token_expiration = rcp.get("configuration", "token_expiration")
|
||||||
verify_remote_cert = rcp.get("configuration", "verify_remote_cert")
|
|
||||||
proj_cre_restriction = rcp.get("configuration", "project_creation_restriction")
|
proj_cre_restriction = rcp.get("configuration", "project_creation_restriction")
|
||||||
secretkey_path = rcp.get("configuration", "secretkey_path")
|
secretkey_path = rcp.get("configuration", "secretkey_path")
|
||||||
if rcp.has_option("configuration", "admiral_url"):
|
if rcp.has_option("configuration", "admiral_url"):
|
||||||
|
@ -239,7 +238,6 @@ render(os.path.join(templates_dir, "adminserver", "env"),
|
||||||
email_identity=email_identity,
|
email_identity=email_identity,
|
||||||
harbor_admin_password=harbor_admin_password,
|
harbor_admin_password=harbor_admin_password,
|
||||||
project_creation_restriction=proj_cre_restriction,
|
project_creation_restriction=proj_cre_restriction,
|
||||||
verify_remote_cert=verify_remote_cert,
|
|
||||||
max_job_workers=max_job_workers,
|
max_job_workers=max_job_workers,
|
||||||
ui_secret=ui_secret,
|
ui_secret=ui_secret,
|
||||||
jobservice_secret=jobservice_secret,
|
jobservice_secret=jobservice_secret,
|
||||||
|
|
|
@ -111,10 +111,6 @@ var (
|
||||||
env: "MAX_JOB_WORKERS",
|
env: "MAX_JOB_WORKERS",
|
||||||
parse: parseStringToInt,
|
parse: parseStringToInt,
|
||||||
},
|
},
|
||||||
common.VerifyRemoteCert: &parser{
|
|
||||||
env: "VERIFY_REMOTE_CERT",
|
|
||||||
parse: parseStringToBool,
|
|
||||||
},
|
|
||||||
common.ProjectCreationRestriction: "PROJECT_CREATION_RESTRICTION",
|
common.ProjectCreationRestriction: "PROJECT_CREATION_RESTRICTION",
|
||||||
common.AdminInitialPassword: "HARBOR_ADMIN_PASSWORD",
|
common.AdminInitialPassword: "HARBOR_ADMIN_PASSWORD",
|
||||||
common.AdmiralEndpoint: "ADMIRAL_URL",
|
common.AdmiralEndpoint: "ADMIRAL_URL",
|
||||||
|
|
|
@ -57,7 +57,6 @@ const (
|
||||||
EmailIdentity = "email_identity"
|
EmailIdentity = "email_identity"
|
||||||
EmailInsecure = "email_insecure"
|
EmailInsecure = "email_insecure"
|
||||||
ProjectCreationRestriction = "project_creation_restriction"
|
ProjectCreationRestriction = "project_creation_restriction"
|
||||||
VerifyRemoteCert = "verify_remote_cert"
|
|
||||||
MaxJobWorkers = "max_job_workers"
|
MaxJobWorkers = "max_job_workers"
|
||||||
TokenExpiration = "token_expiration"
|
TokenExpiration = "token_expiration"
|
||||||
CfgExpiration = "cfg_expiration"
|
CfgExpiration = "cfg_expiration"
|
||||||
|
|
|
@ -76,7 +76,7 @@ func DeleteRepTarget(id int64) error {
|
||||||
func UpdateRepTarget(target models.RepTarget) error {
|
func UpdateRepTarget(target models.RepTarget) error {
|
||||||
o := GetOrmer()
|
o := GetOrmer()
|
||||||
target.UpdateTime = time.Now()
|
target.UpdateTime = time.Now()
|
||||||
_, err := o.Update(&target, "URL", "Name", "Username", "Password", "UpdateTime")
|
_, err := o.Update(&target, "URL", "Name", "Username", "Password", "Insecure", "UpdateTime")
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -105,6 +105,7 @@ type RepTarget struct {
|
||||||
Username string `orm:"column(username)" json:"username"`
|
Username string `orm:"column(username)" json:"username"`
|
||||||
Password string `orm:"column(password)" json:"password"`
|
Password string `orm:"column(password)" json:"password"`
|
||||||
Type int `orm:"column(target_type)" json:"type"`
|
Type int `orm:"column(target_type)" json:"type"`
|
||||||
|
Insecure bool `orm:"column(insecure)" json:"insecure"`
|
||||||
CreationTime time.Time `orm:"column(creation_time);auto_now_add" json:"creation_time"`
|
CreationTime time.Time `orm:"column(creation_time);auto_now_add" json:"creation_time"`
|
||||||
UpdateTime time.Time `orm:"column(update_time);auto_now" json:"update_time"`
|
UpdateTime time.Time `orm:"column(update_time);auto_now" json:"update_time"`
|
||||||
}
|
}
|
||||||
|
|
73
src/common/utils/clair/client_test.go
Normal file
73
src/common/utils/clair/client_test.go
Normal file
|
@ -0,0 +1,73 @@
|
||||||
|
// Copyright (c) 2017 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 clair
|
||||||
|
|
||||||
|
import (
|
||||||
|
"os"
|
||||||
|
"testing"
|
||||||
|
|
||||||
|
"github.com/stretchr/testify/assert"
|
||||||
|
"github.com/vmware/harbor/src/common/models"
|
||||||
|
"github.com/vmware/harbor/src/common/utils/clair/test"
|
||||||
|
)
|
||||||
|
|
||||||
|
var (
|
||||||
|
notificationID = "ec45ec87-bfc8-4129-a1c3-d2b82622175a"
|
||||||
|
layerName = "03adedf41d4e0ea1b2458546a5b4717bf5f24b23489b25589e20c692aaf84d19"
|
||||||
|
client *Client
|
||||||
|
)
|
||||||
|
|
||||||
|
func TestMain(m *testing.M) {
|
||||||
|
mockClairServer := test.NewMockServer()
|
||||||
|
defer mockClairServer.Close()
|
||||||
|
client = NewClient(mockClairServer.URL, nil)
|
||||||
|
rc := m.Run()
|
||||||
|
if rc != 0 {
|
||||||
|
os.Exit(rc)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestListNamespaces(t *testing.T) {
|
||||||
|
assert := assert.New(t)
|
||||||
|
ns, err := client.ListNamespaces()
|
||||||
|
assert.Nil(err)
|
||||||
|
assert.Equal(25, len(ns))
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestNotifications(t *testing.T) {
|
||||||
|
assert := assert.New(t)
|
||||||
|
n, err := client.GetNotification(notificationID)
|
||||||
|
assert.Nil(err)
|
||||||
|
assert.Equal(notificationID, n.Name)
|
||||||
|
_, err = client.GetNotification("noexist")
|
||||||
|
assert.NotNil(err)
|
||||||
|
err = client.DeleteNotification(notificationID)
|
||||||
|
assert.Nil(err)
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestLaysers(t *testing.T) {
|
||||||
|
assert := assert.New(t)
|
||||||
|
layer := models.ClairLayer{
|
||||||
|
Name: "fakelayer",
|
||||||
|
ParentName: "parent",
|
||||||
|
Path: "http://registry:5000/layers/xxx",
|
||||||
|
}
|
||||||
|
err := client.ScanLayer(layer)
|
||||||
|
assert.Nil(err)
|
||||||
|
data, err := client.GetResult(layerName)
|
||||||
|
assert.Nil(err)
|
||||||
|
assert.Equal(layerName, data.Layer.Name)
|
||||||
|
_, err = client.GetResult("notexist")
|
||||||
|
assert.NotNil(err)
|
||||||
|
}
|
62
src/common/utils/clair/test/notification.json
Normal file
62
src/common/utils/clair/test/notification.json
Normal file
|
@ -0,0 +1,62 @@
|
||||||
|
{
|
||||||
|
"Notification": {
|
||||||
|
"Name": "ec45ec87-bfc8-4129-a1c3-d2b82622175a",
|
||||||
|
"Created": "1456247389",
|
||||||
|
"Notified": "1456246708",
|
||||||
|
"Limit": 2,
|
||||||
|
"Page": "gAAAAABWzJaC2JCH6Apr_R1f2EkjGdibnrKOobTcYXBWl6t0Cw6Q04ENGIymB6XlZ3Zi0bYt2c-2cXe43fvsJ7ECZhZz4P8C8F9efr_SR0HPiejzQTuG0qAzeO8klogFfFjSz2peBvgP",
|
||||||
|
"NextPage": "gAAAAABWzJaCTyr6QXP2aYsCwEZfWIkU2GkNplSMlTOhLJfiR3LorBv8QYgEIgyOvZRmHQEzJKvkI6TP2PkRczBkcD17GE89btaaKMqEX14yHDgyfQvdasW1tj3-5bBRt0esKi9ym5En",
|
||||||
|
"New": {
|
||||||
|
"Vulnerability": {
|
||||||
|
"Name": "CVE-TEST",
|
||||||
|
"NamespaceName": "debian:8",
|
||||||
|
"Description": "New CVE",
|
||||||
|
"Severity": "Low",
|
||||||
|
"FixedIn": [
|
||||||
|
{
|
||||||
|
"Name": "grep",
|
||||||
|
"NamespaceName": "debian:8",
|
||||||
|
"Version": "2.25"
|
||||||
|
}
|
||||||
|
]
|
||||||
|
},
|
||||||
|
"OrderedLayersIntroducingVulnerability": [
|
||||||
|
{
|
||||||
|
"Index": 1,
|
||||||
|
"LayerName": "523ef1d23f222195488575f52a39c729c76a8c5630c9a194139cb246fb212da6"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"Index": 2,
|
||||||
|
"LayerName": "3b59c795b34670618fbcace4dac7a27c5ecec156812c9e2c90d3f4be1916b12d"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"LayersIntroducingVulnerability": [
|
||||||
|
"523ef1d23f222195488575f52a39c729c76a8c5630c9a194139cb246fb212da6",
|
||||||
|
"3b59c795b34670618fbcace4dac7a27c5ecec156812c9e2c90d182371916b12d"
|
||||||
|
]
|
||||||
|
},
|
||||||
|
"Old": {
|
||||||
|
"Vulnerability": {
|
||||||
|
"Name": "CVE-TEST",
|
||||||
|
"NamespaceName": "debian:8",
|
||||||
|
"Description": "New CVE",
|
||||||
|
"Severity": "Low",
|
||||||
|
"FixedIn": []
|
||||||
|
},
|
||||||
|
"OrderedLayersIntroducingVulnerability": [
|
||||||
|
{
|
||||||
|
"Index": 1,
|
||||||
|
"LayerName": "523ef1d23f222195488575f52a39c729c76a8c5630c9a194139cb246fb212da6"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"Index": 2,
|
||||||
|
"LayerName": "3b59c795b34670618fbcace4dac7a27c5ecec156812c9e2c90d3f4be1916b12d"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"LayersIntroducingVulnerability": [
|
||||||
|
"3b59c795b34670618fbcace4dac7a27c5ecec156812c9e2c90d3f4be1916b12d",
|
||||||
|
"523ef1d23f222195488575f52a39c729c76a8c5630c9a194139cb246fb212da6"
|
||||||
|
]
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
1
src/common/utils/clair/test/ns.json
Normal file
1
src/common/utils/clair/test/ns.json
Normal file
|
@ -0,0 +1 @@
|
||||||
|
{"Namespaces":[{"Name":"debian:7","VersionFormat":"dpkg"},{"Name":"debian:unstable","VersionFormat":"dpkg"},{"Name":"debian:9","VersionFormat":"dpkg"},{"Name":"debian:10","VersionFormat":"dpkg"},{"Name":"debian:8","VersionFormat":"dpkg"},{"Name":"alpine:v3.6","VersionFormat":"dpkg"},{"Name":"alpine:v3.5","VersionFormat":"dpkg"},{"Name":"alpine:v3.4","VersionFormat":"dpkg"},{"Name":"alpine:v3.3","VersionFormat":"dpkg"},{"Name":"oracle:6","VersionFormat":"rpm"},{"Name":"oracle:7","VersionFormat":"rpm"},{"Name":"oracle:5","VersionFormat":"rpm"},{"Name":"ubuntu:14.04","VersionFormat":"dpkg"},{"Name":"ubuntu:15.10","VersionFormat":"dpkg"},{"Name":"ubuntu:17.04","VersionFormat":"dpkg"},{"Name":"ubuntu:16.04","VersionFormat":"dpkg"},{"Name":"ubuntu:12.04","VersionFormat":"dpkg"},{"Name":"ubuntu:13.04","VersionFormat":"dpkg"},{"Name":"ubuntu:14.10","VersionFormat":"dpkg"},{"Name":"ubuntu:12.10","VersionFormat":"dpkg"},{"Name":"ubuntu:16.10","VersionFormat":"dpkg"},{"Name":"ubuntu:15.04","VersionFormat":"dpkg"},{"Name":"centos:6","VersionFormat":"rpm"},{"Name":"centos:7","VersionFormat":"rpm"},{"Name":"centos:5","VersionFormat":"rpm"}]}
|
117
src/common/utils/clair/test/server.go
Normal file
117
src/common/utils/clair/test/server.go
Normal file
|
@ -0,0 +1,117 @@
|
||||||
|
// Copyright (c) 2017 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 test
|
||||||
|
|
||||||
|
import (
|
||||||
|
"encoding/json"
|
||||||
|
"fmt"
|
||||||
|
"io/ioutil"
|
||||||
|
"net/http"
|
||||||
|
"net/http/httptest"
|
||||||
|
"path"
|
||||||
|
"runtime"
|
||||||
|
"strings"
|
||||||
|
|
||||||
|
"github.com/vmware/harbor/src/common/models"
|
||||||
|
"github.com/vmware/harbor/src/common/utils/log"
|
||||||
|
)
|
||||||
|
|
||||||
|
func currPath() string {
|
||||||
|
_, f, _, ok := runtime.Caller(0)
|
||||||
|
if !ok {
|
||||||
|
panic("Failed to get current directory")
|
||||||
|
}
|
||||||
|
return path.Dir(f)
|
||||||
|
}
|
||||||
|
|
||||||
|
func serveFile(rw http.ResponseWriter, p string) {
|
||||||
|
data, err := ioutil.ReadFile(p)
|
||||||
|
if err != nil {
|
||||||
|
http.Error(rw, err.Error(), 500)
|
||||||
|
}
|
||||||
|
|
||||||
|
_, err2 := rw.Write(data)
|
||||||
|
if err2 != nil {
|
||||||
|
http.Error(rw, err2.Error(), 500)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
type notificationHandler struct {
|
||||||
|
id string
|
||||||
|
}
|
||||||
|
|
||||||
|
func (n *notificationHandler) ServeHTTP(rw http.ResponseWriter, req *http.Request) {
|
||||||
|
suffix := strings.TrimPrefix(req.URL.Path, "/v1/notifications/")
|
||||||
|
if req.Method == http.MethodDelete {
|
||||||
|
rw.WriteHeader(200)
|
||||||
|
} else if req.Method == http.MethodGet {
|
||||||
|
if strings.HasPrefix(suffix, n.id) {
|
||||||
|
serveFile(rw, path.Join(currPath(), "notification.json"))
|
||||||
|
} else {
|
||||||
|
rw.WriteHeader(404)
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
rw.WriteHeader(http.StatusMethodNotAllowed)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
type layerHandler struct {
|
||||||
|
name string
|
||||||
|
}
|
||||||
|
|
||||||
|
func (l *layerHandler) ServeHTTP(rw http.ResponseWriter, req *http.Request) {
|
||||||
|
if req.Method == http.MethodPost {
|
||||||
|
data, err := ioutil.ReadAll(req.Body)
|
||||||
|
defer req.Body.Close()
|
||||||
|
if err != nil {
|
||||||
|
http.Error(rw, err.Error(), 500)
|
||||||
|
}
|
||||||
|
layer := &models.ClairLayerEnvelope{}
|
||||||
|
if err := json.Unmarshal(data, layer); err != nil {
|
||||||
|
http.Error(rw, err.Error(), http.StatusBadRequest)
|
||||||
|
}
|
||||||
|
rw.WriteHeader(http.StatusCreated)
|
||||||
|
} else if req.Method == http.MethodGet {
|
||||||
|
name := strings.TrimPrefix(req.URL.Path, "/v1/layers/")
|
||||||
|
if name == l.name {
|
||||||
|
serveFile(rw, path.Join(currPath(), "total-12.json"))
|
||||||
|
} else {
|
||||||
|
http.Error(rw, fmt.Sprintf("Invalid layer name: %s", name), http.StatusNotFound)
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
http.Error(rw, "", http.StatusMethodNotAllowed)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// NewMockServer ...
|
||||||
|
func NewMockServer() *httptest.Server {
|
||||||
|
mux := http.NewServeMux()
|
||||||
|
mux.HandleFunc("/v1/namespaces", func(rw http.ResponseWriter, req *http.Request) {
|
||||||
|
if req.Method == http.MethodGet {
|
||||||
|
serveFile(rw, path.Join(currPath(), "ns.json"))
|
||||||
|
} else {
|
||||||
|
rw.WriteHeader(http.StatusMethodNotAllowed)
|
||||||
|
}
|
||||||
|
})
|
||||||
|
mux.Handle("/v1/notifications/", ¬ificationHandler{id: "ec45ec87-bfc8-4129-a1c3-d2b82622175a"})
|
||||||
|
mux.Handle("/v1/layers", &layerHandler{name: "03adedf41d4e0ea1b2458546a5b4717bf5f24b23489b25589e20c692aaf84d19"})
|
||||||
|
mux.Handle("/v1/layers/", &layerHandler{name: "03adedf41d4e0ea1b2458546a5b4717bf5f24b23489b25589e20c692aaf84d19"})
|
||||||
|
mux.HandleFunc("/", func(rw http.ResponseWriter, req *http.Request) {
|
||||||
|
log.Infof("method: %s, path: %s", req.Method, req.URL.Path)
|
||||||
|
rw.WriteHeader(http.StatusNotFound)
|
||||||
|
},
|
||||||
|
)
|
||||||
|
return httptest.NewServer(mux)
|
||||||
|
}
|
|
@ -53,7 +53,6 @@ var adminServerDefaultConfig = map[string]interface{}{
|
||||||
common.EmailInsecure: false,
|
common.EmailInsecure: false,
|
||||||
common.EmailIdentity: "",
|
common.EmailIdentity: "",
|
||||||
common.ProjectCreationRestriction: common.ProCrtRestrAdmOnly,
|
common.ProjectCreationRestriction: common.ProCrtRestrAdmOnly,
|
||||||
common.VerifyRemoteCert: false,
|
|
||||||
common.MaxJobWorkers: 3,
|
common.MaxJobWorkers: 3,
|
||||||
common.TokenExpiration: 30,
|
common.TokenExpiration: 30,
|
||||||
common.CfgExpiration: 5,
|
common.CfgExpiration: 5,
|
||||||
|
|
|
@ -74,15 +74,6 @@ func initKeyProvider() {
|
||||||
keyProvider = comcfg.NewFileKeyProvider(path)
|
keyProvider = comcfg.NewFileKeyProvider(path)
|
||||||
}
|
}
|
||||||
|
|
||||||
// VerifyRemoteCert returns bool value.
|
|
||||||
func VerifyRemoteCert() (bool, error) {
|
|
||||||
cfg, err := mg.Get()
|
|
||||||
if err != nil {
|
|
||||||
return true, err
|
|
||||||
}
|
|
||||||
return cfg[common.VerifyRemoteCert].(bool), nil
|
|
||||||
}
|
|
||||||
|
|
||||||
// Database ...
|
// Database ...
|
||||||
func Database() (*models.Database, error) {
|
func Database() (*models.Database, error) {
|
||||||
cfg, err := mg.Get()
|
cfg, err := mg.Get()
|
||||||
|
|
|
@ -49,10 +49,6 @@ func TestConfig(t *testing.T) {
|
||||||
t.Fatalf("failed to initialize configurations: %v", err)
|
t.Fatalf("failed to initialize configurations: %v", err)
|
||||||
}
|
}
|
||||||
|
|
||||||
if _, err := VerifyRemoteCert(); err != nil {
|
|
||||||
t.Fatalf("failed to get verify remote cert: %v", err)
|
|
||||||
}
|
|
||||||
|
|
||||||
if _, err := Database(); err != nil {
|
if _, err := Database(); err != nil {
|
||||||
t.Fatalf("failed to get database settings: %v", err)
|
t.Fatalf("failed to get database settings: %v", err)
|
||||||
}
|
}
|
||||||
|
|
|
@ -106,7 +106,7 @@ func TestRepJob(t *testing.T) {
|
||||||
j, err := dao.GetRepJob(repJobID)
|
j, err := dao.GetRepJob(repJobID)
|
||||||
assert.Equal(models.JobRetrying, j.Status)
|
assert.Equal(models.JobRetrying, j.Status)
|
||||||
assert.Equal(1, rj.parm.Enabled)
|
assert.Equal(1, rj.parm.Enabled)
|
||||||
assert.True(rj.parm.Insecure)
|
assert.False(rj.parm.Insecure)
|
||||||
rj2 := NewRepJob(99999)
|
rj2 := NewRepJob(99999)
|
||||||
err = rj2.Init()
|
err = rj2.Init()
|
||||||
assert.NotNil(err)
|
assert.NotNil(err)
|
||||||
|
|
|
@ -120,17 +120,12 @@ func (rj *RepJob) Init() error {
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
verify, err := config.VerifyRemoteCert()
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
rj.parm = &RepJobParm{
|
rj.parm = &RepJobParm{
|
||||||
LocalRegURL: regURL,
|
LocalRegURL: regURL,
|
||||||
Repository: job.Repository,
|
Repository: job.Repository,
|
||||||
Tags: job.TagList,
|
Tags: job.TagList,
|
||||||
Enabled: policy.Enabled,
|
Enabled: policy.Enabled,
|
||||||
Operation: job.Operation,
|
Operation: job.Operation,
|
||||||
Insecure: !verify,
|
|
||||||
}
|
}
|
||||||
if policy.Enabled == 0 {
|
if policy.Enabled == 0 {
|
||||||
//worker will cancel this job
|
//worker will cancel this job
|
||||||
|
@ -159,6 +154,7 @@ func (rj *RepJob) Init() error {
|
||||||
}
|
}
|
||||||
|
|
||||||
rj.parm.TargetPassword = pwd
|
rj.parm.TargetPassword = pwd
|
||||||
|
rj.parm.Insecure = target.Insecure
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -49,7 +49,6 @@ var (
|
||||||
common.EmailIdentity,
|
common.EmailIdentity,
|
||||||
common.EmailInsecure,
|
common.EmailInsecure,
|
||||||
common.ProjectCreationRestriction,
|
common.ProjectCreationRestriction,
|
||||||
common.VerifyRemoteCert,
|
|
||||||
common.TokenExpiration,
|
common.TokenExpiration,
|
||||||
common.ScanAllPolicy,
|
common.ScanAllPolicy,
|
||||||
}
|
}
|
||||||
|
@ -81,7 +80,6 @@ var (
|
||||||
common.EmailSSL,
|
common.EmailSSL,
|
||||||
common.EmailInsecure,
|
common.EmailInsecure,
|
||||||
common.SelfRegistration,
|
common.SelfRegistration,
|
||||||
common.VerifyRemoteCert,
|
|
||||||
}
|
}
|
||||||
|
|
||||||
passwordKeys = []string{
|
passwordKeys = []string{
|
||||||
|
|
|
@ -61,7 +61,7 @@ func TestPutConfig(t *testing.T) {
|
||||||
apiTest := newHarborAPI()
|
apiTest := newHarborAPI()
|
||||||
|
|
||||||
cfg := map[string]interface{}{
|
cfg := map[string]interface{}{
|
||||||
common.VerifyRemoteCert: false,
|
common.TokenExpiration: 60,
|
||||||
}
|
}
|
||||||
|
|
||||||
code, err := apiTest.PutConfig(*admin, cfg)
|
code, err := apiTest.PutConfig(*admin, cfg)
|
||||||
|
@ -104,13 +104,13 @@ func TestResetConfig(t *testing.T) {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
value, ok := cfgs[common.VerifyRemoteCert]
|
value, ok := cfgs[common.TokenExpiration]
|
||||||
if !ok {
|
if !ok {
|
||||||
t.Errorf("%s not found", common.VerifyRemoteCert)
|
t.Errorf("%s not found", common.TokenExpiration)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
assert.Equal(value.Value.(bool), true, "unexpected value")
|
assert.Equal(int(value.Value.(float64)), 30, "unexpected 30")
|
||||||
|
|
||||||
ccc, err := config.GetSystemCfg()
|
ccc, err := config.GetSystemCfg()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
|
|
|
@ -58,13 +58,8 @@ func (t *TargetAPI) Prepare() {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func (t *TargetAPI) ping(endpoint, username, password string) {
|
func (t *TargetAPI) ping(endpoint, username, password string, insecure bool) {
|
||||||
verify, err := config.VerifyRemoteCert()
|
registry, err := newRegistryClient(endpoint, insecure, username, password)
|
||||||
if err != nil {
|
|
||||||
log.Errorf("failed to check whether insecure or not: %v", err)
|
|
||||||
t.CustomAbort(http.StatusInternalServerError, http.StatusText(http.StatusInternalServerError))
|
|
||||||
}
|
|
||||||
registry, err := newRegistryClient(endpoint, !verify, username, password)
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
// timeout, dns resolve error, connection refused, etc.
|
// timeout, dns resolve error, connection refused, etc.
|
||||||
if urlErr, ok := err.(*url.Error); ok {
|
if urlErr, ok := err.(*url.Error); ok {
|
||||||
|
@ -105,6 +100,7 @@ func (t *TargetAPI) PingByID() {
|
||||||
endpoint := target.URL
|
endpoint := target.URL
|
||||||
username := target.Username
|
username := target.Username
|
||||||
password := target.Password
|
password := target.Password
|
||||||
|
insecure := target.Insecure
|
||||||
if len(password) != 0 {
|
if len(password) != 0 {
|
||||||
password, err = utils.ReversibleDecrypt(password, t.secretKey)
|
password, err = utils.ReversibleDecrypt(password, t.secretKey)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
|
@ -112,7 +108,7 @@ func (t *TargetAPI) PingByID() {
|
||||||
t.CustomAbort(http.StatusInternalServerError, http.StatusText(http.StatusInternalServerError))
|
t.CustomAbort(http.StatusInternalServerError, http.StatusText(http.StatusInternalServerError))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
t.ping(endpoint, username, password)
|
t.ping(endpoint, username, password, insecure)
|
||||||
}
|
}
|
||||||
|
|
||||||
// Ping validates whether the target is reachable and whether the credential is valid
|
// Ping validates whether the target is reachable and whether the credential is valid
|
||||||
|
@ -121,6 +117,7 @@ func (t *TargetAPI) Ping() {
|
||||||
Endpoint string `json:"endpoint"`
|
Endpoint string `json:"endpoint"`
|
||||||
Username string `json:"username"`
|
Username string `json:"username"`
|
||||||
Password string `json:"password"`
|
Password string `json:"password"`
|
||||||
|
Insecure bool `json:"insecure"`
|
||||||
}{}
|
}{}
|
||||||
t.DecodeJSONReq(&req)
|
t.DecodeJSONReq(&req)
|
||||||
|
|
||||||
|
@ -128,7 +125,7 @@ func (t *TargetAPI) Ping() {
|
||||||
t.CustomAbort(http.StatusBadRequest, "endpoint is required")
|
t.CustomAbort(http.StatusBadRequest, "endpoint is required")
|
||||||
}
|
}
|
||||||
|
|
||||||
t.ping(req.Endpoint, req.Username, req.Password)
|
t.ping(req.Endpoint, req.Username, req.Password, req.Insecure)
|
||||||
}
|
}
|
||||||
|
|
||||||
// Get ...
|
// Get ...
|
||||||
|
@ -255,6 +252,7 @@ func (t *TargetAPI) Put() {
|
||||||
Endpoint *string `json:"endpoint"`
|
Endpoint *string `json:"endpoint"`
|
||||||
Username *string `json:"username"`
|
Username *string `json:"username"`
|
||||||
Password *string `json:"password"`
|
Password *string `json:"password"`
|
||||||
|
Insecure *bool `json:"insecure"`
|
||||||
}{}
|
}{}
|
||||||
t.DecodeJSONReq(&req)
|
t.DecodeJSONReq(&req)
|
||||||
|
|
||||||
|
@ -273,6 +271,9 @@ func (t *TargetAPI) Put() {
|
||||||
if req.Password != nil {
|
if req.Password != nil {
|
||||||
target.Password = *req.Password
|
target.Password = *req.Password
|
||||||
}
|
}
|
||||||
|
if req.Insecure != nil {
|
||||||
|
target.Insecure = *req.Insecure
|
||||||
|
}
|
||||||
|
|
||||||
t.Validate(target)
|
t.Validate(target)
|
||||||
|
|
||||||
|
|
|
@ -276,15 +276,6 @@ func OnlyAdminCreateProject() (bool, error) {
|
||||||
return cfg[common.ProjectCreationRestriction].(string) == common.ProCrtRestrAdmOnly, nil
|
return cfg[common.ProjectCreationRestriction].(string) == common.ProCrtRestrAdmOnly, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
// VerifyRemoteCert returns bool value.
|
|
||||||
func VerifyRemoteCert() (bool, error) {
|
|
||||||
cfg, err := mg.Get()
|
|
||||||
if err != nil {
|
|
||||||
return true, err
|
|
||||||
}
|
|
||||||
return cfg[common.VerifyRemoteCert].(bool), nil
|
|
||||||
}
|
|
||||||
|
|
||||||
// Email returns email server settings
|
// Email returns email server settings
|
||||||
func Email() (*models.Email, error) {
|
func Email() (*models.Email, error) {
|
||||||
cfg, err := mg.Get()
|
cfg, err := mg.Get()
|
||||||
|
|
|
@ -108,10 +108,6 @@ func TestConfig(t *testing.T) {
|
||||||
t.Fatalf("failed to get onldy admin create project: %v", err)
|
t.Fatalf("failed to get onldy admin create project: %v", err)
|
||||||
}
|
}
|
||||||
|
|
||||||
if _, err := VerifyRemoteCert(); err != nil {
|
|
||||||
t.Fatalf("failed to get verify remote cert: %v", err)
|
|
||||||
}
|
|
||||||
|
|
||||||
if _, err := Email(); err != nil {
|
if _, err := Email(); err != nil {
|
||||||
t.Fatalf("failed to get email settings: %v", err)
|
t.Fatalf("failed to get email settings: %v", err)
|
||||||
}
|
}
|
||||||
|
|
|
@ -38,6 +38,10 @@ export const CREATE_EDIT_ENDPOINT_TEMPLATE: string = `
|
||||||
<label for="destination_password" class="col-md-4 form-group-label-override">{{ 'DESTINATION.PASSWORD' | translate }}</label>
|
<label for="destination_password" class="col-md-4 form-group-label-override">{{ 'DESTINATION.PASSWORD' | translate }}</label>
|
||||||
<input type="password" class="col-md-8" id="destination_password" [disabled]="testOngoing" [readonly]="!editable" [(ngModel)]="target.password" size="20" name="password" #password="ngModel" (focus)="clearPassword($event)">
|
<input type="password" class="col-md-8" id="destination_password" [disabled]="testOngoing" [readonly]="!editable" [(ngModel)]="target.password" size="20" name="password" #password="ngModel" (focus)="clearPassword($event)">
|
||||||
</div>
|
</div>
|
||||||
|
<div class="form-group">
|
||||||
|
<label for="destination_insecure" class="col-md-4 form-group-label-override">{{'CONFIG.VERIFY_REMOTE_CERT' | translate }}</label>
|
||||||
|
<clr-checkbox #insecure class="col-md-8" name="insecure" id="destination_insecure" [(ngModel)]="target.insecure"></clr-checkbox>
|
||||||
|
</div>
|
||||||
<div class="form-group">
|
<div class="form-group">
|
||||||
<label for="spin" class="col-md-4"></label>
|
<label for="spin" class="col-md-4"></label>
|
||||||
<span class="col-md-8 spinner spinner-inline" [hidden]="!inProgress"></span>
|
<span class="col-md-8 spinner spinner-inline" [hidden]="!inProgress"></span>
|
||||||
|
|
|
@ -21,6 +21,7 @@ describe('CreateEditEndpointComponent (inline template)', () => {
|
||||||
"name": "target_01",
|
"name": "target_01",
|
||||||
"username": "admin",
|
"username": "admin",
|
||||||
"password": "",
|
"password": "",
|
||||||
|
"insecure": false,
|
||||||
"type": 0
|
"type": 0
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
|
@ -91,7 +91,8 @@ export class CreateEditEndpointComponent implements AfterViewChecked, OnDestroy
|
||||||
(this.target.endpoint && this.target.endpoint.trim() !== "") ||
|
(this.target.endpoint && this.target.endpoint.trim() !== "") ||
|
||||||
(this.target.name && this.target.name.trim() !== "") ||
|
(this.target.name && this.target.name.trim() !== "") ||
|
||||||
(this.target.username && this.target.username.trim() !== "") ||
|
(this.target.username && this.target.username.trim() !== "") ||
|
||||||
(this.target.password && this.target.password.trim() !== ""));
|
(this.target.password && this.target.password.trim() !== "")) ||
|
||||||
|
this.target.insecure;
|
||||||
} else {
|
} else {
|
||||||
//Edit
|
//Edit
|
||||||
return !compareValue(this.target, this.initVal);
|
return !compareValue(this.target, this.initVal);
|
||||||
|
@ -104,26 +105,29 @@ export class CreateEditEndpointComponent implements AfterViewChecked, OnDestroy
|
||||||
this.targetForm &&
|
this.targetForm &&
|
||||||
this.targetForm.valid &&
|
this.targetForm.valid &&
|
||||||
this.editable &&
|
this.editable &&
|
||||||
(this.targetNameHasChanged || this.endpointHasChanged);
|
(this.targetNameHasChanged || this.endpointHasChanged || this.checkboxHasChanged);
|
||||||
}
|
}
|
||||||
|
|
||||||
public get inProgress(): boolean {
|
public get inProgress(): boolean {
|
||||||
return this.onGoing || this.testOngoing;
|
return this.onGoing || this.testOngoing;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public get checkboxHasChanged(): boolean {
|
||||||
|
return (this.target.insecure !== this.initVal.insecure) ? true : false;
|
||||||
|
}
|
||||||
ngOnDestroy(): void {
|
ngOnDestroy(): void {
|
||||||
if (this.valueChangesSub) {
|
if (this.valueChangesSub) {
|
||||||
this.valueChangesSub.unsubscribe();
|
this.valueChangesSub.unsubscribe();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
initEndpoint(): Endpoint {
|
initEndpoint(): Endpoint {
|
||||||
return {
|
return {
|
||||||
endpoint: "",
|
endpoint: "",
|
||||||
name: "",
|
name: "",
|
||||||
username: "",
|
username: "",
|
||||||
password: "",
|
password: "",
|
||||||
|
insecure: false,
|
||||||
type: 0
|
type: 0
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
@ -275,20 +279,28 @@ export class CreateEditEndpointComponent implements AfterViewChecked, OnDestroy
|
||||||
if (this.onGoing) {
|
if (this.onGoing) {
|
||||||
return;//Avoid duplicated submitting
|
return;//Avoid duplicated submitting
|
||||||
}
|
}
|
||||||
if (!(this.targetNameHasChanged || this.endpointHasChanged)) {
|
if (!(this.targetNameHasChanged || this.endpointHasChanged || this.checkboxHasChanged)) {
|
||||||
return;//Avoid invalid submitting
|
return;//Avoid invalid submitting
|
||||||
}
|
}
|
||||||
let payload: Endpoint = this.initEndpoint();
|
let payload: Endpoint = this.initEndpoint();
|
||||||
if (this.targetNameHasChanged) {
|
if (this.targetNameHasChanged) {
|
||||||
payload.name = this.target.name;
|
payload.name = this.target.name;
|
||||||
delete payload.endpoint;
|
}else {
|
||||||
|
delete payload.name;
|
||||||
}
|
}
|
||||||
if (this.endpointHasChanged) {
|
if (this.endpointHasChanged) {
|
||||||
payload.endpoint = this.target.endpoint;
|
payload.endpoint = this.target.endpoint;
|
||||||
payload.username = this.target.username;
|
payload.username = this.target.username;
|
||||||
payload.password = this.target.password;
|
payload.password = this.target.password;
|
||||||
delete payload.name;
|
}else {
|
||||||
|
delete payload.endpoint;
|
||||||
}
|
}
|
||||||
|
if (this.checkboxHasChanged) {
|
||||||
|
payload.insecure = this.target.insecure;
|
||||||
|
}else {
|
||||||
|
delete payload.insecure;
|
||||||
|
}
|
||||||
|
|
||||||
if (!this.target.id) { return; }
|
if (!this.target.id) { return; }
|
||||||
|
|
||||||
this.onGoing = true;
|
this.onGoing = true;
|
||||||
|
@ -317,7 +329,7 @@ export class CreateEditEndpointComponent implements AfterViewChecked, OnDestroy
|
||||||
|
|
||||||
handleErrorMessageKey(status: number): string {
|
handleErrorMessageKey(status: number): string {
|
||||||
switch (status) {
|
switch (status) {
|
||||||
case 409: this
|
case 409:
|
||||||
return 'DESTINATION.CONFLICT_NAME';
|
return 'DESTINATION.CONFLICT_NAME';
|
||||||
case 400:
|
case 400:
|
||||||
return 'DESTINATION.INVALID_NAME';
|
return 'DESTINATION.INVALID_NAME';
|
||||||
|
@ -356,7 +368,7 @@ export class CreateEditEndpointComponent implements AfterViewChecked, OnDestroy
|
||||||
keyNumber++;
|
keyNumber++;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if (keyNumber !== 4) {
|
if (keyNumber !== 5) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -110,6 +110,7 @@ describe('CreateEditRuleComponent (inline template)', ()=>{
|
||||||
"name": "target_01",
|
"name": "target_01",
|
||||||
"username": "admin",
|
"username": "admin",
|
||||||
"password": "",
|
"password": "",
|
||||||
|
"insecure": false,
|
||||||
"type": 0
|
"type": 0
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
|
@ -118,6 +119,7 @@ describe('CreateEditRuleComponent (inline template)', ()=>{
|
||||||
"name": "target_02",
|
"name": "target_02",
|
||||||
"username": "AAA",
|
"username": "AAA",
|
||||||
"password": "",
|
"password": "",
|
||||||
|
"insecure": false,
|
||||||
"type": 0
|
"type": 0
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
|
@ -126,6 +128,7 @@ describe('CreateEditRuleComponent (inline template)', ()=>{
|
||||||
"name": "target_03",
|
"name": "target_03",
|
||||||
"username": "admin",
|
"username": "admin",
|
||||||
"password": "",
|
"password": "",
|
||||||
|
"insecure": false,
|
||||||
"type": 0
|
"type": 0
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
|
@ -134,6 +137,7 @@ describe('CreateEditRuleComponent (inline template)', ()=>{
|
||||||
"name": "target_04",
|
"name": "target_04",
|
||||||
"username": "",
|
"username": "",
|
||||||
"password": "",
|
"password": "",
|
||||||
|
"insecure": true,
|
||||||
"type": 0
|
"type": 0
|
||||||
}
|
}
|
||||||
];
|
];
|
||||||
|
|
|
@ -123,6 +123,7 @@ export class CreateEditRuleComponent implements AfterViewChecked {
|
||||||
name: '',
|
name: '',
|
||||||
username: '',
|
username: '',
|
||||||
password: '',
|
password: '',
|
||||||
|
insecure: false,
|
||||||
type: 0
|
type: 0
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
|
@ -19,6 +19,7 @@ export const ENDPOINT_TEMPLATE: string = `
|
||||||
<clr-datagrid [clrDgLoading]="loading">
|
<clr-datagrid [clrDgLoading]="loading">
|
||||||
<clr-dg-column [clrDgField]="'name'">{{'DESTINATION.NAME' | translate}}</clr-dg-column>
|
<clr-dg-column [clrDgField]="'name'">{{'DESTINATION.NAME' | translate}}</clr-dg-column>
|
||||||
<clr-dg-column [clrDgField]="'endpoint'">{{'DESTINATION.URL' | translate}}</clr-dg-column>
|
<clr-dg-column [clrDgField]="'endpoint'">{{'DESTINATION.URL' | translate}}</clr-dg-column>
|
||||||
|
<clr-dg-column [clrDgField]="'insecure'">{{'CONFIG.VERIFY_REMOTE_CERT' | translate }}</clr-dg-column>
|
||||||
<clr-dg-column [clrDgSortBy]="creationTimeComparator">{{'DESTINATION.CREATION_TIME' | translate}}</clr-dg-column>
|
<clr-dg-column [clrDgSortBy]="creationTimeComparator">{{'DESTINATION.CREATION_TIME' | translate}}</clr-dg-column>
|
||||||
<clr-dg-placeholder>{{'DESTINATION.PLACEHOLDER' | translate }}</clr-dg-placeholder>
|
<clr-dg-placeholder>{{'DESTINATION.PLACEHOLDER' | translate }}</clr-dg-placeholder>
|
||||||
<clr-dg-row *clrDgItems="let t of targets" [clrDgItem]='t'>
|
<clr-dg-row *clrDgItems="let t of targets" [clrDgItem]='t'>
|
||||||
|
@ -28,6 +29,9 @@ export const ENDPOINT_TEMPLATE: string = `
|
||||||
</clr-dg-action-overflow>
|
</clr-dg-action-overflow>
|
||||||
<clr-dg-cell>{{t.name}}</clr-dg-cell>
|
<clr-dg-cell>{{t.name}}</clr-dg-cell>
|
||||||
<clr-dg-cell>{{t.endpoint}}</clr-dg-cell>
|
<clr-dg-cell>{{t.endpoint}}</clr-dg-cell>
|
||||||
|
<clr-dg-cell>
|
||||||
|
<clr-checkbox name="insecure" [clrChecked]="t.insecure"> </clr-checkbox>
|
||||||
|
</clr-dg-cell>
|
||||||
<clr-dg-cell>{{t.creation_time | date: 'short'}}</clr-dg-cell>
|
<clr-dg-cell>{{t.creation_time | date: 'short'}}</clr-dg-cell>
|
||||||
</clr-dg-row>
|
</clr-dg-row>
|
||||||
<clr-dg-footer>
|
<clr-dg-footer>
|
||||||
|
|
|
@ -25,6 +25,7 @@ describe('EndpointComponent (inline template)', () => {
|
||||||
"name": "target_01",
|
"name": "target_01",
|
||||||
"username": "admin",
|
"username": "admin",
|
||||||
"password": "",
|
"password": "",
|
||||||
|
"insecure": true,
|
||||||
"type": 0
|
"type": 0
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
|
@ -33,6 +34,7 @@ describe('EndpointComponent (inline template)', () => {
|
||||||
"name": "target_02",
|
"name": "target_02",
|
||||||
"username": "AAA",
|
"username": "AAA",
|
||||||
"password": "",
|
"password": "",
|
||||||
|
"insecure": false,
|
||||||
"type": 0
|
"type": 0
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
|
@ -41,6 +43,7 @@ describe('EndpointComponent (inline template)', () => {
|
||||||
"name": "target_03",
|
"name": "target_03",
|
||||||
"username": "admin",
|
"username": "admin",
|
||||||
"password": "",
|
"password": "",
|
||||||
|
"insecure": false,
|
||||||
"type": 0
|
"type": 0
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
|
@ -49,6 +52,7 @@ describe('EndpointComponent (inline template)', () => {
|
||||||
"name": "target_04",
|
"name": "target_04",
|
||||||
"username": "",
|
"username": "",
|
||||||
"password": "",
|
"password": "",
|
||||||
|
"insecure": false,
|
||||||
"type": 0
|
"type": 0
|
||||||
}
|
}
|
||||||
];
|
];
|
||||||
|
@ -59,6 +63,7 @@ describe('EndpointComponent (inline template)', () => {
|
||||||
"name": "target_01",
|
"name": "target_01",
|
||||||
"username": "admin",
|
"username": "admin",
|
||||||
"password": "",
|
"password": "",
|
||||||
|
"insecure": false,
|
||||||
"type": 0
|
"type": 0
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
|
@ -69,6 +69,7 @@ export class EndpointComponent implements OnInit {
|
||||||
name: "",
|
name: "",
|
||||||
username: "",
|
username: "",
|
||||||
password: "",
|
password: "",
|
||||||
|
insecure: false,
|
||||||
type: 0
|
type: 0
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
|
@ -107,6 +107,7 @@ describe('Replication Component (inline template)', ()=>{
|
||||||
"name": "target_01",
|
"name": "target_01",
|
||||||
"username": "admin",
|
"username": "admin",
|
||||||
"password": "",
|
"password": "",
|
||||||
|
"insecure": false,
|
||||||
"type": 0
|
"type": 0
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
|
@ -115,6 +116,7 @@ describe('Replication Component (inline template)', ()=>{
|
||||||
"name": "target_02",
|
"name": "target_02",
|
||||||
"username": "AAA",
|
"username": "AAA",
|
||||||
"password": "",
|
"password": "",
|
||||||
|
"insecure": false,
|
||||||
"type": 0
|
"type": 0
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
|
@ -123,6 +125,7 @@ describe('Replication Component (inline template)', ()=>{
|
||||||
"name": "target_03",
|
"name": "target_03",
|
||||||
"username": "admin",
|
"username": "admin",
|
||||||
"password": "",
|
"password": "",
|
||||||
|
"insecure": false,
|
||||||
"type": 0
|
"type": 0
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
|
@ -131,6 +134,7 @@ describe('Replication Component (inline template)', ()=>{
|
||||||
"name": "target_04",
|
"name": "target_04",
|
||||||
"username": "",
|
"username": "",
|
||||||
"password": "",
|
"password": "",
|
||||||
|
"insecure": false,
|
||||||
"type": 0
|
"type": 0
|
||||||
}
|
}
|
||||||
];
|
];
|
||||||
|
|
|
@ -73,6 +73,7 @@ export interface Endpoint extends Base {
|
||||||
name: string;
|
name: string;
|
||||||
username?: string;
|
username?: string;
|
||||||
password?: string;
|
password?: string;
|
||||||
|
insecure: boolean;
|
||||||
type: number;
|
type: number;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -31,7 +31,7 @@
|
||||||
"clarity-icons": "^0.9.8",
|
"clarity-icons": "^0.9.8",
|
||||||
"clarity-ui": "^0.9.8",
|
"clarity-ui": "^0.9.8",
|
||||||
"core-js": "^2.4.1",
|
"core-js": "^2.4.1",
|
||||||
"harbor-ui": "0.4.83",
|
"harbor-ui": "0.4.85",
|
||||||
"intl": "^1.2.5",
|
"intl": "^1.2.5",
|
||||||
"mutationobserver-shim": "^0.3.2",
|
"mutationobserver-shim": "^0.3.2",
|
||||||
"ngx-cookie": "^1.0.0",
|
"ngx-cookie": "^1.0.0",
|
||||||
|
|
|
@ -6,9 +6,6 @@
|
||||||
<li role="presentation" class="nav-item">
|
<li role="presentation" class="nav-item">
|
||||||
<button id="config-auth" class="btn btn-link nav-link active" aria-controls="authentication" [class.active]='isCurrentTabLink("config-auth")' type="button" (click)='tabLinkClick("config-auth")'>{{'CONFIG.AUTH' | translate }}</button>
|
<button id="config-auth" class="btn btn-link nav-link active" aria-controls="authentication" [class.active]='isCurrentTabLink("config-auth")' type="button" (click)='tabLinkClick("config-auth")'>{{'CONFIG.AUTH' | translate }}</button>
|
||||||
</li>
|
</li>
|
||||||
<li role="presentation" class="nav-item">
|
|
||||||
<button id="config-replication" class="btn btn-link nav-link" aria-controls="replication" [class.active]='isCurrentTabLink("config-replication")' type="button" (click)='tabLinkClick("config-replication")'>{{'CONFIG.REPLICATION' | translate }}</button>
|
|
||||||
</li>
|
|
||||||
<li role="presentation" class="nav-item">
|
<li role="presentation" class="nav-item">
|
||||||
<button id="config-email" class="btn btn-link nav-link" aria-controls="email" [class.active]='isCurrentTabLink("config-email")' type="button" (click)='tabLinkClick("config-email")'>{{'CONFIG.EMAIL' | translate }}</button>
|
<button id="config-email" class="btn btn-link nav-link" aria-controls="email" [class.active]='isCurrentTabLink("config-email")' type="button" (click)='tabLinkClick("config-email")'>{{'CONFIG.EMAIL' | translate }}</button>
|
||||||
</li>
|
</li>
|
||||||
|
@ -22,9 +19,6 @@
|
||||||
<section id="authentication" role="tabpanel" aria-labelledby="config-auth" [hidden]='!isCurrentTabContent("authentication")'>
|
<section id="authentication" role="tabpanel" aria-labelledby="config-auth" [hidden]='!isCurrentTabContent("authentication")'>
|
||||||
<config-auth [ldapConfig]="allConfig"></config-auth>
|
<config-auth [ldapConfig]="allConfig"></config-auth>
|
||||||
</section>
|
</section>
|
||||||
<section id="replication" role="tabpanel" aria-labelledby="config-replication" [hidden]='!isCurrentTabContent("replication")'>
|
|
||||||
<replication-config [(replicationConfig)]="allConfig"></replication-config>
|
|
||||||
</section>
|
|
||||||
<section id="email" role="tabpanel" aria-labelledby="config-email" [hidden]='!isCurrentTabContent("email")'>
|
<section id="email" role="tabpanel" aria-labelledby="config-email" [hidden]='!isCurrentTabContent("email")'>
|
||||||
<config-email [mailConfig]="allConfig"></config-email>
|
<config-email [mailConfig]="allConfig"></config-email>
|
||||||
</section>
|
</section>
|
||||||
|
|
|
@ -30,7 +30,6 @@ import {
|
||||||
Configuration,
|
Configuration,
|
||||||
StringValueItem,
|
StringValueItem,
|
||||||
ComplexValueItem,
|
ComplexValueItem,
|
||||||
ReplicationConfigComponent,
|
|
||||||
SystemSettingsComponent,
|
SystemSettingsComponent,
|
||||||
VulnerabilityConfigComponent,
|
VulnerabilityConfigComponent,
|
||||||
ClairDBStatus
|
ClairDBStatus
|
||||||
|
@ -59,7 +58,6 @@ export class ConfigurationComponent implements OnInit, OnDestroy {
|
||||||
testingMailOnGoing: boolean = false;
|
testingMailOnGoing: boolean = false;
|
||||||
testingLDAPOnGoing: boolean = false;
|
testingLDAPOnGoing: boolean = false;
|
||||||
|
|
||||||
@ViewChild(ReplicationConfigComponent) replicationConfig: ReplicationConfigComponent;
|
|
||||||
@ViewChild(SystemSettingsComponent) systemSettingsConfig: SystemSettingsComponent;
|
@ViewChild(SystemSettingsComponent) systemSettingsConfig: SystemSettingsComponent;
|
||||||
@ViewChild(VulnerabilityConfigComponent) vulnerabilityConfig: VulnerabilityConfigComponent;
|
@ViewChild(VulnerabilityConfigComponent) vulnerabilityConfig: VulnerabilityConfigComponent;
|
||||||
@ViewChild(ConfigurationEmailComponent) mailConfig: ConfigurationEmailComponent;
|
@ViewChild(ConfigurationEmailComponent) mailConfig: ConfigurationEmailComponent;
|
||||||
|
@ -170,9 +168,7 @@ export class ConfigurationComponent implements OnInit, OnDestroy {
|
||||||
}
|
}
|
||||||
|
|
||||||
public isValid(): boolean {
|
public isValid(): boolean {
|
||||||
return this.replicationConfig &&
|
return this.systemSettingsConfig &&
|
||||||
this.replicationConfig.isValid &&
|
|
||||||
this.systemSettingsConfig &&
|
|
||||||
this.systemSettingsConfig.isValid &&
|
this.systemSettingsConfig.isValid &&
|
||||||
this.mailConfig &&
|
this.mailConfig &&
|
||||||
this.mailConfig.isValid() &&
|
this.mailConfig.isValid() &&
|
||||||
|
|
|
@ -57,19 +57,25 @@ if [[ $DRONE_BRANCH == "master" || $DRONE_BRANCH == *"refs/tags"* || $DRONE_BRAN
|
||||||
echo "Package Harbor build."
|
echo "Package Harbor build."
|
||||||
pybot --removekeywords TAG:secret --include Bundle tests/robot-cases/Group0-Distro-Harbor
|
pybot --removekeywords TAG:secret --include Bundle tests/robot-cases/Group0-Distro-Harbor
|
||||||
echo "Running full CI for $DRONE_BUILD_EVENT on $DRONE_BRANCH"
|
echo "Running full CI for $DRONE_BUILD_EVENT on $DRONE_BRANCH"
|
||||||
pybot -v ip:$container_ip --removekeywords TAG:secret --include BAT tests/robot-cases/Group0-BAT
|
|
||||||
upload_latest_build=true
|
upload_latest_build=true
|
||||||
|
pybot -v ip:$container_ip --removekeywords TAG:secret --include BAT tests/robot-cases/Group0-BAT
|
||||||
elif (echo $buildinfo | grep -q "\[Specific CI="); then
|
elif (echo $buildinfo | grep -q "\[Specific CI="); then
|
||||||
buildtype=$(echo $buildinfo | grep "\[Specific CI=")
|
buildtype=$(echo $buildinfo | grep "\[Specific CI=")
|
||||||
testsuite=$(echo $buildtype | awk -v FS="(=|])" '{print $2}')
|
testsuite=$(echo $buildtype | awk -v FS="(=|])" '{print $2}')
|
||||||
pybot -v ip:$container_ip --removekeywords TAG:secret --suite $testsuite --suite Regression tests/robot-cases
|
pybot -v ip:$container_ip --removekeywords TAG:secret --suite $testsuite --suite Regression tests/robot-cases
|
||||||
elif (echo $buildinfo | grep -q "\[Full CI\]"); then
|
elif (echo $buildinfo | grep -q "\[Full CI\]"); then
|
||||||
upload_build=true
|
|
||||||
pybot -v ip:$container_ip --removekeywords TAG:secret --exclude skip tests/robot-cases
|
pybot -v ip:$container_ip --removekeywords TAG:secret --exclude skip tests/robot-cases
|
||||||
elif (echo $buildinfo | grep -q "\[Skip CI\]"); then
|
elif (echo $buildinfo | grep -q "\[Skip CI\]"); then
|
||||||
echo "Skip CI."
|
echo "Skip CI."
|
||||||
|
elif (echo $buildinfo | grep -q "\[Upload Build\]"); then
|
||||||
|
upload_latest_build=true
|
||||||
|
upload_build=true
|
||||||
|
echo "Package Harbor build."
|
||||||
|
pybot --removekeywords TAG:secret --include Bundle tests/robot-cases/Group0-Distro-Harbor
|
||||||
|
echo "Running full CI for $DRONE_BUILD_EVENT on $DRONE_BRANCH"
|
||||||
|
pybot -v ip:$container_ip --removekeywords TAG:secret --include BAT tests/robot-cases/Group0-BAT
|
||||||
else
|
else
|
||||||
# default mode is BAT.
|
# default mode is BAT.
|
||||||
pybot -v ip:$container_ip --removekeywords TAG:secret --include BAT tests/robot-cases/Group0-BAT
|
pybot -v ip:$container_ip --removekeywords TAG:secret --include BAT tests/robot-cases/Group0-BAT
|
||||||
fi
|
fi
|
||||||
|
|
||||||
|
@ -90,6 +96,13 @@ else
|
||||||
echo "No log output file to upload"
|
echo "No log output file to upload"
|
||||||
fi
|
fi
|
||||||
|
|
||||||
|
## --------------------------------------------- Upload Harbor Build File ---------------------------------------
|
||||||
|
if [ $upload_build == true ] && [ $rc -eq 0 ]; then
|
||||||
|
harbor_build_bundle=$(basename harbor-offline-installer-*.tgz)
|
||||||
|
gsutil cp $harbor_build_bundle gs://harbor-builds
|
||||||
|
gsutil -D setacl public-read gs://harbor-builds/$harbor_build_bundle &> /dev/null
|
||||||
|
fi
|
||||||
|
|
||||||
## --------------------------------------------- Upload Harbor Latest Build File ---------------------------------------
|
## --------------------------------------------- Upload Harbor Latest Build File ---------------------------------------
|
||||||
if [ $upload_latest_build == true ] && [ $rc -eq 0 ]; then
|
if [ $upload_latest_build == true ] && [ $rc -eq 0 ]; then
|
||||||
echo "update latest build file."
|
echo "update latest build file."
|
||||||
|
|
|
@ -102,7 +102,7 @@ Project Creation Should Not Display
|
||||||
Switch To System Settings
|
Switch To System Settings
|
||||||
Sleep 1
|
Sleep 1
|
||||||
Click Element xpath=//clr-main-container//nav//ul/li[3]
|
Click Element xpath=//clr-main-container//nav//ul/li[3]
|
||||||
Click Element xpath=//config//ul/li[4]
|
Click Element xpath=//*[@id="config-system"]
|
||||||
|
|
||||||
Modify Token Expiration
|
Modify Token Expiration
|
||||||
[Arguments] ${minutes}
|
[Arguments] ${minutes}
|
||||||
|
|
|
@ -176,23 +176,6 @@ Test Case - Edit Self-Registration
|
||||||
Enable Self Reg
|
Enable Self Reg
|
||||||
Close Browser
|
Close Browser
|
||||||
|
|
||||||
Test Case - Edit Verify Remote Cert
|
|
||||||
Init Chrome Driver
|
|
||||||
Sign In Harbor ${HARBOR_URL} %{HARBOR_ADMIN} %{HARBOR_PASSWORD}
|
|
||||||
|
|
||||||
Switch To System Replication
|
|
||||||
Check Verify Remote Cert
|
|
||||||
|
|
||||||
Logout Harbor
|
|
||||||
Sign In Harbor ${HARBOR_URL} %{HARBOR_ADMIN} %{HARBOR_PASSWORD}
|
|
||||||
|
|
||||||
Switch To System Replication
|
|
||||||
Should Verify Remote Cert Be Enabled
|
|
||||||
|
|
||||||
#restore setting
|
|
||||||
Check Verify Remote Cert
|
|
||||||
Close Browser
|
|
||||||
|
|
||||||
Test Case - Edit Email Settings
|
Test Case - Edit Email Settings
|
||||||
Init Chrome Driver
|
Init Chrome Driver
|
||||||
Sign In Harbor ${HARBOR_URL} %{HARBOR_ADMIN} %{HARBOR_PASSWORD}
|
Sign In Harbor ${HARBOR_URL} %{HARBOR_ADMIN} %{HARBOR_PASSWORD}
|
||||||
|
|
|
@ -1,5 +1,4 @@
|
||||||
#!/bin/sh
|
#!/bin/sh
|
||||||
|
|
||||||
set -e
|
set -e
|
||||||
|
|
||||||
if docker ps --filter "status=restarting" | grep 'vmware'; then
|
if docker ps --filter "status=restarting" | grep 'vmware'; then
|
||||||
|
|
|
@ -1,16 +1,11 @@
|
||||||
FROM mysql:5.6
|
FROM vmware/mariadb-photon:10.2.8
|
||||||
|
|
||||||
MAINTAINER bhe@vmware.com
|
RUN tdnf distro-sync || echo \
|
||||||
|
&& tdnf install -y mariadb-devel python2 python2-devel python-pip gcc\
|
||||||
RUN sed -i -e 's/us.archive.ubuntu.com/archive.ubuntu.com/g' /etc/apt/sources.list
|
linux-api-headers glibc-devel binutils zlib-devel openssl-devel \
|
||||||
|
&& pip install mysqlclient alembic \
|
||||||
RUN apt-get update
|
&& tdnf clean all \
|
||||||
|
&& mkdir -p /harbor-migration
|
||||||
RUN apt-get install -y curl python python-pip git python-mysqldb
|
|
||||||
|
|
||||||
RUN pip install alembic
|
|
||||||
|
|
||||||
RUN mkdir -p /harbor-migration
|
|
||||||
|
|
||||||
WORKDIR /harbor-migration
|
WORKDIR /harbor-migration
|
||||||
|
|
||||||
|
|
|
@ -30,7 +30,7 @@ script_location = migration_harbor
|
||||||
# are written from script.py.mako
|
# are written from script.py.mako
|
||||||
# output_encoding = utf-8
|
# output_encoding = utf-8
|
||||||
|
|
||||||
sqlalchemy.url = mysql://$DB_USR:$DB_PWD@localhost:3306/registry
|
sqlalchemy.url = mysql://$DB_USR:$DB_PWD@localhost:3306/registry?unix_socket=/var/run/mysqld/mysqld.sock
|
||||||
|
|
||||||
# Logging configuration
|
# Logging configuration
|
||||||
[loggers]
|
[loggers]
|
||||||
|
|
|
@ -54,4 +54,5 @@ Changelog for harbor database schema
|
||||||
|
|
||||||
- create table `project_metadata`
|
- create table `project_metadata`
|
||||||
- insert data into table `project_metadata`
|
- insert data into table `project_metadata`
|
||||||
- delete column `public` from table `project`
|
- delete column `public` from table `project`
|
||||||
|
- add column `insecure` to table `replication_target`
|
||||||
|
|
|
@ -8,8 +8,6 @@ fi
|
||||||
|
|
||||||
source ./alembic.tpl > ./alembic.ini
|
source ./alembic.tpl > ./alembic.ini
|
||||||
|
|
||||||
WAITTIME=60
|
|
||||||
|
|
||||||
DBCNF="-hlocalhost -u${DB_USR}"
|
DBCNF="-hlocalhost -u${DB_USR}"
|
||||||
|
|
||||||
#prevent shell to print insecure message
|
#prevent shell to print insecure message
|
||||||
|
@ -44,24 +42,21 @@ fi
|
||||||
echo 'Trying to start mysql server...'
|
echo 'Trying to start mysql server...'
|
||||||
DBRUN=0
|
DBRUN=0
|
||||||
mysqld &
|
mysqld &
|
||||||
for i in $(seq 1 $WAITTIME); do
|
for i in {60..0}; do
|
||||||
echo "$(/usr/sbin/service mysql status)"
|
mysqladmin -u$DB_USR -p$DB_PWD processlist >/dev/null 2>&1
|
||||||
if [[ "$(/usr/sbin/service mysql status)" =~ "not running" ]]; then
|
if [ $? = 0 ]; then
|
||||||
sleep 1
|
|
||||||
else
|
|
||||||
DBRUN=1
|
|
||||||
break
|
break
|
||||||
fi
|
fi
|
||||||
|
echo 'Waiting for MySQL start...'
|
||||||
|
sleep 1
|
||||||
done
|
done
|
||||||
|
if [ "$i" = 0 ]; then
|
||||||
if [[ $DBRUN -eq 0 ]]; then
|
|
||||||
echo "timeout. Can't run mysql server."
|
echo "timeout. Can't run mysql server."
|
||||||
if [[ $1 = "test" ]]; then
|
if [[ $1 = "test" ]]; then
|
||||||
echo "test failed."
|
echo "test failed."
|
||||||
fi
|
fi
|
||||||
exit 1
|
exit 1
|
||||||
fi
|
fi
|
||||||
|
|
||||||
if [[ $1 = "test" ]]; then
|
if [[ $1 = "test" ]]; then
|
||||||
echo "test passed."
|
echo "test passed."
|
||||||
exit 0
|
exit 0
|
||||||
|
|
Loading…
Reference in New Issue
Block a user