mirror of
https://github.com/goharbor/harbor.git
synced 2025-03-14 23:49:09 +01:00
Merge branch 'dev' into dev
This commit is contained in:
commit
0d09379fb7
.travis.yml
docs
README.md
img
installation_guide.mdinstallation_guide_ova.mdswagger.yamluser_guide.mduser_guide_ova.mdmake/common/templates/nginx
src
common/utils/registry/auth
ui
api
static/resources/js/components
details
inline-help
paginator
repository
validator
views
tools/ova/script
@ -73,7 +73,7 @@ before_script:
|
||||
- sudo sqlite3 /registry.db < make/common/db/registry_sqlite.sql
|
||||
|
||||
script:
|
||||
- sudo service mysql-5.6 stop
|
||||
- sudo service mysql stop
|
||||
- sudo ./tests/testprepare.sh
|
||||
- docker-compose -f ./make/docker-compose.test.yml up -d
|
||||
- go list ./... | grep -v -E 'vendor|tests' | xargs -L1 fgt golint
|
||||
|
@ -23,7 +23,7 @@ Guide to deploy Harbor on Kubenetes. (maintained by community)
|
||||
|
||||
### Developer documents
|
||||
|
||||
[Arthitecture Overview of Harbor](https://github.com/vmware/harbor/wiki/Architecture-Overview-of-Harbor)
|
||||
[Architecture Overview of Harbor](https://github.com/vmware/harbor/wiki/Architecture-Overview-of-Harbor)
|
||||
Developers read this first.
|
||||
|
||||
[Harbor API Specs by Swagger](configure_swagger.md)
|
||||
@ -61,7 +61,7 @@ How to add your local language to Harbor.
|
||||
|
||||
[Overall Architecture of Harbor Registry](http://www.compare-review-information.com/overall-architecture-of-harbor-registry/)
|
||||
|
||||
[makeing a Private Secured Docker Registry in 15 Minutes](http://alexanderzeitler.com/articles/deploying-a-private-secured-docker-registry-within-15-minutes/)
|
||||
[Making a Private Secured Docker Registry in 15 Minutes](http://alexanderzeitler.com/articles/deploying-a-private-secured-docker-registry-within-15-minutes/)
|
||||
|
||||
[Docker Private Registry Using Harbor](https://blog.imaginea.com/docker-private-registry-using-harbor-2/)
|
||||
|
||||
|
Binary file not shown.
Before ![]() (image error) Size: 72 KiB After ![]() (image error) Size: 46 KiB ![]() ![]() |
BIN
docs/img/rbac.png
Normal file
BIN
docs/img/rbac.png
Normal file
Binary file not shown.
After ![]() (image error) Size: 189 KiB |
@ -77,14 +77,13 @@ The parameters are described below - note that at the very least, you will need
|
||||
* **token_expiration**: The expiration time (in minutes) of a token created by token service, default is 30 minutes.
|
||||
|
||||
* **verify_remote_cert**: (**on** or **off**. Default is **on**) This flag determines whether or not to verify SSL/TLS certificate when Harbor communicates with a remote registry instance. Setting this attribute to **off** bypasses the SSL/TLS verification, which is often used when the remote instance has a self-signed or untrusted certificate.
|
||||
* **customize_crt**: (**on** or **off**. Default is **on**) When this attribute is **on**, the prepare script creates private key and root certificate for the generation/verification of the registry's token.
|
||||
* The following attributes:**crt_country**, **crt_state**, **crt_location**, **crt_organization**, **crt_organizationalunit**, **crt_commonname**, **crt_email** are used as parameters for generating the keys. Set this attribute to **off** when the key and root certificate are supplied by external sources. Refer to [Customize Key and Certificate of Harbor Token Service](customize_token_service.md) for more info.
|
||||
* **customize_crt**: (**on** or **off**. Default is **on**) When this attribute is **on**, the prepare script creates private key and root certificate for the generation/verification of the registry's token. The following attributes:**crt_country**, **crt_state**, **crt_location**, **crt_organization**, **crt_organizationalunit**, **crt_commonname**, **crt_email** are used as parameters for generating the keys. Set this attribute to **off** when the key and root certificate are supplied by external sources. Refer to [Customize Key and Certificate of Harbor Token Service](customize_token_service.md) for more info.
|
||||
|
||||
#### Configuring storage backend (optional)
|
||||
|
||||
By default, Harbor stores images on your local filesystem. In a production environment, you may consider
|
||||
using other storage backend instead of the local filesystem, like S3, Openstack Swift, Ceph, etc.
|
||||
What you need to update is the section of `storage` in the file `templates/registry/config.yml`.
|
||||
What you need to update is the section of `storage` in the file `common/templates/registry/config.yml`.
|
||||
For example, if you use Openstack Swift as your storage backend, the section may look like this:
|
||||
|
||||
```
|
||||
@ -103,7 +102,7 @@ _NOTE: For detailed information on storage backend of a registry, refer to [Regi
|
||||
|
||||
|
||||
#### Finishing installation and starting Harbor
|
||||
Once **harbord.cfg** and storage backend (optional) are configured, install and start Harbor using the ```install.sh``` script. Note that it may take some time for the online installer to download Harbor images from Docker hub.
|
||||
Once **harbor.cfg** and storage backend (optional) are configured, install and start Harbor using the ```install.sh``` script. Note that it may take some time for the online installer to download Harbor images from Docker hub.
|
||||
|
||||
```sh
|
||||
$ sudo ./install.sh
|
||||
@ -159,15 +158,7 @@ $ sudo install.sh
|
||||
|
||||
Removing Harbor's containers while keeping the image data and Harbor's database files on the file system:
|
||||
```
|
||||
$ sudo docker-compose rm
|
||||
Going to remove nginx, harbor-jobservice, registry, harbor-ui, harbor-db, harbor-log
|
||||
Are you sure? [yN] y
|
||||
Removing nginx ... done
|
||||
Removing harbor-jobservice ... done
|
||||
Removing registry ... done
|
||||
Removing harbor-ui ... done
|
||||
Removing harbor-db ... done
|
||||
Removing harbor-log ... done
|
||||
$ sudo docker-compose down
|
||||
```
|
||||
|
||||
Removing Harbor's database and image data (for a clean re-installation):
|
||||
@ -286,10 +277,16 @@ $ sudo install.sh
|
||||
nginx nginx -g daemon off; Up 0.0.0.0:443->443/tcp, 0.0.0.0:80->80/tcp
|
||||
registry /entrypoint.sh serve /etc/ ... Up 5000/tcp
|
||||
```
|
||||
If a container is not in **UP** state, check the log file of that container in directory ```/var/log/harbor```. For example, if the container ```harbor_ui_1``` is not running, you should look at the log file ```docker_ui.log```.
|
||||
If a container is not in **UP** state, check the log file of that container in directory ```/var/log/harbor```. For example, if the container ```harbor-ui``` is not running, you should look at the log file ```ui.log```.
|
||||
|
||||
|
||||
2.When setting up Harbor behind an nginx proxy or elastic load balancing, look for the line below, in `make/config/nginx/nginx.conf` and remove it from the sections if the proxy already has similar settings: `location /`, `location /v2/` and `location /service/`.
|
||||
2.When setting up Harbor behind an nginx proxy or elastic load balancing, look for the line below, in `common/templates/nginx/nginx.http.conf` and remove it from the sections if the proxy already has similar settings: `location /`, `location /v2/` and `location /service/`.
|
||||
```
|
||||
proxy_set_header X-Forwarded-Proto $scheme;
|
||||
```
|
||||
And run the following commands to restart Harbor:
|
||||
```sh
|
||||
$ sudo docker-compose down
|
||||
$ sudo ./prepare
|
||||
$ sudo docker-compose up -d
|
||||
```
|
@ -1,6 +1,13 @@
|
||||
# Installing and Configuring Harbor on vSphere as Virtual Appliance
|
||||
|
||||
This guide walks you through the steps about installing and configuring Harbor on vSphere as an virtual appliance. If you are installing Harbor on a Linux host, refer to this **[Installation Guide](installation_guide.md)**.
|
||||
* [Prerequisites](#prerequisites)
|
||||
* [Planning for installation](#planning-for-installation)
|
||||
* [Installation](#installation)
|
||||
* [Getting Certificate of Harbor's CA](#getting-certificate-of-harbors-ca)
|
||||
* [Reconfiguration](#reconfiguration)
|
||||
* [Troubleshooting](#troubleshooting)
|
||||
|
||||
This guide walks you through the steps about installing and configuring Harbor on vSphere as a virtual appliance. If you are installing Harbor on a Linux host, refer to this **[Installation Guide](installation_guide.md)**.
|
||||
|
||||
## Prerequisites
|
||||
* vCenter 5.5+ and at least an ESX host.
|
||||
@ -18,7 +25,7 @@ By default, Harbor stores user information in an internal database. Harbor can a
|
||||
|
||||
Harbor uses HTTPS for secure communication by default. A self-signed certificate is generated at first boot based on its FQDN (Fully Qualified Domain Name) or IP address. A Docker client or a VCH (Virtual Container Host) needs to trust the certificate of Harbor's CA (Certificate Authority) in order to interact with Harbor.
|
||||
|
||||
Harbor always tries to generate a self-signed certificate based on its FQDN. Therefore, its IP address must have a FQDN associated with it in the DNS server. If Harbor cannot resolve its IP address to a FQDN, it generates the self-signed certificate using its IP address. In this case, Harbor can only be accessed by IP address. When Harbor's IP address or FQDN is changed, the self-signed certificate will be re-generated.
|
||||
Harbor always tries to generate a self-signed certificate based on its FQDN. Therefore, its IP address must have a FQDN associated with it in the DNS server. If Harbor cannot resolve its IP address to a FQDN, it generates the self-signed certificate using its IP address. In this case, Harbor can only be accessed by IP address. When Harbor's IP address or FQDN is changed, the self-signed certificate will be re-generated after a reboot.
|
||||
|
||||
Harbor's self-generated certificate can be replaced by supplying a certificate signed by other CAs in OVA's settings.
|
||||
|
||||
@ -68,10 +75,9 @@ For the purpose of generating a self-signed certificate, it is recommended that
|
||||
|
||||
* System
|
||||
* **Root Password**: The initial password of the root user. Subsequent changes of password should be performed in operating system. (8-128 characters)
|
||||
* **Harbor Admin Password**: The initial password of Harbor admin. It only works for the first time when Harbor starts. It has no effect after the first launch of Harbor. Change the admin password from UI after launching Harbor.
|
||||
* **Harbor Admin Password**: The initial password of Harbor admin. It only works for the first time when Harbor starts. It has no effect after the first launch of Harbor. Change the admin password from UI after launching Harbor. (8-20 characters)
|
||||
* **Database Password**: The initial password of the root user of MySQL database. Subsequent changes of password should be performed in operating system. (8-128 characters)
|
||||
* **Permit Root Login**: Specifies whether root user can log in using SSH.
|
||||
* **Self Registration**: Determine whether the self-registration is allowed or not. Set this to off to disable a user's self-registration in Harbor. This flag has no effect when users are stored in LDAP or AD.
|
||||
* **Garbage Collection**: When setting this to true, Harbor performs garbage collection everytime it boots up. The first time setting this flag to true needs to power off the VM and power it on again.
|
||||
|
||||
* Authentication
|
||||
@ -79,8 +85,9 @@ For the purpose of generating a self-signed certificate, it is recommended that
|
||||
The **Authentication Mode** must be set before the first boot of Harbor. Subsequent changes to **Authentication Mode** do not have any effect. When **ldap_auth** mode is enabled, properties related to LDAP/AD must be set.
|
||||
|
||||
* **Authentication Mode**: The default authentication mode is **db_auth**. Set it to **ldap_auth** when users' credentials are stored in an LDAP or AD server. Note: this option can only be set once.
|
||||
* **Self Registration**: Determine whether the self-registration is allowed or not. Set this to off to disable a user's self-registration in Harbor. This flag has no effect when users are stored in LDAP or AD.
|
||||
* **LDAP URL**: The URL of an LDAP/AD server.
|
||||
* **LDAP Search DN**: A user's DN who has the permission to search the LDAP/AD server. Leave blank if your LDAP/AD server supports anonymous search, otherwise you should configure this DN and **LDAP Seach Password**.
|
||||
* **LDAP Search DN**: A user's DN who has the permission to search the LDAP/AD server. Leave blank if your LDAP/AD server supports anonymous search, otherwise you should configure this DN and **LDAP Search Password**.
|
||||
* **LDAP Search Password**: The password of the user for LDAP search. Leave blank if your LDAP/AD server supports anonymous search.
|
||||
* **LDAP Base DN**: The base DN of a node from which to look up a user for authentication. The search scope includes subtree of the node.
|
||||
* **LDAP UID**: The attribute used in a search to match a user, it could be uid, cn, email, sAMAccountName or other attributes depending on your LDAP/AD server.
|
||||
@ -103,7 +110,7 @@ For the purpose of generating a self-signed certificate, it is recommended that
|
||||
* **Email Username**: The user from whom the password reset email is sent. Usually this is a system email address.
|
||||
* **Email Password**: The password of the user from whom the password reset email is sent.
|
||||
* **Email From**: The name of the email sender.
|
||||
* **Email SSL**: Whether to enabled secure mail transmission.
|
||||
* **Email SSL**: Whether to enable secure mail transmission.
|
||||
|
||||
* Networking properties
|
||||
* **Default Gateway**: The default gateway address for this VM. Leave blank if DHCP is desired.
|
||||
@ -119,16 +126,16 @@ For the purpose of generating a self-signed certificate, it is recommended that
|
||||
|
||||

|
||||
|
||||
11. Power on the virtual appliance. It may take a few minutes for the first bootup. The virtual appliance needs to initialize itself for configuration like netowrk address and password.
|
||||
11. Power on the virtual appliance. It may take a few minutes for the first bootup. The virtual appliance needs to initialize itself for configuration like network address and password.
|
||||
|
||||
12. When the appliance is ready, check from vSphere Web Client for its IP address. Open a browser and type in the URL `http(s)://harbor_ip_address` or `http(s)://harbor_host_name`. Log in as the admin user and verify Harbor has been successfully installed.
|
||||
|
||||
13. For information on how to use Harbor, please refer to [User Guide of Harbor](user_guide.md).
|
||||
13. For information on how to use Harbor, please refer to [User Guide of Harbor Virtual Appliance](user_guide_ova.md).
|
||||
|
||||
## Getting Certificate of Harbor's CA
|
||||
|
||||
By default, Harbor uses a self-signed certificate in HTTPS. A Docker client or a VCH needs to trust the certificate of Harbor's CA in order to interact with Harbor.
|
||||
To download the certificate of Harbor's CA and import into a Docker client, follow the below steps:
|
||||
By default, Harbor uses a self-signed certificate in HTTPS. A Docker client or a VCH needs to trust the self-signed certificate of Harbor's CA in order to interact with Harbor.
|
||||
To download the certificate of Harbor's CA and import into a Docker client, follow the below steps. If a certificate issued by a public known CA is used, the below steps are not needed.
|
||||
|
||||
1. Log in Harbor's UI as an admin user.
|
||||
2. Click on the admin's name at the upper left corner and select **"About"** from the drop-down menu.
|
||||
@ -147,7 +154,7 @@ To download the certificate of Harbor's CA and import into a Docker client, foll
|
||||
cp ca.crt /etc/docker/certs.d/<Harbor_IP>/
|
||||
```
|
||||
|
||||
**Note:** If you run the above two sets of commands, Harbor can be accessed by both FQDN and IP address.
|
||||
**Note:** If you run both of the above two sets of commands, Harbor can be accessed by either FQDN or IP address.
|
||||
|
||||
5. Run `docker login` command to verify that HTTPS is working.
|
||||
|
||||
@ -165,10 +172,10 @@ If you want to change the properties of Harbor, follow the below steps:
|
||||
|
||||

|
||||
|
||||
4. **Power on** the VM.
|
||||
4. **Power on** the VM and Harbor will reconfigure itself based on the new settings.
|
||||
|
||||
**Notes:**
|
||||
1. The authentication mode can only be set once before the firtst boot. Subsequent modification of this option does not have any effect.
|
||||
**Note:**
|
||||
1. The **Authentication Mode** can only be set once before the first boot. Subsequent modification of this option does not have any effect.
|
||||
2. The initial admin password, root password of the virtual appliance, MySQL root password, and all networking properties can not be modified using this method after Harbor's first launch. Modify them by the following approach:
|
||||
* **Harbor Admin Password**: Change it in Harbor admin portal.
|
||||
* **Root Password of Virtual Appliance**: Change it by logging in the virtual appliance and doing it in the Linux operating system.
|
||||
|
@ -1310,6 +1310,44 @@ paths:
|
||||
description: User does not have permission of admin role.
|
||||
500:
|
||||
description: Unexpected internal errors.
|
||||
/systeminfo/volumes:
|
||||
get:
|
||||
summary: Get system volume info (total/free size).
|
||||
description: |
|
||||
This endpoint is for retrieving system volume info that only provides for admin user.
|
||||
tags:
|
||||
- Products
|
||||
responses:
|
||||
200:
|
||||
description: Get system volumes successfully.
|
||||
schema:
|
||||
type: object
|
||||
items:
|
||||
$ref: '#/definitions/SystemInfo'
|
||||
401:
|
||||
description: User need to log in first.
|
||||
403:
|
||||
description: User does not have permission of admin role.
|
||||
500:
|
||||
description: Unexpected internal errors.
|
||||
/systeminfo/getcert:
|
||||
get:
|
||||
summary: Get default root certificate under OVA deployment.
|
||||
description: |
|
||||
This endpoint is for downloading a default root certificate that only provides for admin user under OVA deployment.
|
||||
tags:
|
||||
- Products
|
||||
responses:
|
||||
200:
|
||||
description: Get default root certificate successfully.
|
||||
401:
|
||||
description: User need to log in first.
|
||||
403:
|
||||
description: User does not have permission of admin role.
|
||||
404:
|
||||
description: Not found the default root certificate.
|
||||
500:
|
||||
description: Unexpected internal errors.
|
||||
definitions:
|
||||
Search:
|
||||
type: object
|
||||
@ -1741,3 +1779,22 @@ definitions:
|
||||
comment:
|
||||
type: string
|
||||
description: The new comment.
|
||||
Storage:
|
||||
type: object
|
||||
properties:
|
||||
total:
|
||||
type: integer
|
||||
format: int64
|
||||
description: Total volume size.
|
||||
free:
|
||||
type: integer
|
||||
format: int64
|
||||
description: Free volume size.
|
||||
SystemInfo:
|
||||
type: object
|
||||
properties:
|
||||
storage:
|
||||
type: array
|
||||
description: The storage of system.
|
||||
items:
|
||||
$ref: '#/definitions/Storage'
|
||||
|
@ -15,29 +15,48 @@ This guide walks you through the fundamentals of using Harbor. You'll learn how
|
||||
|
||||
|
||||
##Role Based Access Control
|
||||
RBAC (Role Based Access Control) is provided in Harbor and there are four roles with different privileges:
|
||||
|
||||

|
||||
|
||||
Harbor manages images through projects. Users can be added into one project as a member with three different roles:
|
||||
|
||||
* **Guest**: Guest has read-only privilege for a specified project.
|
||||
* **Developer**: Developer has read and write privileges for a project.
|
||||
* **ProjectAdmin**: When creating a new project, you will be assigned the "ProjectAdmin" role to the project. Besides read-write privileges, the "ProjectAdmin" also has some management privileges, such as adding and removing members.
|
||||
|
||||
Besides the above three roles, there are two system-wide roles:
|
||||
|
||||
* **SysAdmin**: "SysAdmin" has the most privileges. In addition to the privileges mentioned above, "SysAdmin" can also list all projects, set an ordinary user as administrator and delete users. The public project "library" is also owned by the administrator.
|
||||
* **Anonymous**: When a user is not logged in, the user is considered as an "anonymous" user. An anonymous user has no access to private projects and has read-only access to public projects.
|
||||
|
||||
##User account
|
||||
As a new user, you can sign up an account by going through the self-registration process. The username and email must be unique in the Harbor system. The password must contain at least 7 characters with 1 lowercase letter, 1 uppercase letter and 1 numeric character.
|
||||
Harbor supports two authentication modes:
|
||||
|
||||
If the administrator has configured LDAP/AD as authentication source, no sign-up is required. The LDAP/AD user id can be used directly to log in to Harbor.
|
||||
* **Database(db_auth)**
|
||||
|
||||
When you forgot your password, you can follow the below steps to reset the password:
|
||||
Users are stored in the local database.
|
||||
|
||||
1. Click the link "forgot password" in the sign in page.
|
||||
2. Input the email used when you signed up, an email will be sent out to you.
|
||||
3. After receiving the email, click on the link in the email which directs you to a password reset web page.
|
||||
4. Input your new password and click "Submit".
|
||||
A user can self register himself/herself in Harbor in this mode. To disable user self-registration, refer to the [installation guide](installation_guide_ova.md). When self-registration is disabled, the system administrator can add users in Harbor.
|
||||
|
||||
When registering or adding a new user, the username and email must be unique in the Harbor system. The password must contain at least 8 characters with 1 lowercase letter, 1 uppercase letter and 1 numeric character.
|
||||
|
||||
When you forgot your password, you can follow the below steps to reset the password:
|
||||
|
||||
1. Click the link "Forgot Password" in the sign in page.
|
||||
2. Input the email address entered when you signed up, an email will be sent out to you for password reset.
|
||||
3. After receiving the email, click on the link in the email which directs you to a password reset web page.
|
||||
4. Input your new password and click "Save".
|
||||
|
||||
* **LDAP/Active Directory (ldap_auth)**
|
||||
|
||||
Under this authentication mode, users whose credentials are stored in an external LDAP or AD server can log in to Harbor directly.
|
||||
|
||||
When an LDAP/AD user logs in by *username* and *password*, Harbor binds to the LDAP/AD server with the **"LDAP Search DN"** and **"LDAP Search Password"** described in [installation guide](installation_guide_ova.md). If it successes, Harbor looks up the user under the LDAP entry **"LDAP Base DN"** including substree. The attribute (such as uid, cn) specified by **"LDAP UID"** is used to match a user with the *username*. If a match is found, the user's *password* is verified by a bind request to the LDAP/AD server.
|
||||
|
||||
Self-registration, changing password and resetting password are not supported anymore under LDAP/AD authentication mode because the users are managed by LDAP or AD.
|
||||
|
||||
##Managing projects
|
||||
A project in Harbor contains all repositories of an application. RBAC is applied to a project. There are two types of projects in Harbor:
|
||||
A project in Harbor contains all repositories of an application. No images can be pushed to Harbor before the project is created. RBAC is applied to a project. There are two types of projects in Harbor:
|
||||
|
||||
* **Public**: All users have the read privilege to a public project, it's convenient for you to share some repositories with others in this way.
|
||||
* **Private**: A private project can only be accessed by users with proper privileges.
|
||||
@ -66,15 +85,20 @@ You can update or remove a member by clicking the icon on the right.
|
||||

|
||||
|
||||
##Replicating images
|
||||
If you are a system administrator, you can replicate images to a remote registry, which is called destination in Harbor. Only Harbor instance is supported as a destination for now.
|
||||
Images replication is used to replicate repositories from one Harbor instance to another.
|
||||
|
||||
The function is project-oriented, and once the system administrator set a policy to one project, all repositories under the project will be replicated to the remote registry. Each repository will start a job to run. If the project does not exist on the remote registry, a new project will be created automatically, but if it already exists and the user configured in policy has no write privilege to it, the process will fail. When a new repository is pushed to this project or an existing repository is deleted from this project, the same operation will also be replicated to the destination. The member information will not be replicated.
|
||||
|
||||
There may be a bit of delay during replication according to the situation of the network. If replication job fails due to the network issue, the job will be re-scheduled a few minutes later.
|
||||
|
||||
**Note:** The replication feature is incompatible between Harbor instance before version 0.3.5(included) and after version 0.3.5.
|
||||
|
||||
Click "Add New Policy" on the "Replication" tab, fill the necessary fields and click "OK", a policy for this project will be created. If "Enable" is chosen, the project will be replicated to the remote immediately, and when a new repository is pushed to this project or an existing repository is deleted from this project, the same operation will also be replicated to the destination.
|
||||
Start replication by creating a policy. Click "Add New Policy" on the "Replication" tab, fill the necessary fields, if there is no destination in the list, you need to create one, and then click "OK", a policy for this project will be created. If "Enable" is chosen, the project will be replicated to the remote immediately.
|
||||
|
||||

|
||||
|
||||
You can enable or disable a policy in the policy list view, and only the policies which are disbled can be edited.
|
||||
You can enable, disable or delete a policy in the policy list view. Only policies which are disabled can be edited and only policies which are disabled and have no running jobs can be deleted. If a policy is disabled, the running jobs under it will be stopped.
|
||||
|
||||
Click a policy, jobs which belong to this policy will be listed. A job represents the progress which will replicate a repository of one project to the remote.
|
||||
|
||||

|
||||
@ -104,7 +128,7 @@ You can list, edit, enable and disable policies in the "Replication" tab. Make s
|
||||
|
||||
**NOTE: Harbor only supports Registry V2 API. You need to use Docker client 1.6.0 or higher.**
|
||||
|
||||
Harbor supports HTTP by default and Docker client trys to connect to Harbor using HTTPS first, so if you encounter an error as below when you pull or push images, you need to add '--insecure-registry' option to /etc/default/docker (ubuntu) or /etc/sysconfig/docker (centos):
|
||||
Harbor supports HTTP by default and Docker client tries to connect to Harbor using HTTPS first, so if you encounter an error as below when you pull or push images, you need to add '--insecure-registry' option to /etc/default/docker (ubuntu) or /etc/sysconfig/docker (centos) and restart Docker:
|
||||
*FATA[0000] Error response from daemon: v1 ping attempt failed with error:
|
||||
Get https://myregistrydomain.com:5000/v1/_ping: tls: oversized record received with length 20527.
|
||||
If this private registry supports only HTTP or HTTPS with an unknown CA certificate,please add
|
||||
@ -155,7 +179,7 @@ $ docker push 10.117.169.182/demo/ubuntu:14.04
|
||||
Repository deletion runs in two steps.
|
||||
|
||||
First, delete a repository in Harbor's UI. This is soft deletion. You can delete the entire repository or just a tag of it. After the soft deletion,
|
||||
the repository is no longer managed in Harbor, however, the files of the repository still remains in Harbor's storage.
|
||||
the repository is no longer managed in Harbor, however, the files of the repository still remain in Harbor's storage.
|
||||
|
||||

|
||||
|
||||
|
207
docs/user_guide_ova.md
Normal file
207
docs/user_guide_ova.md
Normal file
@ -0,0 +1,207 @@
|
||||
#User Guide of Harbor Virtual Appliance
|
||||
##Overview
|
||||
This guide walks you through the fundamentals of using Harbor virtual appliance. You'll learn how to use Harbor to:
|
||||
|
||||
* Manage your projects.
|
||||
* Manage members of a project.
|
||||
* Replicate projects to a remote registry.
|
||||
* Search projects and repositories.
|
||||
* Manage Harbor system if you are the system administrator:
|
||||
+ Manage users.
|
||||
+ Manage destinations.
|
||||
+ Manage replication policies.
|
||||
* Pull and push images using Docker client.
|
||||
* Delete repositories and images.
|
||||
|
||||
|
||||
##Role Based Access Control
|
||||
|
||||

|
||||
|
||||
In Harbor, images are grouped under projects. To access an image, a user should be added as a member into the project of the image. A member can have one of the three roles:
|
||||
|
||||
* **Guest**: Guest has read-only privilege for a specified project.
|
||||
* **Developer**: Developer has read and write privileges for a project.
|
||||
* **ProjectAdmin**: When creating a new project, you will be assigned the "ProjectAdmin" role to the project. Besides read-write privileges, the "ProjectAdmin" also has some management privileges, such as adding and removing members.
|
||||
|
||||
Besides the above three roles, there are two system-wide roles:
|
||||
|
||||
* **SysAdmin**: "SysAdmin" has the most privileges. In addition to the privileges mentioned above, "SysAdmin" can also list all projects, set an ordinary user as administrator and delete users. The public project "library" is also owned by the administrator.
|
||||
* **Anonymous**: When a user is not logged in, the user is considered as an "anonymous" user. An anonymous user has no access to private projects and has read-only access to public projects.
|
||||
|
||||
##User account
|
||||
Harbor supports two authentication modes:
|
||||
|
||||
* **Database(db_auth)**
|
||||
|
||||
Users are stored in the local database.
|
||||
|
||||
A user can register himself/herself in Harbor in this mode. To disable user self-registration, refer to the **[installation guide](installation_guide_ova.md)**. When self-registration is disabled, the system administrator can add users in Harbor.
|
||||
|
||||
When registering or adding a new user, the username and email must be unique in the Harbor system. The password must contain at least 8 characters, less than 20 characters with 1 lowercase letter, 1 uppercase letter and 1 numeric character.
|
||||
|
||||
When you forgot your password, you can follow the below steps to reset the password:
|
||||
|
||||
1. Click the link "Forgot Password" in the sign in page.
|
||||
2. Input the email address entered when you signed up, an email will be sent out to you for password reset.
|
||||
3. After receiving the email, click on the link in the email which directs you to a password reset web page.
|
||||
4. Input your new password and click "Save".
|
||||
|
||||
* **LDAP/Active Directory (ldap_auth)**
|
||||
|
||||
Under this authentication mode, users whose credentials are stored in an external LDAP or AD server can log in to Harbor directly.
|
||||
|
||||
When an LDAP/AD user logs in by *username* and *password*, Harbor binds to the LDAP/AD server with the **"LDAP Search DN"** and **"LDAP Search Password"** described in [installation guide](installation_guide_ova.md). If it successes, Harbor looks up the user under the LDAP entry **"LDAP Base DN"** including substree. The attribute (such as uid, cn) specified by **"LDAP UID"** is used to match a user with the *username*. If a match is found, the user's *password* is verified by a bind request to the LDAP/AD server.
|
||||
|
||||
Self-registration, changing password and resetting password are not supported anymore under LDAP/AD authentication mode because the users are managed by LDAP or AD.
|
||||
|
||||
##Managing projects
|
||||
A project in Harbor contains all repositories of an application. No images can be pushed to Harbor before the project is created. RBAC is applied to a project. There are two types of projects in Harbor:
|
||||
|
||||
* **Public**: All users have the read privilege to a public project, it's convenient for you to share some repositories with others in this way.
|
||||
* **Private**: A private project can only be accessed by users with proper privileges.
|
||||
|
||||
You can create a project after you signed in. Enabling the "Public" checkbox makes the project public.
|
||||
|
||||

|
||||
|
||||
After the project is created, you can browse repositories, users and logs using the navigation tab.
|
||||
|
||||

|
||||
|
||||
All logs can be listed by clicking "Logs". You can apply a filter by username, or operations and dates under "Advanced Search".
|
||||
|
||||

|
||||
|
||||
##Managing members of a project
|
||||
###Adding members
|
||||
You can add members with different roles to an existing project.
|
||||
|
||||

|
||||
|
||||
###Updating and removing members
|
||||
You can update or remove a member by clicking the icon on the right.
|
||||
|
||||

|
||||
|
||||
##Replicating images
|
||||
Images can be replicated between Harbor instances. It can be used to transfer images from one data center to another, or from an on-prem registry to an instance in the cloud.
|
||||
|
||||
A replication policy needs to be set up on the source instance to govern the replication process.
|
||||
One key fact about the replication is that only images are replicated between Harbor instances.
|
||||
Users, roles and other information are not replicated. As such, always keep in mind that the user, roles and policy information is individually managed by each Harbor instance.
|
||||
|
||||
The replication is project-based. When a system administrator sets a policy to a project, all repositories under the project will be replicated to the remote registry. A replication job will be scheduled for each repository.
|
||||
If the project does not exist on the remote registry, a new project is created automatically.
|
||||
If the project already exists and the replication user configured in the policy has no write privilege to it,
|
||||
the process will fail.
|
||||
|
||||
When the policy is first enabled, all images of the project are replicated to the remote registry. Images subsequently pushed to the project on the source registry
|
||||
will be incrementally replicated to the remote instance. When an image is deleted from the source registry, the policy ensures that the remote registry deletes the same image as well.
|
||||
Please note, the user and member information will not be replicated.
|
||||
|
||||
Depending on the size of the images and the network condition, the replication requires some time to complete. On the remote registry, an image is not available until
|
||||
all its layers have been synchronized from the source. If a replication job fails due to some network issue, the job will be scheduled for a retry after a few minutes.
|
||||
Always checks the log to see if there is any error of the replication. When a policy is disabled (stopped), Harbor tries to stop all existing jobs. It may take a while
|
||||
before all jobs finish. A policy can be restarted by disabling and then enabling it again.
|
||||
|
||||
To enable image replication, a policy must first be created. Click "Add New Policy" on the "Replication" tab, fill the necessary fields, if there is no destination in the list, you need to create one, and then click "OK", a policy for this project will be created. If "Enable" is chosen, the project will be replicated to the remote immediately.
|
||||
|
||||
**Note:** Set **"Verify Remote Cert"** to off according to the [installation guide](installation_guide_ova.md) if the destination uses a self-signed or untrusted certificate.
|
||||
|
||||

|
||||
|
||||
You can enable, disable or delete a policy in the policy list view. Only policies which are disabled can be edited. Only policies which are disabled and have no running jobs can be deleted. If a policy is disabled, the running jobs under it will be stopped.
|
||||
|
||||
Click on a policy, jobs belonging to this policy will be listed. A job represents the progress of replicating a repository to the remote instance.
|
||||
|
||||

|
||||
|
||||
##Searching projects and repositories
|
||||
Entering a keyword in the search field at the top lists all matching projects and repositories. The search result includes both public and private repositories you have access privilege to.
|
||||
|
||||

|
||||
|
||||
##Administrator options
|
||||
###Managing user
|
||||
Administrator can add "administrator" role to an ordinary user by toggling the switch under "Administrator". To delete a user, click on the recycle bin icon.
|
||||
|
||||

|
||||
|
||||
###Managing destination
|
||||
You can list, add, edit and delete destinations in the "Destination" tab. Only destinations which are not referenced by any policies can be edited.
|
||||
|
||||

|
||||
|
||||
###Managing replication
|
||||
You can list, edit, enable and disable policies in the "Replication" tab. Make sure the policy is disabled before you edit it.
|
||||
|
||||

|
||||
|
||||
##Pulling and pushing images using Docker client
|
||||
|
||||
**NOTE: Harbor only supports Registry V2 API. You need to use Docker client 1.6.0 or higher.**
|
||||
|
||||
Harbor uses HTTPS for secure communication by default. A self-signed certificate is generated at first boot based on its FQDN (Fully Qualified Domain Name) or IP address. If you use Docker client to interact with it, there are two options you can choose:
|
||||
|
||||
1. Trust the certificate of Harbor's CA
|
||||
Refer to the "Getting Certificate of Harbor's CA" part of [installation guide](installation_guide_ova.md).
|
||||
2. Set "--insecure-registry" option
|
||||
Add "--insecure-registry" option to /etc/default/docker (ubuntu) or /etc/sysconfig/docker (centos) and restart Docker service.
|
||||
|
||||
If Harbor is configured as using HTTP, just set the "--insecure-registry" option.
|
||||
|
||||
If the certificate used by Harbor is signed by a trusted authority, Docker should work without any additional configuration.
|
||||
|
||||
###Pulling images
|
||||
If the project that the image belongs to is private, you should sign in first:
|
||||
|
||||
```sh
|
||||
$ docker login 10.117.169.182
|
||||
```
|
||||
|
||||
You can now pull the image:
|
||||
|
||||
```sh
|
||||
$ docker pull 10.117.169.182/library/ubuntu:14.04
|
||||
```
|
||||
|
||||
**Note: Replace "10.117.169.182" with the IP address or domain name of your Harbor node.**
|
||||
|
||||
###Pushing images
|
||||
Before pushing an image, you must create a corresponding project on Harbor web UI.
|
||||
|
||||
First, log in from Docker client:
|
||||
|
||||
```sh
|
||||
$ docker login 10.117.169.182
|
||||
```
|
||||
|
||||
Tag the image:
|
||||
|
||||
```sh
|
||||
$ docker tag ubuntu:14.04 10.117.169.182/demo/ubuntu:14.04
|
||||
```
|
||||
|
||||
Push the image:
|
||||
|
||||
```sh
|
||||
$ docker push 10.117.169.182/demo/ubuntu:14.04
|
||||
```
|
||||
|
||||
**Note: Replace "10.117.169.182" with the IP address or domain name of your Harbor node.**
|
||||
|
||||
##Deleting repositories
|
||||
|
||||
Repository deletion runs in two steps.
|
||||
|
||||
First, delete a repository in Harbor's UI. This is soft deletion. You can delete the entire repository or just a tag of it. After the soft deletion,
|
||||
the repository is no longer managed in Harbor, however, the files of the repository still remain in Harbor's storage.
|
||||
|
||||

|
||||
|
||||
**CAUTION: If both tag A and tag B refer to the same image, after deleting tag A, B will also get deleted.**
|
||||
|
||||
Next, set **"Garbage Collection"** to true according to the [installation guide](installation_guide_ova.md)(skip this step if this flag has already been set) and reboot the VM, Harbor will perform garbage collection when it boots up.
|
||||
|
||||
For more information about garbage collection, please see Docker's document on [GC](https://github.com/docker/docker.github.io/blob/master/registry/garbage-collection.md).
|
@ -70,6 +70,10 @@ http {
|
||||
|
||||
proxy_buffering off;
|
||||
proxy_request_buffering off;
|
||||
}
|
||||
|
||||
location /service/notifications {
|
||||
return 404;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -87,6 +87,10 @@ http {
|
||||
|
||||
proxy_buffering off;
|
||||
proxy_request_buffering off;
|
||||
}
|
||||
|
||||
location /service/notifications {
|
||||
return 404;
|
||||
}
|
||||
}
|
||||
server {
|
||||
|
@ -18,6 +18,8 @@ package auth
|
||||
import (
|
||||
"fmt"
|
||||
"net/http"
|
||||
"net/url"
|
||||
"strings"
|
||||
"time"
|
||||
|
||||
au "github.com/docker/distribution/registry/client/auth"
|
||||
@ -37,6 +39,7 @@ type Authorizer interface {
|
||||
// And it implements interface Modifier
|
||||
type AuthorizerStore struct {
|
||||
authorizers []Authorizer
|
||||
ping *url.URL
|
||||
challenges []au.Challenge
|
||||
}
|
||||
|
||||
@ -49,15 +52,21 @@ func NewAuthorizerStore(endpoint string, insecure bool, authorizers ...Authorize
|
||||
Timeout: 30 * time.Second,
|
||||
}
|
||||
|
||||
resp, err := client.Get(buildPingURL(endpoint))
|
||||
pingURL := buildPingURL(endpoint)
|
||||
resp, err := client.Get(pingURL)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
defer resp.Body.Close()
|
||||
|
||||
challenges := ParseChallengeFromResponse(resp)
|
||||
ping, err := url.Parse(pingURL)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return &AuthorizerStore{
|
||||
authorizers: authorizers,
|
||||
ping: ping,
|
||||
challenges: challenges,
|
||||
}, nil
|
||||
}
|
||||
@ -68,6 +77,23 @@ func buildPingURL(endpoint string) string {
|
||||
|
||||
// Modify adds authorization to the request
|
||||
func (a *AuthorizerStore) Modify(req *http.Request) error {
|
||||
//only handle the requests sent to registry
|
||||
v2Index := strings.Index(req.URL.Path, "/v2/")
|
||||
if v2Index == -1 {
|
||||
return nil
|
||||
}
|
||||
|
||||
ping := url.URL{
|
||||
Host: req.URL.Host,
|
||||
Scheme: req.URL.Scheme,
|
||||
Path: req.URL.Path[:v2Index+4],
|
||||
}
|
||||
|
||||
if ping.Host != a.ping.Host || ping.Scheme != a.ping.Scheme ||
|
||||
ping.Path != a.ping.Path {
|
||||
return nil
|
||||
}
|
||||
|
||||
for _, challenge := range a.challenges {
|
||||
for _, authorizer := range a.authorizers {
|
||||
if authorizer.Scheme() == challenge.Scheme {
|
||||
|
@ -17,6 +17,7 @@ package auth
|
||||
|
||||
import (
|
||||
"net/http"
|
||||
"net/url"
|
||||
"strings"
|
||||
"testing"
|
||||
|
||||
@ -64,12 +65,17 @@ func TestModify(t *testing.T) {
|
||||
Scheme: "bearer",
|
||||
}
|
||||
|
||||
ping, err := url.Parse("http://example.com/v2/")
|
||||
if err != nil {
|
||||
t.Fatalf("failed to parse URL: %v", err)
|
||||
}
|
||||
as := &AuthorizerStore{
|
||||
authorizers: []Authorizer{authorizer},
|
||||
ping: ping,
|
||||
challenges: []auth.Challenge{challenge},
|
||||
}
|
||||
|
||||
req, err := http.NewRequest("GET", "http://example.com", nil)
|
||||
req, err := http.NewRequest("GET", "http://example.com/v2/ubuntu/manifests/14.04", nil)
|
||||
if err != nil {
|
||||
t.Fatalf("failed to create request: %v", err)
|
||||
}
|
||||
@ -86,4 +92,18 @@ func TestModify(t *testing.T) {
|
||||
if !strings.HasPrefix(header, "Bearer") {
|
||||
t.Fatal("\"Authorization\" header does not start with \"Bearer\"")
|
||||
}
|
||||
|
||||
req, err = http.NewRequest("GET", "http://example.com", nil)
|
||||
if err != nil {
|
||||
t.Fatalf("failed to create request: %v", err)
|
||||
}
|
||||
|
||||
if err = as.Modify(req); err != nil {
|
||||
t.Fatalf("failed to modify request: %v", err)
|
||||
}
|
||||
|
||||
header = req.Header.Get("Authorization")
|
||||
if len(header) != 0 {
|
||||
t.Fatal("\"Authorization\" header should not be added")
|
||||
}
|
||||
}
|
||||
|
@ -164,15 +164,15 @@ func (ra *RepositoryAPI) Delete() {
|
||||
}
|
||||
|
||||
for _, t := range tags {
|
||||
if err := rc.DeleteTag(t); err != nil {
|
||||
if err = rc.DeleteTag(t); err != nil {
|
||||
if regErr, ok := err.(*registry_error.Error); ok {
|
||||
if regErr.StatusCode != http.StatusNotFound {
|
||||
ra.CustomAbort(regErr.StatusCode, regErr.Detail)
|
||||
if regErr.StatusCode == http.StatusNotFound {
|
||||
continue
|
||||
}
|
||||
} else {
|
||||
log.Errorf("error occurred while deleting tag %s:%s: %v", repoName, t, err)
|
||||
ra.CustomAbort(http.StatusInternalServerError, "internal error")
|
||||
ra.CustomAbort(regErr.StatusCode, regErr.Detail)
|
||||
}
|
||||
log.Errorf("error occurred while deleting tag %s:%s: %v", repoName, t, err)
|
||||
ra.CustomAbort(http.StatusInternalServerError, "internal error")
|
||||
}
|
||||
log.Infof("delete tag: %s:%s", repoName, t)
|
||||
go TriggerReplicationByRepository(repoName, []string{t}, models.RepOpDelete)
|
||||
|
@ -73,6 +73,7 @@ func (sia *SystemInfoAPI) GetVolumeInfo() {
|
||||
func (sia *SystemInfoAPI) GetCert() {
|
||||
if sia.isAdmin {
|
||||
if _, err := os.Stat(defaultRootCert); !os.IsNotExist(err) {
|
||||
sia.Ctx.Output.Header("Content-Type", "application/octet-stream")
|
||||
sia.Ctx.Output.Header("Content-Disposition", "attachment; filename=ca.crt")
|
||||
http.ServeFile(sia.Ctx.ResponseWriter, sia.Ctx.Request, defaultRootCert)
|
||||
} else {
|
||||
@ -80,5 +81,5 @@ func (sia *SystemInfoAPI) GetCert() {
|
||||
sia.CustomAbort(http.StatusNotFound, "No certificate found.")
|
||||
}
|
||||
}
|
||||
sia.CustomAbort(http.StatusUnauthorized, "")
|
||||
sia.CustomAbort(http.StatusForbidden, "")
|
||||
}
|
||||
|
@ -188,14 +188,13 @@
|
||||
return directive;
|
||||
|
||||
function link(scope, element, attrs, ctrl) {
|
||||
|
||||
$(document).on('click', clickHandler);
|
||||
|
||||
function clickHandler(e) {
|
||||
$('[data-toggle="popover"]').each(function () {
|
||||
if (!$(this).is(e.target) &&
|
||||
$(this).has(e.target).length === 0 &&
|
||||
$('.popover').has(e.target).length === 0) {
|
||||
$(this).parent().popover('hide');
|
||||
$('[data-toggle="popover"],[data-original-title]').each(function () {
|
||||
if (!$(this).is(e.target) && $(this).has(e.target).length === 0 && $('.popover').has(e.target).length === 0) {
|
||||
(($(this).popover('hide').data('bs.popover')||{}).inState||{}).click = false;
|
||||
}
|
||||
});
|
||||
var targetId = $(e.target).attr('id');
|
||||
|
@ -1,17 +1,3 @@
|
||||
<!--
|
||||
Copyright (c) 2016 VMware, Inc. All Rights Reserved.
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
You may obtain a copy of the License at
|
||||
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing, software
|
||||
distributed under the License is distributed on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
-->
|
||||
<a role="button" tab-index="0" data-trigger="focus" data-toggle="popover" data-placement="right" data-title="//vm.helpTitle//">
|
||||
<a href="javascript:void(0);" role="button" tabindex="0" data-trigger="focus" data-toggle="popover" data-placement="right">
|
||||
<span class="glyphicon glyphicon-info-sign"></span>
|
||||
</a>
|
@ -29,6 +29,7 @@
|
||||
'helpTitle': '@',
|
||||
'content': '@'
|
||||
},
|
||||
'replace': true,
|
||||
'link': link,
|
||||
'controller': InlineHelpController,
|
||||
'controllerAs': 'vm',
|
||||
|
@ -1,3 +1,17 @@
|
||||
<!--
|
||||
Copyright (c) 2016 VMware, Inc. All Rights Reserved.
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
You may obtain a copy of the License at
|
||||
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing, software
|
||||
distributed under the License is distributed on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
-->
|
||||
<nav aria-label="Page navigation" class="pull-left">
|
||||
<ul class="pagination" style="margin: 0 0 0 10px;">
|
||||
<li ng-class="vm.disabledFirst" ng-show="vm.visible">
|
||||
|
@ -1,3 +1,17 @@
|
||||
/*
|
||||
Copyright (c) 2016 VMware, Inc. All Rights Reserved.
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
You may obtain a copy of the License at
|
||||
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing, software
|
||||
distributed under the License is distributed on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
*/
|
||||
(function() {
|
||||
|
||||
'use strict';
|
||||
@ -198,218 +212,6 @@
|
||||
}
|
||||
}
|
||||
|
||||
function gotoFirst() {
|
||||
ctrl.page = 1;
|
||||
tc.setTime(0);
|
||||
drawButtons(0);
|
||||
|
||||
toggleFirst();
|
||||
toggleLast();
|
||||
|
||||
togglePageButton();
|
||||
togglePrevious(tc.canDecrement());
|
||||
toggleNext(tc.canIncrement());
|
||||
}
|
||||
(function() {
|
||||
|
||||
'use strict';
|
||||
|
||||
angular
|
||||
.module('harbor.paginator')
|
||||
.directive('paginator', paginator);
|
||||
|
||||
PaginatorController.$inject = [];
|
||||
|
||||
function PaginatorController() {
|
||||
var vm = this;
|
||||
}
|
||||
|
||||
paginator.$inject = [];
|
||||
|
||||
function paginator() {
|
||||
var directive = {
|
||||
'restrict': 'E',
|
||||
'templateUrl': '/static/resources/js/components/paginator/paginator.directive.html',
|
||||
'scope': {
|
||||
'totalCount': '@',
|
||||
'pageSize': '@',
|
||||
'page': '=',
|
||||
'displayCount': '@'
|
||||
},
|
||||
'link': link,
|
||||
'controller': PaginatorController,
|
||||
'controllerAs': 'vm',
|
||||
'bindToController': true
|
||||
};
|
||||
return directive;
|
||||
|
||||
function link(scope, element, attrs, ctrl) {
|
||||
scope.$watch('vm.page', function(current) {
|
||||
if(current) {
|
||||
ctrl.page = current;
|
||||
togglePageButton();
|
||||
}
|
||||
});
|
||||
|
||||
var tc;
|
||||
|
||||
scope.$watch('vm.totalCount', function(current) {
|
||||
if(current) {
|
||||
var totalCount = current;
|
||||
|
||||
tc = new TimeCounter();
|
||||
|
||||
console.log('Total Count:' + totalCount + ', Page Size:' + ctrl.pageSize + ', Display Count:' + ctrl.displayCount + ', Page:' + ctrl.page);
|
||||
|
||||
ctrl.buttonCount = Math.ceil(totalCount / ctrl.pageSize);
|
||||
|
||||
if(ctrl.buttonCount <= ctrl.displayCount) {
|
||||
tc.setMaximum(1);
|
||||
ctrl.visible = false;
|
||||
}else{
|
||||
tc.setMaximum(Math.ceil(ctrl.buttonCount / ctrl.displayCount));
|
||||
ctrl.visible = true;
|
||||
}
|
||||
|
||||
ctrl.gotoFirst = gotoFirst;
|
||||
ctrl.gotoLast = gotoLast;
|
||||
|
||||
if(ctrl.buttonCount < ctrl.page) {
|
||||
ctrl.page = ctrl.buttonCount;
|
||||
}
|
||||
|
||||
ctrl.previous = previous;
|
||||
ctrl.next = next;
|
||||
|
||||
drawButtons(tc.getTime());
|
||||
|
||||
togglePrevious(tc.canDecrement());
|
||||
toggleNext(tc.canIncrement());
|
||||
|
||||
toggleFirst();
|
||||
toggleLast();
|
||||
|
||||
togglePageButton();
|
||||
|
||||
}
|
||||
});
|
||||
|
||||
var TimeCounter = function() {
|
||||
this.time = 0;
|
||||
this.minimum = 0;
|
||||
this.maximum = 0;
|
||||
};
|
||||
|
||||
TimeCounter.prototype.setMaximum = function(maximum) {
|
||||
this.maximum = maximum;
|
||||
};
|
||||
|
||||
TimeCounter.prototype.getMaximum = function() {
|
||||
return this.maximum;
|
||||
};
|
||||
|
||||
TimeCounter.prototype.increment = function() {
|
||||
if(this.time < this.maximum) {
|
||||
++this.time;
|
||||
if((ctrl.page % ctrl.displayCount) != 0) {
|
||||
ctrl.page = this.time * ctrl.displayCount;
|
||||
}
|
||||
++ctrl.page;
|
||||
}
|
||||
};
|
||||
|
||||
TimeCounter.prototype.canIncrement = function() {
|
||||
if(this.time + 1 < this.maximum) {
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
};
|
||||
|
||||
TimeCounter.prototype.decrement = function() {
|
||||
if(this.time > this.minimum) {
|
||||
if(this.time === 0) {
|
||||
ctrl.page = ctrl.displayCount;
|
||||
}else{
|
||||
ctrl.page = this.time * ctrl.displayCount;
|
||||
}
|
||||
--this.time;
|
||||
}
|
||||
};
|
||||
|
||||
TimeCounter.prototype.canDecrement = function() {
|
||||
if(this.time > this.minimum) {
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
};
|
||||
|
||||
TimeCounter.prototype.getTime = function() {
|
||||
return this.time;
|
||||
};
|
||||
|
||||
TimeCounter.prototype.setTime = function(time) {
|
||||
this.time = time;
|
||||
};
|
||||
|
||||
function drawButtons(time) {
|
||||
element.find('li[tag="pagination-button"]').remove();
|
||||
var buttons = [];
|
||||
for(var i = 1; i <= ctrl.displayCount; i++) {
|
||||
var displayNumber = ctrl.displayCount * time + i;
|
||||
if(displayNumber <= ctrl.buttonCount) {
|
||||
buttons.push('<li tag="pagination-button"><a href="javascript:void(0)" page="' + displayNumber + '">' + displayNumber + '<span class="sr-only"></span></a></li>');
|
||||
}
|
||||
}
|
||||
|
||||
$(buttons.join(''))
|
||||
.insertAfter(element.find('ul li:eq(' + (ctrl.visible ? 1 : 0) + ')')).end()
|
||||
.on('click', buttonClickHandler);
|
||||
}
|
||||
|
||||
function togglePrevious(status) {
|
||||
ctrl.disabledPrevious = status ? '' : 'disabled';
|
||||
toggleFirst();
|
||||
toggleLast();
|
||||
}
|
||||
|
||||
function toggleNext(status) {
|
||||
ctrl.disabledNext = status ? '' : 'disabled';
|
||||
toggleFirst();
|
||||
toggleLast();
|
||||
}
|
||||
|
||||
function toggleFirst() {
|
||||
ctrl.disabledFirst = (ctrl.page > 1) ? '' : 'disabled';
|
||||
}
|
||||
|
||||
function toggleLast() {
|
||||
ctrl.disabledLast = (ctrl.page < ctrl.buttonCount) ? '' : 'disabled';
|
||||
}
|
||||
|
||||
function buttonClickHandler(e) {
|
||||
ctrl.page = $(e.target).attr('page');
|
||||
togglePageButton();
|
||||
togglePrevious(tc.canDecrement());
|
||||
toggleNext(tc.canIncrement());
|
||||
|
||||
scope.$apply();
|
||||
}
|
||||
|
||||
function togglePageButton() {
|
||||
element.find('li[tag="pagination-button"]').removeClass('active');
|
||||
element.find('li[tag="pagination-button"] a[page="' + ctrl.page + '"]').parent().addClass('active');
|
||||
}
|
||||
|
||||
function previous() {
|
||||
if(tc.canDecrement()) {
|
||||
tc.decrement();
|
||||
drawButtons(tc.getTime());
|
||||
togglePageButton();
|
||||
togglePrevious(tc.canDecrement());
|
||||
toggleNext(tc.canIncrement());
|
||||
}
|
||||
}
|
||||
|
||||
function gotoFirst() {
|
||||
ctrl.page = 1;
|
||||
tc.setTime(0);
|
||||
@ -448,30 +250,4 @@
|
||||
}
|
||||
}
|
||||
|
||||
})();
|
||||
function next() {
|
||||
if(tc.canIncrement()) {
|
||||
tc.increment();
|
||||
drawButtons(tc.getTime());
|
||||
togglePageButton();
|
||||
togglePrevious(tc.canDecrement());
|
||||
toggleNext(tc.canIncrement());
|
||||
}
|
||||
}
|
||||
|
||||
function gotoLast() {
|
||||
ctrl.page = ctrl.buttonCount;
|
||||
tc.setTime(Math.ceil(ctrl.buttonCount / ctrl.displayCount) - 1);
|
||||
drawButtons(tc.getTime());
|
||||
|
||||
toggleFirst();
|
||||
toggleLast();
|
||||
|
||||
togglePageButton();
|
||||
togglePrevious(tc.canDecrement());
|
||||
toggleNext(tc.canIncrement());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
})();
|
@ -1,3 +1,17 @@
|
||||
/*
|
||||
Copyright (c) 2016 VMware, Inc. All Rights Reserved.
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
You may obtain a copy of the License at
|
||||
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing, software
|
||||
distributed under the License is distributed on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
*/
|
||||
(function() {
|
||||
|
||||
'use strict';
|
||||
|
@ -1,4 +1,3 @@
|
||||
<a href="javascript:void(0)">
|
||||
<span class="glyphicon glyphicon-info-sign" role="button" data-trigger="click" data-toggle="popover" data-placement="right">
|
||||
</span>
|
||||
<a href="javascript:void(0);" role="button" tabindex="0" data-trigger="click" data-toggle="popover" data-placement="right">
|
||||
<span class="glyphicon glyphicon-info-sign"></span>
|
||||
</a>
|
||||
|
@ -46,6 +46,7 @@
|
||||
return directive;
|
||||
|
||||
function link(scope, element, attrs, ctrl) {
|
||||
|
||||
element
|
||||
.popover({
|
||||
'template': '<div class="popover" role="tooltip"><div class="arrow"></div><div class="popover-title"></div><div class="popover-content"></div></div>',
|
||||
@ -71,11 +72,11 @@
|
||||
.on('inserted.bs.popover', function(e){
|
||||
var self = jQuery(this);
|
||||
$('[type="text"]:input', self.parent())
|
||||
.on('click', function() {
|
||||
.on('click', function(e) {
|
||||
$(this).select();
|
||||
});
|
||||
self.parent().find('.glyphicon.glyphicon-remove-circle').on('click', function() {
|
||||
element.trigger('click');
|
||||
self.trigger('click');
|
||||
});
|
||||
});
|
||||
|
||||
|
@ -0,0 +1,71 @@
|
||||
/*
|
||||
Copyright (c) 2016 VMware, Inc. All Rights Reserved.
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
You may obtain a copy of the License at
|
||||
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing, software
|
||||
distributed under the License is distributed on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
*/
|
||||
(function() {
|
||||
|
||||
'use strict';
|
||||
|
||||
angular
|
||||
.module('harbor.validator')
|
||||
.directive('charsLength', charsLength);
|
||||
|
||||
charsLength.$inject = ['ASCII_CHARS'];
|
||||
|
||||
function charsLength(ASCII_CHARS) {
|
||||
var directive = {
|
||||
'require': 'ngModel',
|
||||
'scope': {
|
||||
min: '@',
|
||||
max: '@'
|
||||
},
|
||||
'link': link
|
||||
};
|
||||
|
||||
return directive;
|
||||
|
||||
function link(scope, element, attrs, ctrl) {
|
||||
|
||||
ctrl.$validators.charsLength = validator;
|
||||
|
||||
function validator(modelValue, viewValue) {
|
||||
if(ctrl.$isEmpty(modelValue)) {
|
||||
return true;
|
||||
}
|
||||
|
||||
var actualLength = 0;
|
||||
|
||||
if(ASCII_CHARS.test(modelValue)) {
|
||||
actualLength = modelValue.length;
|
||||
}else{
|
||||
for(var i = 0; i < modelValue.length; i++) {
|
||||
ASCII_CHARS.test(modelValue[i]) ? actualLength += 1 : actualLength += 2;
|
||||
}
|
||||
}
|
||||
|
||||
if(attrs.min && actualLength < attrs.min) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if(attrs.max && actualLength > attrs.max) {
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
})();
|
@ -40,10 +40,7 @@
|
||||
|
||||
function validator(modelValue, viewValue) {
|
||||
|
||||
console.log('modelValue:' + modelValue + ', viewValue:' + viewValue);
|
||||
|
||||
if(ctrl.$isEmpty(modelValue)) {
|
||||
console.log('Model value is empty.');
|
||||
return true;
|
||||
}
|
||||
|
||||
|
@ -21,6 +21,7 @@
|
||||
.constant('INVALID_CHARS', [",","~","#", "$", "%"])
|
||||
.constant('PASSWORD_REGEXP', /^(?=.*\d)(?=.*[a-z])(?=.*[A-Z])(?!.*\s).{8,20}$/)
|
||||
.constant('PROJECT_REGEXP', /^[a-z0-9](?:-*[a-z0-9])*(?:[._][a-z0-9](?:-*[a-z0-9])*)*$/)
|
||||
.constant('EMAIL_REGEXP', /^(([^<>()[\]\.,;:\s@\"]+(\.[^<>()[\]\.,;:\s@\"]+)*)|(\".+\"))@((\[[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}\])|(([a-zA-Z\-0-9]+\.)+[a-zA-Z]{2,}))$/);
|
||||
.constant('EMAIL_REGEXP', /^(([^<>()[\]\.,;:\s@\"]+(\.[^<>()[\]\.,;:\s@\"]+)*)|(\".+\"))@((\[[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}\])|(([a-zA-Z\-0-9]+\.)+[a-zA-Z]{2,}))$/)
|
||||
.constant('ASCII_CHARS', /^[\000-\177]*$/);
|
||||
|
||||
})();
|
@ -42,11 +42,11 @@
|
||||
<div class="form-group">
|
||||
<label for="fullName" class="col-sm-3 control-label">// 'full_name' | tr //:</label>
|
||||
<div class="col-sm-7">
|
||||
<input type="text" class="form-control" id="fullName" ng-model="user.realname" name="uFullName" required maxlength="20" invalid-chars>
|
||||
<input type="text" class="form-control" id="fullName" ng-model="user.realname" name="uFullName" required chars-length max="20" invalid-chars>
|
||||
<div class="error-message" ng-messages="form.uFullName.$touched && form.uFullName.$error">
|
||||
<span ng-message="required">// 'full_name_is_required' | tr //</span>
|
||||
<span ng-message="invalidChars">// 'full_name_contains_illegal_chars' | tr //</span>
|
||||
<span ng-message="maxlength">// 'full_name_is_too_long' | tr //</span>
|
||||
<span ng-message="charsLength">// 'full_name_is_too_long' | tr //</span>
|
||||
</div>
|
||||
</div>
|
||||
<div class="col-sm-2">
|
||||
@ -56,9 +56,9 @@
|
||||
<div class="form-group">
|
||||
<label for="comments" class="col-sm-3 control-label">// 'comments' | tr //:</label>
|
||||
<div class="col-sm-7">
|
||||
<input type="text" class="form-control" id="comments" ng-model="user.comment" name="uComments" maxlength="20">
|
||||
<input type="text" class="form-control" id="comments" ng-model="user.comment" name="uComments" chars-length max="20">
|
||||
<div class="error-message" ng-messages="form.uComments.$touched && form.uComments.$error">
|
||||
<span ng-show="maxlength">// 'comment_is_too_long' | tr //</span>
|
||||
<span ng-message="charsLength">// 'comment_is_too_long' | tr //</span>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
@ -131,6 +131,7 @@
|
||||
<script src="/static/resources/js/components/validator/invalid-chars.validator.js"></script>
|
||||
<script src="/static/resources/js/components/validator/project-name.validator.js"></script>
|
||||
<script src="/static/resources/js/components/validator/email.validator.js"></script>
|
||||
<script src="/static/resources/js/components/validator/chars-length.validator.js"></script>
|
||||
|
||||
<script src="/static/resources/js/components/search/search.module.js"></script>
|
||||
<script src="/static/resources/js/components/search/search.directive.js"></script>
|
||||
|
@ -30,12 +30,12 @@
|
||||
<div class="form-group">
|
||||
<label for="username" class="col-sm-3 control-label">// 'username' | tr //:</label>
|
||||
<div class="col-sm-7">
|
||||
<input type="text" class="form-control" id="username" ng-model="user.username" name="uUsername" ng-model-options="{ updateOn: 'blur' }" required maxlength="20" invalid-chars user-exists data-target="username">
|
||||
<input type="text" class="form-control" id="username" ng-model="user.username" name="uUsername" required invalid-chars user-exists data-target="username" maxlength="20">
|
||||
<div class="error-message" ng-messages="(form.$submitted || form.uUsername.$touched) && form.uUsername.$error" >
|
||||
<span ng-message="required">// 'username_is_required' | tr //</span>
|
||||
<span ng-message="maxlength">// 'username_is_too_long' | tr //</span>
|
||||
<span ng-message="invalidChars">// 'username_contains_illegal_chars' | tr //</span>
|
||||
<span ng-message="userExists">// 'username_has_been_taken' | tr //</span>
|
||||
<span ng-message="invalidChars">// 'username_contains_illegal_chars' | tr //</span>
|
||||
</div>
|
||||
</div>
|
||||
<div class="col-sm-2">
|
||||
@ -45,7 +45,7 @@
|
||||
<div class="form-group">
|
||||
<label for="email" class="col-sm-3 control-label">// 'email' | tr //:</label>
|
||||
<div class="col-sm-7">
|
||||
<input type="text" class="form-control" id="email" ng-model="user.email" name="uEmail" ng-model-options="{ updateOn: 'blur' }" required user-exists data-target="email" email>
|
||||
<input type="text" class="form-control" id="email" ng-model="user.email" name="uEmail" required user-exists data-target="email" email>
|
||||
<div class="error-message" ng-messages="(form.$submitted || form.uEmail.$touched) && form.uEmail.$error">
|
||||
<span ng-message="required">// 'email_is_required' | tr //</span>
|
||||
<span ng-message="email">// 'email_content_illegal' | tr //</span>
|
||||
@ -60,11 +60,11 @@
|
||||
<div class="form-group">
|
||||
<label for="fullName" class="col-sm-3 control-label">// 'full_name' | tr //:</label>
|
||||
<div class="col-sm-7">
|
||||
<input type="text" class="form-control" id="fullName" ng-model="user.fullName" name="uFullName" ng-model-options="{ updateOn: 'blur' }" required maxlength="20" invalid-chars>
|
||||
<input type="text" class="form-control" id="fullName" ng-model="user.fullName" name="uFullName" required chars-length max="20" invalid-chars>
|
||||
<div class="error-message" ng-messages="(form.$submitted || form.uFullName.$touched) && form.uFullName.$error">
|
||||
<span ng-message="required">// 'full_name_is_required' | tr //</span>
|
||||
<span ng-message="invalidChars">// 'full_name_contains_illegal_chars' | tr //</span>
|
||||
<span ng-message="maxlength">// 'full_name_is_too_long' | tr //</span>
|
||||
<span ng-message="charsLength">// 'full_name_is_too_long' | tr //</span>
|
||||
</div>
|
||||
<p class="help-block small-size-fonts">// 'full_name_desc' | tr //</p>
|
||||
</div>
|
||||
@ -75,7 +75,7 @@
|
||||
<div class="form-group">
|
||||
<label for="password" class="col-sm-3 control-label">// 'password' | tr //:</label>
|
||||
<div class="col-sm-7">
|
||||
<input type="password" class="form-control" id="password" ng-model="user.password" name="uPassword" ng-model-options="{ updateOn: 'blur' }" required password>
|
||||
<input type="password" class="form-control" id="password" ng-model="user.password" name="uPassword" required password>
|
||||
<div class="error-message" ng-messages="(form.$submitted || form.uPassword.$touched) && form.uPassword.$error">
|
||||
<span ng-message="required">// 'password_is_required' | tr //</span>
|
||||
<span ng-message="password">// 'password_is_invalid' | tr //</span>
|
||||
@ -89,7 +89,7 @@
|
||||
<div class="form-group">
|
||||
<label for="confirmPassword" class="col-sm-3 control-label">// 'confirm_password' | tr //:</label>
|
||||
<div class="col-sm-7">
|
||||
<input type="password" class="form-control" id="confirmPassword" ng-model="user.confirmPassword" name="uConfirmPassword" ng-model-options="{ updateOn: 'blur' }" compare-to="user.password">
|
||||
<input type="password" class="form-control" id="confirmPassword" ng-model="user.confirmPassword" name="uConfirmPassword" compare-to="user.password">
|
||||
<div class="error-message" ng-messages="(form.$submitted || form.uConfirmPassword.$touched) && form.uConfirmPassword.$error">
|
||||
<span ng-message="compareTo">// 'password_does_not_match' | tr //</span>
|
||||
</div>
|
||||
@ -101,9 +101,9 @@
|
||||
<div class="form-group">
|
||||
<label for="comments" class="col-sm-3 control-label">// 'comments' | tr //:</label>
|
||||
<div class="col-sm-7">
|
||||
<input type="text" class="form-control" id="comments" ng-model="user.comment" name="uComments" ng-model-options="{ updateOn: 'blur' }" maxlength="20">
|
||||
<input type="text" class="form-control" id="comments" ng-model="user.comment" name="uComments" chars-length max="20">
|
||||
<div class="error-message" ng-messages="(form.$submitted || form.uComments.$touched) && form.uComments.$error">
|
||||
<span ng-show="maxlength">// 'comment_is_too_long' | tr //</span>
|
||||
<span ng-message="charsLength">// 'comment_is_too_long' | tr //</span>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
@ -1,6 +1,8 @@
|
||||
#!/bin/bash
|
||||
|
||||
dir=harbor_logs
|
||||
outputdir=/tmp
|
||||
outputfolder=harbor_logs
|
||||
dir=$outputdir/$outputfolder
|
||||
mkdir -p $dir
|
||||
|
||||
echo "Version" >> $dir/docker
|
||||
@ -15,7 +17,29 @@ docker ps >> $dir/docker
|
||||
docker-compose version >> $dir/docker-compose
|
||||
|
||||
base_dir="$( cd "$( dirname "${BASH_SOURCE[0]}" )" && pwd )"
|
||||
cp -r $base_dir/../harbor/common $dir/
|
||||
cp -r $base_dir/../script $dir/
|
||||
cp $base_dir/../harbor/harbor.cfg $dir/
|
||||
cp -r /var/log/harbor $dir/
|
||||
tar --remove-files -zcf $dir.tar.gz $dir
|
||||
|
||||
properties=(
|
||||
email_server
|
||||
email_server_port
|
||||
email_username
|
||||
email_password
|
||||
email_from
|
||||
harbor_admin_password
|
||||
ldap_url
|
||||
ldap_searchdn
|
||||
ldap_search_pwd
|
||||
ldap_basedn
|
||||
db_password
|
||||
)
|
||||
|
||||
for property in "${properties[@]}"
|
||||
do
|
||||
sed -i -r "s%#?$property\s*=\s*.*%$property = %" $dir/harbor.cfg
|
||||
done
|
||||
|
||||
tar --remove-files -zcf $outputfolder.tar.gz -C $outputdir $outputfolder
|
||||
|
||||
echo "$outputfolder.tar.gz is generated in current directory."
|
@ -10,9 +10,6 @@ function down {
|
||||
function up {
|
||||
base_dir="$( cd "$( dirname "${BASH_SOURCE[0]}" )" && pwd )"
|
||||
$base_dir/start_harbor.sh
|
||||
|
||||
echo "Resetting DNS and hostname using vami_ovf_process..."
|
||||
/opt/vmware/share/vami/vami_ovf_process --setnetwork || true
|
||||
}
|
||||
|
||||
#Configure Harbor
|
||||
@ -21,18 +18,25 @@ function configure {
|
||||
$base_dir/config.sh
|
||||
}
|
||||
|
||||
function getRegistryVersion {
|
||||
registry_version=""
|
||||
base_dir="$( cd "$( dirname "${BASH_SOURCE[0]}" )" && pwd )"
|
||||
registry_version=$(sed -n -e 's|.*library/registry:||p' $base_dir/../harbor/docker-compose.yml)
|
||||
if [ -z registry_version ]
|
||||
then
|
||||
registry_version="latest"
|
||||
fi
|
||||
}
|
||||
|
||||
#Garbage collectoin
|
||||
function gc {
|
||||
echo "======================= $(date)====================="
|
||||
|
||||
#the registry image
|
||||
image=$1
|
||||
|
||||
base_dir="$( cd "$( dirname "${BASH_SOURCE[0]}" )" && pwd )"
|
||||
getRegistryVersion
|
||||
|
||||
docker run --name gc --rm --volume /data/registry:/storage \
|
||||
--volume $base_dir/../harbor/common/config/registry/:/etc/registry/ \
|
||||
$image garbage-collect /etc/registry/config.yml
|
||||
registry:$registry_version garbage-collect /etc/registry/config.yml
|
||||
|
||||
echo "===================================================="
|
||||
}
|
||||
@ -88,55 +92,16 @@ function configureHarborCfg {
|
||||
|
||||
if [ -n "$cfg_key" ]
|
||||
then
|
||||
sed -i -r s%"#?$cfg_key\s*=\s*.*"%"$cfg_key = $cfg_value"% $cfg_file
|
||||
cfg_value=$(echo "$cfg_value" | sed -r -e 's%[\/&%]%\\&%g')
|
||||
sed -i -r "s%#?$cfg_key\s*=\s*.*%$cfg_key = $cfg_value%" $cfg_file
|
||||
fi
|
||||
}
|
||||
|
||||
function configureDockerDNS {
|
||||
echo "Resetting DNS using vami_ovf_process..."
|
||||
/opt/vmware/share/vami/vami_ovf_process --setnetwork || true
|
||||
|
||||
sed -n -e 's/^nameserver //p' /etc/resolv.conf > /tmp/dns
|
||||
readarray dns < /tmp/dns
|
||||
|
||||
opts=""
|
||||
for d in "${dns[@]}"
|
||||
do
|
||||
if [ -n "$d" ]
|
||||
then
|
||||
opts="$opts --dns=$d"
|
||||
fi
|
||||
done
|
||||
rm /tmp/dns
|
||||
|
||||
domain=$(sed -n -e 's/^domain //p' /etc/resolv.conf)
|
||||
if [ -n "$domain" ]
|
||||
then
|
||||
opts="$opts --dns-search=$domain"
|
||||
fi
|
||||
|
||||
search=$(sed -n -e 's/^search //p' /etc/resolv.conf)
|
||||
if [ -n "$search" ]
|
||||
then
|
||||
searcharray=($search)
|
||||
for s in "${searcharray[@]}"
|
||||
do
|
||||
if [ -n "$s" ]
|
||||
then
|
||||
opts="$opts --dns-search=$s"
|
||||
fi
|
||||
done
|
||||
fi
|
||||
|
||||
echo Setting docker: $opts
|
||||
echo DOCKER_OPTS=$opts > /etc/default/docker
|
||||
systemctl restart docker
|
||||
}
|
||||
|
||||
function pushPhoton {
|
||||
set +e
|
||||
basedir="$( cd "$( dirname "${BASH_SOURCE[0]}" )" && pwd )"
|
||||
registry_version=$(sed -n -e 's|.*library/registry:||p' $basedir/../harbor/docker-compose.yml)
|
||||
|
||||
getRegistryVersion
|
||||
|
||||
docker run -d --name photon_pusher -v /data/registry:/var/lib/registry -p 5000:5000 registry:$registry_version
|
||||
docker tag photon:1.0 127.0.0.1:5000/library/photon:1.0
|
||||
sleep 5
|
||||
|
@ -132,15 +132,6 @@ function secure {
|
||||
}
|
||||
|
||||
function detectHostname {
|
||||
#echo "Read attribute using ovfenv: [ vami.domain.Harbor ]"
|
||||
#hostname=$(ovfenv -k vami.domain.Harbor)
|
||||
#if [ -n $hostname ]
|
||||
#then
|
||||
# echo "Get hostname from ovfenv: $hostname"
|
||||
# return
|
||||
#fi
|
||||
echo "Resetting hostname using vami_ovf_process..."
|
||||
/opt/vmware/share/vami/vami_ovf_process --setnetwork || true
|
||||
hostname=$(hostname --fqdn) || true
|
||||
if [ -n $hostname ]
|
||||
then
|
||||
@ -166,7 +157,7 @@ fi
|
||||
if [ -n "$hostname" ]
|
||||
then
|
||||
echo "Hostname: $hostname"
|
||||
configureHarborCfg hostname $hostname
|
||||
configureHarborCfg "hostname" "$hostname"
|
||||
else
|
||||
echo "Failed to get the hostname"
|
||||
exit 1
|
||||
@ -193,16 +184,11 @@ do
|
||||
echo "Read attribute using ovfenv: [ $attr ]"
|
||||
value=$(ovfenv -k $attr)
|
||||
|
||||
#ldap search password and email password can be null
|
||||
if [ -n "$value" ] || [ "$attr" = "ldap_search_pwd" ] \
|
||||
|| [ "$attr" = "email_password" ]
|
||||
then
|
||||
#if [ "$attr" = ldap_search_pwd ] \
|
||||
# || [ "$attr" = email_password ]
|
||||
#then
|
||||
# bs=$(echo $value | base64)
|
||||
# value={base64}$bs
|
||||
#fi
|
||||
configureHarborCfg $attr $value
|
||||
fi
|
||||
#if [ "$attr" = ldap_search_pwd ] \
|
||||
# || [ "$attr" = email_password ]
|
||||
#then
|
||||
# bs=$(echo $value | base64)
|
||||
# value={base64}$bs
|
||||
#fi
|
||||
configureHarborCfg "$attr" "$value"
|
||||
done
|
@ -13,7 +13,7 @@ value=$(ovfenv -k root_pwd)
|
||||
if [ -n "$value" ]
|
||||
then
|
||||
echo "Resetting root password..."
|
||||
printf "$value\n$value\n" | passwd root
|
||||
printf "%s\n%s\n" "$value" "$value" | passwd root
|
||||
fi
|
||||
|
||||
#configure SSH
|
||||
@ -25,10 +25,8 @@ addIptableRules
|
||||
echo "Installing docker compose..."
|
||||
installDockerCompose
|
||||
|
||||
#echo "Starting docker service..."
|
||||
#systemctl start docker
|
||||
echo "Configuring docker..."
|
||||
configureDockerDNS
|
||||
echo "Starting docker service..."
|
||||
systemctl start docker
|
||||
|
||||
echo "Uncompress Harbor offline instaler tar..."
|
||||
tar -zxvf $base_dir/../harbor-offline-installer*.tgz -C $base_dir/../
|
||||
|
@ -9,7 +9,7 @@ echo "Read attribute using ovfenv: [ auth_mode ]"
|
||||
auth_mode=$(ovfenv -k auth_mode)
|
||||
if [ -n "$auth_mode" ]
|
||||
then
|
||||
configureHarborCfg auth_mode $auth_mode
|
||||
configureHarborCfg "auth_mode" "$auth_mode"
|
||||
fi
|
||||
|
||||
#Configure password of Harbor administrator
|
||||
@ -17,7 +17,7 @@ echo "Read attribute using ovfenv: [ harbor_admin_password ]"
|
||||
adm_pwd=$(ovfenv -k harbor_admin_password)
|
||||
if [ -n "$adm_pwd" ]
|
||||
then
|
||||
configureHarborCfg harbor_admin_password $adm_pwd
|
||||
configureHarborCfg "harbor_admin_password" "$adm_pwd"
|
||||
fi
|
||||
|
||||
#Configure password of database
|
||||
@ -25,7 +25,7 @@ echo "Read attribute using ovfenv: [ db_password ]"
|
||||
db_pwd=$(ovfenv -k db_password)
|
||||
if [ -n "$db_pwd" ]
|
||||
then
|
||||
configureHarborCfg db_password $db_pwd
|
||||
configureHarborCfg "db_password" "$db_pwd"
|
||||
fi
|
||||
|
||||
#Configure other attrs
|
||||
|
@ -24,7 +24,7 @@ then
|
||||
echo "GC enabled, starting garbage collection..."
|
||||
#If the registry contains no images, the gc will fail.
|
||||
#So append a true to avoid failure.
|
||||
gc registry:2.5.0 2>&1 >> /var/log/harbor/gc.log || true
|
||||
gc 2>&1 >> /var/log/harbor/gc.log || true
|
||||
else
|
||||
echo "GC disabled, skip garbage collection"
|
||||
fi
|
||||
@ -33,9 +33,6 @@ fi
|
||||
echo "Configuring Harbor..."
|
||||
configure
|
||||
|
||||
echo "Configuring docker..."
|
||||
configureDockerDNS
|
||||
|
||||
#Start Harbor
|
||||
echo "Starting Harbor..."
|
||||
up
|
||||
|
Loading…
Reference in New Issue
Block a user