Merge remote-tracking branch 'upstream/master' into batchDelection
4
Makefile
@ -284,9 +284,11 @@ modify_composefile: modify_composefile_notary modify_composefile_clair
|
|||||||
@cp $(DOCKERCOMPOSEFILEPATH)/$(DOCKERCOMPOSETPLFILENAME) $(DOCKERCOMPOSEFILEPATH)/$(DOCKERCOMPOSEFILENAME)
|
@cp $(DOCKERCOMPOSEFILEPATH)/$(DOCKERCOMPOSETPLFILENAME) $(DOCKERCOMPOSEFILEPATH)/$(DOCKERCOMPOSEFILENAME)
|
||||||
@cp $(DOCKERCOMPOSEFILEPATH)/ha/$(DOCKERCOMPOSETPLFILENAME) $(DOCKERCOMPOSEFILEPATH)/ha/$(DOCKERCOMPOSEFILENAME)
|
@cp $(DOCKERCOMPOSEFILEPATH)/ha/$(DOCKERCOMPOSETPLFILENAME) $(DOCKERCOMPOSEFILEPATH)/ha/$(DOCKERCOMPOSEFILENAME)
|
||||||
@$(SEDCMD) -i 's/__version__/$(VERSIONTAG)/g' $(DOCKERCOMPOSEFILEPATH)/ha/$(DOCKERCOMPOSEFILENAME)
|
@$(SEDCMD) -i 's/__version__/$(VERSIONTAG)/g' $(DOCKERCOMPOSEFILEPATH)/ha/$(DOCKERCOMPOSEFILENAME)
|
||||||
@$(SEDCMD) -i 's/__reg_version__/$(REGISTRYVERSION)-$(VERSIONTAG)/g' $(DOCKERCOMPOSEFILEPATH)/$(DOCKERCOMPOSEFILENAME)
|
|
||||||
@$(SEDCMD) -i 's/__version__/$(VERSIONTAG)/g' $(DOCKERCOMPOSEFILEPATH)/$(DOCKERCOMPOSEFILENAME)
|
@$(SEDCMD) -i 's/__version__/$(VERSIONTAG)/g' $(DOCKERCOMPOSEFILEPATH)/$(DOCKERCOMPOSEFILENAME)
|
||||||
|
@$(SEDCMD) -i 's/__reg_version__/$(REGISTRYVERSION)-$(VERSIONTAG)/g' $(DOCKERCOMPOSEFILEPATH)/$(DOCKERCOMPOSEFILENAME)
|
||||||
|
@$(SEDCMD) -i 's/__reg_version__/$(REGISTRYVERSION)-$(VERSIONTAG)/g' $(DOCKERCOMPOSEFILEPATH)/ha/$(DOCKERCOMPOSEFILENAME)
|
||||||
@$(SEDCMD) -i 's/__nginx_version__/$(NGINXVERSION)/g' $(DOCKERCOMPOSEFILEPATH)/$(DOCKERCOMPOSEFILENAME)
|
@$(SEDCMD) -i 's/__nginx_version__/$(NGINXVERSION)/g' $(DOCKERCOMPOSEFILEPATH)/$(DOCKERCOMPOSEFILENAME)
|
||||||
|
@$(SEDCMD) -i 's/__nginx_version__/$(NGINXVERSION)/g' $(DOCKERCOMPOSEFILEPATH)/ha/$(DOCKERCOMPOSEFILENAME)
|
||||||
|
|
||||||
modify_composefile_notary:
|
modify_composefile_notary:
|
||||||
@echo "preparing docker-compose notary file..."
|
@echo "preparing docker-compose notary file..."
|
||||||
|
104
docs/expand_hard_disk.md
Normal file
@ -0,0 +1,104 @@
|
|||||||
|
# Expand the Hard Disk of Virtual Appliance
|
||||||
|
|
||||||
|
If you install Harbor with OVA, the persistent data(such as images and database) is stored in a hard disk which is mounted on directory "/data", and the default size is 60GB. As more and more images are pushed into it, the capacity may not meet your requirements.
|
||||||
|
|
||||||
|
You can check the space on Harbor web UI by clicking on **Projects**:
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
![lvm](img/lvm/check_on_ui_01.png)
|
||||||
|
|
||||||
|
If your free space is running out, you can expand the size of the hard disk by the following steps:
|
||||||
|
|
||||||
|
1. Add New Hard Disk to VM
|
||||||
|
|
||||||
|
(1) Log in vSphere web client. Power off Harbor's virtual appliance.
|
||||||
|
(2) Right click on the VM and select "Edit Settings".
|
||||||
|
(3) Select "New Hard Disk", and click "OK".
|
||||||
|
|
||||||
|
![lvm](img/lvm/add_new_hard_disk.png)
|
||||||
|
|
||||||
|
We add a 10GB new hard disk to show the operations.
|
||||||
|
|
||||||
|
(4) Power on the VM.
|
||||||
|
|
||||||
|
2. Expand Hard Disk using LVM
|
||||||
|
|
||||||
|
Login from the console of the virtual appliance and run the following commands:
|
||||||
|
|
||||||
|
(1) Check the current size of "/data":
|
||||||
|
```sh
|
||||||
|
df -h /data
|
||||||
|
```
|
||||||
|
|
||||||
|
![lvm](img/lvm/size_of_data_01.png)
|
||||||
|
|
||||||
|
(2) Find the new hard disk, e.g. "/dev/sdc". Replace all "/dev/sdc" with your disk in the following commands.
|
||||||
|
```sh
|
||||||
|
fdisk -l
|
||||||
|
```
|
||||||
|
|
||||||
|
![lvm](img/lvm/find_the_new_harddisk.png)
|
||||||
|
|
||||||
|
(3) Create new physical volume:
|
||||||
|
```sh
|
||||||
|
pvcreate /dev/sdc
|
||||||
|
```
|
||||||
|
|
||||||
|
(4) Check the volume group:
|
||||||
|
```sh
|
||||||
|
vgdisplay
|
||||||
|
```
|
||||||
|
|
||||||
|
![lvm](img/lvm/vg_01.png)
|
||||||
|
|
||||||
|
(5) Expand the volume group:
|
||||||
|
```sh
|
||||||
|
vgextend data1_vg /dev/sdc
|
||||||
|
```
|
||||||
|
|
||||||
|
(6) Check the volume group again:
|
||||||
|
```sh
|
||||||
|
vgdisplay
|
||||||
|
```
|
||||||
|
|
||||||
|
![lvm](img/lvm/vg_02.png)
|
||||||
|
|
||||||
|
(7) Check the logical volume:
|
||||||
|
```sh
|
||||||
|
lvdisplay
|
||||||
|
```
|
||||||
|
|
||||||
|
![lvm](img/lvm/lv_01.png)
|
||||||
|
|
||||||
|
(8) Resize the logical volume:
|
||||||
|
```sh
|
||||||
|
lvresize -l +100%FREE /dev/data1_vg/data
|
||||||
|
```
|
||||||
|
|
||||||
|
![lvm](img/lvm/resize_lv.png)
|
||||||
|
|
||||||
|
(9) Check the logical volume again, note the change of "LV Size":
|
||||||
|
```sh
|
||||||
|
lvdisplay
|
||||||
|
```
|
||||||
|
|
||||||
|
![lvm](img/lvm/lv_02.png)
|
||||||
|
|
||||||
|
(10) Resize the file system:
|
||||||
|
```sh
|
||||||
|
resize2fs /dev/data1_vg/data
|
||||||
|
```
|
||||||
|
|
||||||
|
(11) Check the size "/data" again:
|
||||||
|
```sh
|
||||||
|
df -h /data
|
||||||
|
```
|
||||||
|
|
||||||
|
![lvm](img/lvm/size_of_data_02.png)
|
||||||
|
|
||||||
|
You can also check the size on Harbor web UI:
|
||||||
|
|
||||||
|
![lvm](img/lvm/check_on_ui.png)
|
||||||
|
|
||||||
|
After that, your disk should be expanded successfully. If you want to add more hard disks, do the steps again.
|
Before Width: | Height: | Size: 42 KiB After Width: | Height: | Size: 78 KiB |
Before Width: | Height: | Size: 24 KiB After Width: | Height: | Size: 70 KiB |
Before Width: | Height: | Size: 23 KiB After Width: | Height: | Size: 70 KiB |
Before Width: | Height: | Size: 45 KiB After Width: | Height: | Size: 40 KiB |
Before Width: | Height: | Size: 36 KiB After Width: | Height: | Size: 25 KiB |
Before Width: | Height: | Size: 36 KiB After Width: | Height: | Size: 25 KiB |
Before Width: | Height: | Size: 20 KiB After Width: | Height: | Size: 12 KiB |
Before Width: | Height: | Size: 13 KiB After Width: | Height: | Size: 9.6 KiB |
Before Width: | Height: | Size: 14 KiB After Width: | Height: | Size: 9.6 KiB |
Before Width: | Height: | Size: 36 KiB After Width: | Height: | Size: 26 KiB |
Before Width: | Height: | Size: 38 KiB After Width: | Height: | Size: 26 KiB |
BIN
docs/img/ovainstall/pvscan.png
Normal file
After Width: | Height: | Size: 74 KiB |
BIN
docs/img/ovainstall/removedisk.png
Normal file
After Width: | Height: | Size: 114 KiB |
BIN
docs/img/ovainstall/vappoptions.png
Normal file
After Width: | Height: | Size: 116 KiB |
@ -110,4 +110,8 @@
|
|||||||
|
|
||||||
![Screenshot of after login](img/ovainstall/afterlogin.png)
|
![Screenshot of after login](img/ovainstall/afterlogin.png)
|
||||||
|
|
||||||
Please run "tdnf distro-sync" command from time to time to keep the OS up to date.
|
To migrate Harbor OVA, please refer [migrate OVA guide](migrate_ova_guide.md)
|
||||||
|
|
||||||
|
To extend the data disk in Harbor OVA, please refer [Expand the Hard Disk of Virtual Appliance](expand_hard_disk.md)
|
||||||
|
|
||||||
|
Please run "tdnf distro-sync" command from time to time to keep the OS up to date.
|
||||||
|
41
docs/migrate_ova_guide.md
Normal file
@ -0,0 +1,41 @@
|
|||||||
|
# Harbor OVA upgrade and database migration guide
|
||||||
|
|
||||||
|
This guide is limited to migrate a Harbor OVA instance to a next release. All Harbor data storeage settings is not manually changed after the existing Harbor OVA deployed.
|
||||||
|
|
||||||
|
**Steps to migrate Harbor OVA instances:**
|
||||||
|
|
||||||
|
1. Before migration, you need to note down these following items:
|
||||||
|
- Note down the value of db_password in /data/harbor.cfg.
|
||||||
|
- **"Networking properties"** if need to keep these settings. you can find them in **"Edit Settings"** -> **"vApp Options"**
|
||||||
|
![Screenshot of vApp Options](img/ovainstall/vappoptions.png)
|
||||||
|
|
||||||
|
2. In the vSphere Web Client, Select **"Shut Down Guest OS"** on the existing OVA instances, then select **"Edit Settings"**, remove the **"Hard disk 2"**, uncheck **"Delete files from datastore"**. All disks in the logical volume "data1_vg" should be removed if the original logic volume have been extended. Use pvscan command to check disks in logical volume "data1_vg".
|
||||||
|
|
||||||
|
![Screenshot of pvscan](img/ovainstall/pvscan.png)
|
||||||
|
|
||||||
|
Usually, the sda is the Hard Disk1 in the **"Edit Settings"** pannel, and the sdb is Hard Disk2 and so on. Note down all location of disk files in logical volume "data1_vg".
|
||||||
|
|
||||||
|
![Screenshot of Remove Disk](img/ovainstall/removedisk.png)
|
||||||
|
|
||||||
|
3. Deploy a new Harbor OVA instances. You may use different settings or keep same settings.
|
||||||
|
|
||||||
|
| Configuration Section Name |Use Different Settings | Keep Same Settings |
|
||||||
|
|-------------------------------- | ------------------|--------------------------------------------- |
|
||||||
|
| Certificates | Leave blank to use auto-generated certificates or paste in new certificates |Replace files in /data/ca_download and /data/cert with backup files in the same path of prevous Harbor instance and restart Harbor OVA after migration |
|
||||||
|
| Harbor Configuration | N/A |Input a random administrator password to bypass the data validation, it uses previous settings after migration |
|
||||||
|
| LDAP Configuration | N/A |Leave blank, it uses previous settings after migration |
|
||||||
|
| Networking Properties | Input new settings | Input previous settings |
|
||||||
|
| System | Input new settings | Input previous settings |
|
||||||
|
|
||||||
|
4. Copy all disk files of logic volume "data1_vg" in Step 2 to the new Harbor OVA's folder in vSphere's datastore.
|
||||||
|
5. Before powering on the new Harbor OVA instances, select **"Edit Settings"**, after remove the "Hard disk 2", then click **" Existing Hard Disk"** in **"New Device"**, let it point to the disk file copied in Step 4. Please add all disk files in the same order with previous OVA instance.
|
||||||
|
6. Power on the new OVA instance, login to the console and run following commands:
|
||||||
|
```
|
||||||
|
chmod 700 /migrate_OVA.sh
|
||||||
|
/migrate_OVA.sh
|
||||||
|
```
|
||||||
|
When prompt, input the value of db_password that note down in Step 1.
|
||||||
|
7. After the script is complete, visit URL: https://*<DNS Name>* to verify the new Harbor OVA instance. the administrator's password is the same password of previous Harbor instance.
|
||||||
|
|
||||||
|
|
||||||
|
|
@ -9,6 +9,8 @@ When upgrading your existing Habor instance to a newer version, you may need to
|
|||||||
|
|
||||||
- You must back up your data before any data migration.
|
- You must back up your data before any data migration.
|
||||||
|
|
||||||
|
- To migrate harbor OVA, please refer [migrate OVA guide](migrate_ova_guide.md)
|
||||||
|
|
||||||
### Upgrading Harbor and migrating data
|
### Upgrading Harbor and migrating data
|
||||||
|
|
||||||
1. Log in to the host that Harbor runs on, stop and remove existing Harbor instance if it is still running:
|
1. Log in to the host that Harbor runs on, stop and remove existing Harbor instance if it is still running:
|
||||||
|
@ -2,7 +2,7 @@ swagger: '2.0'
|
|||||||
info:
|
info:
|
||||||
title: Harbor API
|
title: Harbor API
|
||||||
description: These APIs provide services for manipulating Harbor project.
|
description: These APIs provide services for manipulating Harbor project.
|
||||||
version: 0.3.0
|
version: 1.4.0
|
||||||
host: localhost
|
host: localhost
|
||||||
schemes:
|
schemes:
|
||||||
- http
|
- http
|
||||||
@ -11,7 +11,6 @@ produces:
|
|||||||
- application/json
|
- application/json
|
||||||
- text/plain
|
- text/plain
|
||||||
consumes:
|
consumes:
|
||||||
- text/plain
|
|
||||||
- application/json
|
- application/json
|
||||||
paths:
|
paths:
|
||||||
/search:
|
/search:
|
||||||
@ -142,6 +141,8 @@ paths:
|
|||||||
description: User need to log in first.
|
description: User need to log in first.
|
||||||
'409':
|
'409':
|
||||||
description: Project name already exists.
|
description: Project name already exists.
|
||||||
|
'415':
|
||||||
|
$ref: '#/responses/UnsupportedMediaType'
|
||||||
'500':
|
'500':
|
||||||
description: Unexpected internal errors.
|
description: Unexpected internal errors.
|
||||||
'/projects/{project_id}':
|
'/projects/{project_id}':
|
||||||
@ -355,6 +356,8 @@ paths:
|
|||||||
description: User does not have permission to the project.
|
description: User does not have permission to the project.
|
||||||
'404':
|
'404':
|
||||||
description: Project ID does not exist.
|
description: Project ID does not exist.
|
||||||
|
'415':
|
||||||
|
$ref: '#/responses/UnsupportedMediaType'
|
||||||
'500':
|
'500':
|
||||||
description: Internal server errors.
|
description: Internal server errors.
|
||||||
'/projects/{project_id}/metadatas/{meta_name}':
|
'/projects/{project_id}/metadatas/{meta_name}':
|
||||||
@ -511,6 +514,8 @@ paths:
|
|||||||
description: Project ID or username does not exist.
|
description: Project ID or username does not exist.
|
||||||
'409':
|
'409':
|
||||||
description: User has already added as a project role member.
|
description: User has already added as a project role member.
|
||||||
|
'415':
|
||||||
|
$ref: '#/responses/UnsupportedMediaType'
|
||||||
'500':
|
'500':
|
||||||
description: Unexpected internal errors.
|
description: Unexpected internal errors.
|
||||||
'/projects/{project_id}/members/{user_id}':
|
'/projects/{project_id}/members/{user_id}':
|
||||||
@ -712,6 +717,8 @@ paths:
|
|||||||
description: >-
|
description: >-
|
||||||
User registration can only be used by admin role user when
|
User registration can only be used by admin role user when
|
||||||
self-registration is off.
|
self-registration is off.
|
||||||
|
'415':
|
||||||
|
$ref: '#/responses/UnsupportedMediaType'
|
||||||
'500':
|
'500':
|
||||||
description: Unexpected internal errors.
|
description: Unexpected internal errors.
|
||||||
/users/current:
|
/users/current:
|
||||||
@ -1131,6 +1138,8 @@ paths:
|
|||||||
description: User doesn't have permission to perform the action.
|
description: User doesn't have permission to perform the action.
|
||||||
'404':
|
'404':
|
||||||
description: The image does not exist in Harbor.
|
description: The image does not exist in Harbor.
|
||||||
|
'415':
|
||||||
|
$ref: '#/responses/UnsupportedMediaType'
|
||||||
'503':
|
'503':
|
||||||
description: Harbor is not deployed with Clair.
|
description: Harbor is not deployed with Clair.
|
||||||
/repositories/scanAll:
|
/repositories/scanAll:
|
||||||
@ -1146,7 +1155,9 @@ paths:
|
|||||||
- name: project_id
|
- name: project_id
|
||||||
in: query
|
in: query
|
||||||
type: integer
|
type: integer
|
||||||
description: When this parm is set only the images under the project identified by the project_id will be scanned.
|
description: >-
|
||||||
|
When this parm is set only the images under the project identified
|
||||||
|
by the project_id will be scanned.
|
||||||
responses:
|
responses:
|
||||||
'202':
|
'202':
|
||||||
description: >-
|
description: >-
|
||||||
@ -1156,6 +1167,8 @@ paths:
|
|||||||
description: User needs to login or call the API with correct credentials.
|
description: User needs to login or call the API with correct credentials.
|
||||||
'403':
|
'403':
|
||||||
description: User doesn't have permission to perform the action.
|
description: User doesn't have permission to perform the action.
|
||||||
|
'415':
|
||||||
|
$ref: '#/responses/UnsupportedMediaType'
|
||||||
'500':
|
'500':
|
||||||
description: Failed to initiate the action.
|
description: Failed to initiate the action.
|
||||||
'503':
|
'503':
|
||||||
@ -1393,7 +1406,7 @@ paths:
|
|||||||
description: Unexpected internal errors.
|
description: Unexpected internal errors.
|
||||||
put:
|
put:
|
||||||
summary: Update status of jobs. Only stop is supported for now.
|
summary: Update status of jobs. Only stop is supported for now.
|
||||||
description: >
|
description: |
|
||||||
The endpoint is used to stop the replication jobs of a policy.
|
The endpoint is used to stop the replication jobs of a policy.
|
||||||
tags:
|
tags:
|
||||||
- Products
|
- Products
|
||||||
@ -1417,7 +1430,7 @@ paths:
|
|||||||
description: Resource requested does not exist.
|
description: Resource requested does not exist.
|
||||||
'500':
|
'500':
|
||||||
description: Unexpected internal errors.
|
description: Unexpected internal errors.
|
||||||
/jobs/replication/{id}:
|
'/jobs/replication/{id}':
|
||||||
delete:
|
delete:
|
||||||
summary: Delete specific ID job.
|
summary: Delete specific ID job.
|
||||||
description: |
|
description: |
|
||||||
@ -1442,7 +1455,7 @@ paths:
|
|||||||
description: Project ID does not exist.
|
description: Project ID does not exist.
|
||||||
'500':
|
'500':
|
||||||
description: Unexpected internal errors.
|
description: Unexpected internal errors.
|
||||||
/jobs/replication/{id}/log:
|
'/jobs/replication/{id}/log':
|
||||||
get:
|
get:
|
||||||
summary: Get job logs.
|
summary: Get job logs.
|
||||||
description: |
|
description: |
|
||||||
@ -1467,7 +1480,7 @@ paths:
|
|||||||
description: The specific repository ID's log does not exist.
|
description: The specific repository ID's log does not exist.
|
||||||
'500':
|
'500':
|
||||||
description: Unexpected internal errors.
|
description: Unexpected internal errors.
|
||||||
/jobs/scan/{id}/log:
|
'/jobs/scan/{id}/log':
|
||||||
get:
|
get:
|
||||||
summary: Get job logs.
|
summary: Get job logs.
|
||||||
description: |
|
description: |
|
||||||
@ -1492,7 +1505,6 @@ paths:
|
|||||||
description: The specific repository ID's log does not exist.
|
description: The specific repository ID's log does not exist.
|
||||||
'500':
|
'500':
|
||||||
description: Unexpected internal errors.
|
description: Unexpected internal errors.
|
||||||
|
|
||||||
/policies/replication:
|
/policies/replication:
|
||||||
get:
|
get:
|
||||||
summary: List filters policies by name and project_id
|
summary: List filters policies by name and project_id
|
||||||
@ -1516,13 +1528,13 @@ paths:
|
|||||||
type: integer
|
type: integer
|
||||||
format: int32
|
format: int32
|
||||||
required: false
|
required: false
|
||||||
description: 'The page nubmer.'
|
description: The page nubmer.
|
||||||
- name: page_size
|
- name: page_size
|
||||||
in: query
|
in: query
|
||||||
type: integer
|
type: integer
|
||||||
format: int32
|
format: int32
|
||||||
required: false
|
required: false
|
||||||
description: 'The size of per page.'
|
description: The size of per page.
|
||||||
tags:
|
tags:
|
||||||
- Products
|
- Products
|
||||||
responses:
|
responses:
|
||||||
@ -1563,9 +1575,11 @@ paths:
|
|||||||
description: >-
|
description: >-
|
||||||
Policy name already used or policy already exists with the same
|
Policy name already used or policy already exists with the same
|
||||||
project and target.
|
project and target.
|
||||||
|
'415':
|
||||||
|
$ref: '#/responses/UnsupportedMediaType'
|
||||||
'500':
|
'500':
|
||||||
description: Unexpected internal errors.
|
description: Unexpected internal errors.
|
||||||
/policies/replication/{id}:
|
'/policies/replication/{id}':
|
||||||
get:
|
get:
|
||||||
summary: Get replication policy.
|
summary: Get replication policy.
|
||||||
description: |
|
description: |
|
||||||
@ -1604,7 +1618,7 @@ paths:
|
|||||||
description: policy ID
|
description: policy ID
|
||||||
- name: policyupdate
|
- name: policyupdate
|
||||||
in: body
|
in: body
|
||||||
description: 'Updated properties of the replication policy.'
|
description: Updated properties of the replication policy.
|
||||||
required: true
|
required: true
|
||||||
schema:
|
schema:
|
||||||
$ref: '#/definitions/RepPolicy'
|
$ref: '#/definitions/RepPolicy'
|
||||||
@ -1646,6 +1660,8 @@ paths:
|
|||||||
description: User need to log in first.
|
description: User need to log in first.
|
||||||
'404':
|
'404':
|
||||||
description: The policy does not exist.
|
description: The policy does not exist.
|
||||||
|
'415':
|
||||||
|
$ref: '#/responses/UnsupportedMediaType'
|
||||||
'500':
|
'500':
|
||||||
description: Unexpected internal errors.
|
description: Unexpected internal errors.
|
||||||
/targets:
|
/targets:
|
||||||
@ -1695,6 +1711,8 @@ paths:
|
|||||||
description: User need to log in first.
|
description: User need to log in first.
|
||||||
'409':
|
'409':
|
||||||
description: Replication target name already exists.
|
description: Replication target name already exists.
|
||||||
|
'415':
|
||||||
|
$ref: '#/responses/UnsupportedMediaType'
|
||||||
'500':
|
'500':
|
||||||
description: Unexpected internal errors.
|
description: Unexpected internal errors.
|
||||||
/targets/ping:
|
/targets/ping:
|
||||||
@ -1725,6 +1743,8 @@ paths:
|
|||||||
target.
|
target.
|
||||||
'404':
|
'404':
|
||||||
description: Target not found.
|
description: Target not found.
|
||||||
|
'415':
|
||||||
|
$ref: '#/responses/UnsupportedMediaType'
|
||||||
'500':
|
'500':
|
||||||
description: Unexpected internal errors.
|
description: Unexpected internal errors.
|
||||||
'/targets/{id}':
|
'/targets/{id}':
|
||||||
@ -1852,6 +1872,8 @@ paths:
|
|||||||
description: User need to log in first.
|
description: User need to log in first.
|
||||||
'403':
|
'403':
|
||||||
description: User does not have permission of admin role.
|
description: User does not have permission of admin role.
|
||||||
|
'415':
|
||||||
|
$ref: '#/responses/UnsupportedMediaType'
|
||||||
'500':
|
'500':
|
||||||
description: Unexpected internal errors.
|
description: Unexpected internal errors.
|
||||||
/systeminfo:
|
/systeminfo:
|
||||||
@ -1938,6 +1960,8 @@ paths:
|
|||||||
description: User need to login first.
|
description: User need to login first.
|
||||||
'403':
|
'403':
|
||||||
description: Only admin has this authority.
|
description: Only admin has this authority.
|
||||||
|
'415':
|
||||||
|
$ref: '#/responses/UnsupportedMediaType'
|
||||||
'500':
|
'500':
|
||||||
description: Unexpected internal errors.
|
description: Unexpected internal errors.
|
||||||
/ldap/users/search:
|
/ldap/users/search:
|
||||||
@ -1977,6 +2001,8 @@ paths:
|
|||||||
description: User need to login first.
|
description: User need to login first.
|
||||||
'403':
|
'403':
|
||||||
description: Only admin has this authority.
|
description: Only admin has this authority.
|
||||||
|
'415':
|
||||||
|
$ref: '#/responses/UnsupportedMediaType'
|
||||||
'500':
|
'500':
|
||||||
description: Unexpected internal errors.
|
description: Unexpected internal errors.
|
||||||
/ldap/users/import:
|
/ldap/users/import:
|
||||||
@ -2008,6 +2034,8 @@ paths:
|
|||||||
description: User need to login first.
|
description: User need to login first.
|
||||||
'403':
|
'403':
|
||||||
description: Only admin has this authority.
|
description: Only admin has this authority.
|
||||||
|
'415':
|
||||||
|
$ref: '#/responses/UnsupportedMediaType'
|
||||||
'500':
|
'500':
|
||||||
description: Failed import some users.
|
description: Failed import some users.
|
||||||
schema:
|
schema:
|
||||||
@ -2046,7 +2074,9 @@ paths:
|
|||||||
required: true
|
required: true
|
||||||
schema:
|
schema:
|
||||||
$ref: '#/definitions/Configurations'
|
$ref: '#/definitions/Configurations'
|
||||||
description: The configuration map can contain a subset of the attributes of the schema, which are to be updated.
|
description: >-
|
||||||
|
The configuration map can contain a subset of the attributes of the
|
||||||
|
schema, which are to be updated.
|
||||||
responses:
|
responses:
|
||||||
'200':
|
'200':
|
||||||
description: Modify system configurations successfully.
|
description: Modify system configurations successfully.
|
||||||
@ -2071,6 +2101,8 @@ paths:
|
|||||||
description: User need to log in first.
|
description: User need to log in first.
|
||||||
'403':
|
'403':
|
||||||
description: User does not have permission of admin role.
|
description: User does not have permission of admin role.
|
||||||
|
'415':
|
||||||
|
$ref: '#/responses/UnsupportedMediaType'
|
||||||
'500':
|
'500':
|
||||||
description: Unexpected internal errors.
|
description: Unexpected internal errors.
|
||||||
/email/ping:
|
/email/ping:
|
||||||
@ -2098,8 +2130,14 @@ paths:
|
|||||||
description: User need to login first.
|
description: User need to login first.
|
||||||
'403':
|
'403':
|
||||||
description: Only admin has this authority.
|
description: Only admin has this authority.
|
||||||
|
'415':
|
||||||
|
$ref: '#responses/UnsupportedMediaType'
|
||||||
'500':
|
'500':
|
||||||
description: Unexpected internal errors.
|
description: Unexpected internal errors.
|
||||||
|
responses:
|
||||||
|
UnsupportedMediaType:
|
||||||
|
description: The Media Type of the request is not supported, it has to be "application/json"
|
||||||
|
|
||||||
definitions:
|
definitions:
|
||||||
Search:
|
Search:
|
||||||
type: object
|
type: object
|
||||||
@ -2451,7 +2489,9 @@ definitions:
|
|||||||
properties:
|
properties:
|
||||||
kind:
|
kind:
|
||||||
type: string
|
type: string
|
||||||
description: The replication policy trigger kind. The valid values are manual, immediate and schedule.
|
description: >-
|
||||||
|
The replication policy trigger kind. The valid values are manual,
|
||||||
|
immediate and schedule.
|
||||||
schedule_param:
|
schedule_param:
|
||||||
$ref: '#/definitions/ScheduleParam'
|
$ref: '#/definitions/ScheduleParam'
|
||||||
ScheduleParam:
|
ScheduleParam:
|
||||||
@ -2463,17 +2503,19 @@ definitions:
|
|||||||
weekday:
|
weekday:
|
||||||
type: integer
|
type: integer
|
||||||
format: int8
|
format: int8
|
||||||
description: Optional, only used when the type is weedly. The valid values are 1-7.
|
description: 'Optional, only used when the type is weedly. The valid values are 1-7.'
|
||||||
offtime:
|
offtime:
|
||||||
type: integer
|
type: integer
|
||||||
format: int64
|
format: int64
|
||||||
description: The time offset with the UTC 00:00 in seconds.
|
description: 'The time offset with the UTC 00:00 in seconds.'
|
||||||
RepFilter:
|
RepFilter:
|
||||||
type: object
|
type: object
|
||||||
properties:
|
properties:
|
||||||
kind:
|
kind:
|
||||||
type: string
|
type: string
|
||||||
description: The replication policy filter kind. The valid values are project, repository and tag.
|
description: >-
|
||||||
|
The replication policy filter kind. The valid values are project,
|
||||||
|
repository and tag.
|
||||||
pattern:
|
pattern:
|
||||||
type: string
|
type: string
|
||||||
description: The replication policy filter pattern.
|
description: The replication policy filter pattern.
|
||||||
@ -2505,7 +2547,9 @@ definitions:
|
|||||||
description: Reserved field.
|
description: Reserved field.
|
||||||
insecure:
|
insecure:
|
||||||
type: boolean
|
type: boolean
|
||||||
description: Whether or not the certificate will be verified when Harbor tries to access the server.
|
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.
|
||||||
@ -2529,7 +2573,9 @@ definitions:
|
|||||||
description: The target server password.
|
description: The target server password.
|
||||||
insecure:
|
insecure:
|
||||||
type: boolean
|
type: boolean
|
||||||
description: Whether or not the certificate will be verified when Harbor tries to access the server.
|
description: >-
|
||||||
|
Whether or not the certificate will be verified when Harbor tries to
|
||||||
|
access the server.
|
||||||
PingTarget:
|
PingTarget:
|
||||||
type: object
|
type: object
|
||||||
properties:
|
properties:
|
||||||
@ -2548,7 +2594,9 @@ definitions:
|
|||||||
description: The target server password.
|
description: The target server password.
|
||||||
insecure:
|
insecure:
|
||||||
type: boolean
|
type: boolean
|
||||||
description: Whether or not the certificate will be verified when Harbor tries to access the server.
|
description: >-
|
||||||
|
Whether or not the certificate will be verified when Harbor tries to
|
||||||
|
access the server.
|
||||||
PutTarget:
|
PutTarget:
|
||||||
type: object
|
type: object
|
||||||
properties:
|
properties:
|
||||||
@ -2566,7 +2614,9 @@ definitions:
|
|||||||
description: The target server password.
|
description: The target server password.
|
||||||
insecure:
|
insecure:
|
||||||
type: boolean
|
type: boolean
|
||||||
description: Whether or not the certificate will be verified when Harbor tries to access the server.
|
description: >-
|
||||||
|
Whether or not the certificate will be verified when Harbor tries to
|
||||||
|
access the server.
|
||||||
HasAdminRole:
|
HasAdminRole:
|
||||||
type: object
|
type: object
|
||||||
properties:
|
properties:
|
||||||
@ -2898,7 +2948,7 @@ definitions:
|
|||||||
properties:
|
properties:
|
||||||
auth_mode:
|
auth_mode:
|
||||||
type: string
|
type: string
|
||||||
description: The auth mode of current system, such as "db_auth", "ldap_auth"
|
description: 'The auth mode of current system, such as "db_auth", "ldap_auth"'
|
||||||
email_from:
|
email_from:
|
||||||
type: string
|
type: string
|
||||||
description: The sender name for Email notification.
|
description: The sender name for Email notification.
|
||||||
@ -2916,10 +2966,15 @@ definitions:
|
|||||||
description: The username for authenticate against SMTP server.
|
description: The username for authenticate against SMTP server.
|
||||||
email_ssl:
|
email_ssl:
|
||||||
type: boolean
|
type: boolean
|
||||||
description: When it's set to true the system will access Email server via TLS by default. If it's set to false, it still will handle "STARTTLS" from server side.
|
description: >-
|
||||||
|
When it's set to true the system will access Email server via TLS by
|
||||||
|
default. If it's set to false, it still will handle "STARTTLS" from
|
||||||
|
server side.
|
||||||
email_insecure:
|
email_insecure:
|
||||||
type: boolean
|
type: boolean
|
||||||
description: Whether or not the certificate will be verified when Harbor tries to access the email server.
|
description: >-
|
||||||
|
Whether or not the certificate will be verified when Harbor tries to
|
||||||
|
access the email server.
|
||||||
ldap_url:
|
ldap_url:
|
||||||
type: string
|
type: string
|
||||||
description: The URL of LDAP server.
|
description: The URL of LDAP server.
|
||||||
@ -2931,10 +2986,12 @@ definitions:
|
|||||||
description: The filter for LDAP binding.
|
description: The filter for LDAP binding.
|
||||||
ldap_scope:
|
ldap_scope:
|
||||||
type: integer
|
type: integer
|
||||||
description: 0-LDAP_SCOPE_BASE, 1-LDAP_SCOPE_ONELEVEL, 2-LDAP_SCOPE_SUBTREE
|
description: '0-LDAP_SCOPE_BASE, 1-LDAP_SCOPE_ONELEVEL, 2-LDAP_SCOPE_SUBTREE'
|
||||||
ldap_uid:
|
ldap_uid:
|
||||||
type: string
|
type: string
|
||||||
description: The attribute which is used as identity for the LDAP binding, such as "CN" or "SAMAccountname"
|
description: >-
|
||||||
|
The attribute which is used as identity for the LDAP binding, such as
|
||||||
|
"CN" or "SAMAccountname"
|
||||||
ldap_search_dn:
|
ldap_search_dn:
|
||||||
type: string
|
type: string
|
||||||
description: The DN of the user to do the search.
|
description: The DN of the user to do the search.
|
||||||
@ -2943,29 +3000,41 @@ definitions:
|
|||||||
description: timeout in seconds for connection to LDAP server.
|
description: timeout in seconds for connection to LDAP server.
|
||||||
project_creation_restriction:
|
project_creation_restriction:
|
||||||
type: string
|
type: string
|
||||||
description: This attribute restricts what users have the permission to create project. It can be "everyone" or "adminonly".
|
description: >-
|
||||||
|
This attribute restricts what users have the permission to create
|
||||||
|
project. It can be "everyone" or "adminonly".
|
||||||
self_registration:
|
self_registration:
|
||||||
type: boolean
|
type: boolean
|
||||||
description: Whether the Harbor instance supports self-registration. If it's set to false, admin need to add user to the instance.
|
description: >-
|
||||||
|
Whether the Harbor instance supports self-registration. If it's set
|
||||||
|
to false, admin need to add user to the instance.
|
||||||
token_expiration:
|
token_expiration:
|
||||||
type: integer
|
type: integer
|
||||||
description: The expiration time of the token for internal Registry, in minutes.
|
description: 'The expiration time of the token for internal Registry, in minutes.'
|
||||||
verify_remote_cert:
|
verify_remote_cert:
|
||||||
type: boolean
|
type: boolean
|
||||||
description: Whether or not the certificate will be verified when Harbor tries to access a remote Harbor instance for replication.
|
description: >-
|
||||||
|
Whether or not the certificate will be verified when Harbor tries to
|
||||||
|
access a remote Harbor instance for replication.
|
||||||
scan_all_policy:
|
scan_all_policy:
|
||||||
type: object
|
type: object
|
||||||
properties:
|
properties:
|
||||||
type:
|
type:
|
||||||
type: string
|
type: string
|
||||||
description: The type of scan all policy, currently the valid values are "none" and "daily"
|
description: >-
|
||||||
|
The type of scan all policy, currently the valid values are "none"
|
||||||
|
and "daily"
|
||||||
parameter:
|
parameter:
|
||||||
type: object
|
type: object
|
||||||
properties:
|
properties:
|
||||||
daily_time:
|
daily_time:
|
||||||
type: integer
|
type: integer
|
||||||
description: The offest in seconds of UTC 0 o'clock, only valid when the policy type is "daily"
|
description: >-
|
||||||
description: The parameters of the policy, the values are dependant on the type of the policy.
|
The offest in seconds of UTC 0 o'clock, only valid when the
|
||||||
|
policy type is "daily"
|
||||||
|
description: >-
|
||||||
|
The parameters of the policy, the values are dependant on the type
|
||||||
|
of the policy.
|
||||||
Replication:
|
Replication:
|
||||||
type: object
|
type: object
|
||||||
properties:
|
properties:
|
||||||
|
@ -12,7 +12,7 @@ services:
|
|||||||
networks:
|
networks:
|
||||||
- harbor
|
- harbor
|
||||||
registry:
|
registry:
|
||||||
image: vmware/registry:2.6.2-photon
|
image: vmware/registry-photon:__reg_version__
|
||||||
container_name: registry
|
container_name: registry
|
||||||
restart: always
|
restart: always
|
||||||
volumes:
|
volumes:
|
||||||
@ -95,7 +95,7 @@ services:
|
|||||||
syslog-address: "tcp://127.0.0.1:1514"
|
syslog-address: "tcp://127.0.0.1:1514"
|
||||||
tag: "jobservice"
|
tag: "jobservice"
|
||||||
proxy:
|
proxy:
|
||||||
image: vmware/nginx-photon:1.11.13
|
image: vmware/nginx-photon:__nginx_version__
|
||||||
container_name: nginx
|
container_name: nginx
|
||||||
restart: always
|
restart: always
|
||||||
volumes:
|
volumes:
|
||||||
|
@ -265,7 +265,7 @@ else:
|
|||||||
storage_provider_name = rcp.get("configuration", "registry_storage_provider_name").strip()
|
storage_provider_name = rcp.get("configuration", "registry_storage_provider_name").strip()
|
||||||
storage_provider_config = rcp.get("configuration", "registry_storage_provider_config").strip()
|
storage_provider_config = rcp.get("configuration", "registry_storage_provider_config").strip()
|
||||||
# yaml requires 1 or more spaces between the key and value
|
# yaml requires 1 or more spaces between the key and value
|
||||||
storage_provider_config = storage_provider_config.replace(":", ": ")
|
storage_provider_config = storage_provider_config.replace(":", ": ", 1)
|
||||||
|
|
||||||
ui_secret = ''.join(random.choice(string.ascii_letters+string.digits) for i in range(16))
|
ui_secret = ''.join(random.choice(string.ascii_letters+string.digits) for i in range(16))
|
||||||
jobservice_secret = ''.join(random.choice(string.ascii_letters+string.digits) for i in range(16))
|
jobservice_secret = ''.join(random.choice(string.ascii_letters+string.digits) for i in range(16))
|
||||||
|
@ -100,7 +100,9 @@ func schedulePolicy(notification ScanPolicyNotification) error {
|
|||||||
OffsetTime: notification.DailyTime,
|
OffsetTime: notification.DailyTime,
|
||||||
})
|
})
|
||||||
attachTask := task.NewScanAllTask()
|
attachTask := task.NewScanAllTask()
|
||||||
schedulePolicy.AttachTasks(attachTask)
|
if err := schedulePolicy.AttachTasks(attachTask); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
return scheduler.DefaultScheduler.Schedule(schedulePolicy)
|
return scheduler.DefaultScheduler.Schedule(schedulePolicy)
|
||||||
}
|
}
|
||||||
|
@ -100,7 +100,9 @@ func TestTCPConn(addr string, timeout, interval int) error {
|
|||||||
time.Sleep(time.Duration(interval) * time.Second)
|
time.Sleep(time.Duration(interval) * time.Second)
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
conn.Close()
|
if err = conn.Close(); err != nil {
|
||||||
|
log.Errorf("failed to close the connection: %v", err)
|
||||||
|
}
|
||||||
success <- 1
|
success <- 1
|
||||||
break
|
break
|
||||||
}
|
}
|
||||||
|
@ -28,7 +28,7 @@ func NewLogger(j Job) (*log.Logger, error) {
|
|||||||
logFile := j.LogPath()
|
logFile := j.LogPath()
|
||||||
d := filepath.Dir(logFile)
|
d := filepath.Dir(logFile)
|
||||||
if _, err := os.Stat(d); os.IsNotExist(err) {
|
if _, err := os.Stat(d); os.IsNotExist(err) {
|
||||||
err := os.MkdirAll(d, 0755)
|
err := os.MkdirAll(d, 0700)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
log.Errorf("Failed to create directory for log file %s, the error: %v", logFile, err)
|
log.Errorf("Failed to create directory for log file %s, the error: %v", logFile, err)
|
||||||
}
|
}
|
||||||
|
@ -158,9 +158,11 @@ func (l *Auth) PostAuthenticate(u *models.User) error {
|
|||||||
if !Re.MatchString(u.Email) {
|
if !Re.MatchString(u.Email) {
|
||||||
log.Debugf("Not a valid email address: %v, skip to sync", u.Email)
|
log.Debugf("Not a valid email address: %v, skip to sync", u.Email)
|
||||||
} else {
|
} else {
|
||||||
dao.ChangeUserProfile(*u, "Email")
|
if err = dao.ChangeUserProfile(*u, "Email"); err != nil {
|
||||||
}
|
|
||||||
u.Email = dbUser.Email
|
u.Email = dbUser.Email
|
||||||
|
log.Errorf("failed to sync user email: %v", err)
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return nil
|
return nil
|
||||||
|
@ -116,7 +116,9 @@ func main() {
|
|||||||
scheduler.DefaultScheduler.Start()
|
scheduler.DefaultScheduler.Start()
|
||||||
|
|
||||||
//Subscribe the policy change topic.
|
//Subscribe the policy change topic.
|
||||||
notifier.Subscribe(notifier.ScanAllPolicyTopic, ¬ifier.ScanPolicyNotificationHandler{})
|
if err = notifier.Subscribe(notifier.ScanAllPolicyTopic, ¬ifier.ScanPolicyNotificationHandler{}); err != nil {
|
||||||
|
log.Errorf("failed to subscribe scan all policy change topic: %v", err)
|
||||||
|
}
|
||||||
|
|
||||||
//Get policy configuration.
|
//Get policy configuration.
|
||||||
scanAllPolicy := config.ScanAllPolicy()
|
scanAllPolicy := config.ScanAllPolicy()
|
||||||
@ -129,7 +131,10 @@ func main() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
//Send notification to handle first policy change.
|
//Send notification to handle first policy change.
|
||||||
notifier.Publish(notifier.ScanAllPolicyTopic, notifier.ScanPolicyNotification{Type: scanAllPolicy.Type, DailyTime: (int64)(dailyTime)})
|
if err = notifier.Publish(notifier.ScanAllPolicyTopic,
|
||||||
|
notifier.ScanPolicyNotification{Type: scanAllPolicy.Type, DailyTime: (int64)(dailyTime)}); err != nil {
|
||||||
|
log.Errorf("failed to publish scan all policy topic: %v", err)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if err := core.Init(); err != nil {
|
if err := core.Init(); err != nil {
|
||||||
|
@ -89,6 +89,11 @@ if build_type == "ova" :
|
|||||||
logger.info("Harbor is not ready after 10 minutes.")
|
logger.info("Harbor is not ready after 10 minutes.")
|
||||||
sys.exit(-1)
|
sys.exit(-1)
|
||||||
logger.info("%s is ready for test now..." % item)
|
logger.info("%s is ready for test now..." % item)
|
||||||
|
# ----- log harbor version -----
|
||||||
|
harbor_version = harbor_util.get_harbor_version(item, 'admin', 'Harbor12345')
|
||||||
|
logger.info("Harbor version: %s ..." % harbor_version)
|
||||||
|
with open(os.getcwd() + '/build.properties', 'w') as the_file:
|
||||||
|
the_file.write('harbor_version=%s' % harbor_version)
|
||||||
|
|
||||||
# ----- execute test cases -----
|
# ----- execute test cases -----
|
||||||
try:
|
try:
|
||||||
|
@ -1,6 +1,33 @@
|
|||||||
from urllib2 import urlopen
|
from urllib2 import urlopen
|
||||||
import ssl
|
import ssl
|
||||||
import time
|
import time
|
||||||
|
import os
|
||||||
|
try:
|
||||||
|
import json
|
||||||
|
except ImportError:
|
||||||
|
import simplejson as json
|
||||||
|
|
||||||
|
import requests
|
||||||
|
from requests.packages.urllib3.exceptions import InsecureRequestWarning
|
||||||
|
requests.packages.urllib3.disable_warnings(InsecureRequestWarning)
|
||||||
|
|
||||||
|
def request(harbor_endpoint, url, method, user, pwd, **kwargs):
|
||||||
|
url = "https://" + harbor_endpoint + "/api" + url
|
||||||
|
kwargs.setdefault('headers', kwargs.get('headers', {}))
|
||||||
|
kwargs['headers']['Accept'] = 'application/json'
|
||||||
|
if 'body' in kwargs:
|
||||||
|
kwargs['headers']['Content-Type'] = 'application/json'
|
||||||
|
kwargs['data'] = json.dumps(kwargs['body'])
|
||||||
|
del kwargs['body']
|
||||||
|
|
||||||
|
resp = requests.request(method, url, verify=False, auth=(user, pwd), **kwargs)
|
||||||
|
if resp.status_code >= 400:
|
||||||
|
raise Exception("Error: %s" % resp.text)
|
||||||
|
try:
|
||||||
|
body = json.loads(resp.text)
|
||||||
|
except ValueError:
|
||||||
|
body = resp.text
|
||||||
|
return body
|
||||||
|
|
||||||
# wait for 10 minutes as OVA needs about 7 minutes to startup harbor.
|
# wait for 10 minutes as OVA needs about 7 minutes to startup harbor.
|
||||||
def wait_for_harbor_ready(harbor_endpoint, timeout=600):
|
def wait_for_harbor_ready(harbor_endpoint, timeout=600):
|
||||||
@ -21,3 +48,6 @@ def wait_for_harbor_ready(harbor_endpoint, timeout=600):
|
|||||||
continue
|
continue
|
||||||
timeout -= interval
|
timeout -= interval
|
||||||
time.sleep(interval)
|
time.sleep(interval)
|
||||||
|
|
||||||
|
def get_harbor_version(harbor_endpoint, harbor_user, harbor_pwd):
|
||||||
|
return request(harbor_endpoint, '/systeminfo', 'get', harbor_user, harbor_pwd)['harbor_version']
|
||||||
|