mirror of
https://github.com/goharbor/harbor.git
synced 2024-12-19 23:28:20 +01:00
Merge branch 'job-service' into new-ui-with-sync-image
This commit is contained in:
commit
91eacb895c
53
.travis.yml
53
.travis.yml
@ -1,3 +1,5 @@
|
||||
sudo: true
|
||||
|
||||
language: go
|
||||
|
||||
go:
|
||||
@ -5,10 +7,32 @@ go:
|
||||
|
||||
go_import_path: github.com/vmware/harbor
|
||||
|
||||
#service:
|
||||
# - mysql
|
||||
services:
|
||||
- docker
|
||||
- mysql
|
||||
|
||||
env: DB_HOST=127.0.0.1 DB_PORT=3306 DB_USR=root DB_PWD=
|
||||
dist: trusty
|
||||
addons:
|
||||
apt:
|
||||
packages:
|
||||
- mysql-server-5.6
|
||||
- mysql-client-core-5.6
|
||||
- mysql-client-5.6
|
||||
|
||||
env:
|
||||
DB_HOST: 127.0.0.1
|
||||
DB_PORT: 3306
|
||||
DB_USR: root
|
||||
DB_PWD:
|
||||
DOCKER_COMPOSE_VERSION: 1.7.1
|
||||
HARBOR_ADMIN: admin
|
||||
HARBOR_ADMIN_PASSWD: Harbor12345
|
||||
|
||||
before_install:
|
||||
- ./tests/hostcfg.sh
|
||||
- cd Deploy
|
||||
- ./prepare
|
||||
- cd ..
|
||||
|
||||
install:
|
||||
- sudo apt-get update && sudo apt-get install -y libldap2-dev
|
||||
@ -29,12 +53,29 @@ install:
|
||||
- go get -d github.com/go-sql-driver/mysql
|
||||
- go get github.com/golang/lint/golint
|
||||
- go get github.com/GeertJohan/fgt
|
||||
- sudo apt-get install -y -o Dpkg::Options::="--force-confdef" -o Dpkg::Options::="--force-confold" docker-engine=1.11.1-0~trusty
|
||||
- sudo rm /usr/local/bin/docker-compose
|
||||
- curl -L https://github.com/docker/compose/releases/download/${DOCKER_COMPOSE_VERSION}/docker-compose-`uname -s`-`uname -m` > docker-compose
|
||||
- chmod +x docker-compose
|
||||
- sudo mv docker-compose /usr/local/bin
|
||||
- go get github.com/dghubble/sling
|
||||
- go get github.com/stretchr/testify
|
||||
|
||||
before_script:
|
||||
# create tables and load data
|
||||
- mysql < ./Deploy/db/registry.sql -uroot --verbose
|
||||
|
||||
script:
|
||||
- go list ./... | grep -v /vendor/ | xargs -L1 fgt golint
|
||||
- go list ./... | grep -v 'vendor' | xargs -L1 go vet
|
||||
- go list ./... | grep -v 'vendor' | xargs -L1 go test -v
|
||||
- go list ./... | grep -v 'tests' | grep -v /vendor/ | xargs -L1 fgt golint
|
||||
- go list ./... | grep -v 'tests' | grep -v 'vendor' | xargs -L1 go vet
|
||||
- go list ./... | grep -v 'tests' | grep -v 'vendor' | xargs -L1 go test -v
|
||||
|
||||
- docker-compose -f Deploy/docker-compose.yml up -d
|
||||
|
||||
- docker ps
|
||||
- go run tests/startuptest.go http://localhost/
|
||||
- go run tests/userlogintest.go -name ${HARBOR_ADMIN} -passwd ${HARBOR_ADMIN_PASSWD}
|
||||
|
||||
|
||||
# test for API
|
||||
- go test -v ./tests/apitests
|
||||
|
@ -33,7 +33,7 @@ type ProjectMemberAPI struct {
|
||||
}
|
||||
|
||||
type memberReq struct {
|
||||
Username string `json:"user_name"`
|
||||
Username string `json:"username"`
|
||||
UserID int `json:"user_id"`
|
||||
Roles []int `json:"roles"`
|
||||
}
|
||||
@ -104,7 +104,7 @@ func (pma *ProjectMemberAPI) Get() {
|
||||
log.Errorf("Error occurred in GetUser, error: %v", err)
|
||||
pma.CustomAbort(http.StatusInternalServerError, "Internal error.")
|
||||
}
|
||||
result["user_name"] = user.Username
|
||||
result["username"] = user.Username
|
||||
result["user_id"] = pma.memberID
|
||||
result["roles"] = roleList
|
||||
pma.Data["json"] = result
|
||||
|
@ -14,8 +14,6 @@ import (
|
||||
// RepPolicyAPI handles /api/replicationPolicies /api/replicationPolicies/:id/enablement
|
||||
type RepPolicyAPI struct {
|
||||
BaseAPI
|
||||
policyID int64
|
||||
policy *models.RepPolicy
|
||||
}
|
||||
|
||||
// Prepare validates whether the user has system admin role
|
||||
@ -214,11 +212,11 @@ func (pa *RepPolicyAPI) UpdateEnablement() {
|
||||
return
|
||||
}
|
||||
|
||||
if pa.policy.Enabled == e.Enabled {
|
||||
if policy.Enabled == e.Enabled {
|
||||
return
|
||||
}
|
||||
|
||||
if err := dao.UpdateRepPolicyEnablement(pa.policyID, e.Enabled); err != nil {
|
||||
if err := dao.UpdateRepPolicyEnablement(id, e.Enabled); err != nil {
|
||||
log.Errorf("Failed to update policy enablement in DB, error: %v", err)
|
||||
pa.RenderError(http.StatusInternalServerError, "Internal Error")
|
||||
return
|
||||
@ -226,18 +224,18 @@ func (pa *RepPolicyAPI) UpdateEnablement() {
|
||||
|
||||
if e.Enabled == 1 {
|
||||
go func() {
|
||||
if err := TriggerReplication(pa.policyID, "", nil, models.RepOpTransfer); err != nil {
|
||||
log.Errorf("failed to trigger replication of %d: %v", pa.policyID, err)
|
||||
if err := TriggerReplication(id, "", nil, models.RepOpTransfer); err != nil {
|
||||
log.Errorf("failed to trigger replication of %d: %v", id, err)
|
||||
} else {
|
||||
log.Infof("replication of %d triggered", pa.policyID)
|
||||
log.Infof("replication of %d triggered", id)
|
||||
}
|
||||
}()
|
||||
} else {
|
||||
go func() {
|
||||
if err := postReplicationAction(pa.policyID, "stop"); err != nil {
|
||||
log.Errorf("failed to stop replication of %d: %v", pa.policyID, err)
|
||||
if err := postReplicationAction(id, "stop"); err != nil {
|
||||
log.Errorf("failed to stop replication of %d: %v", id, err)
|
||||
} else {
|
||||
log.Infof("try to stop replication of %d", pa.policyID)
|
||||
log.Infof("try to stop replication of %d", id)
|
||||
}
|
||||
}()
|
||||
}
|
||||
|
@ -4,7 +4,7 @@ swagger: '2.0'
|
||||
info:
|
||||
title: Harbor API
|
||||
description: These APIs provide services for manipulating Harbor project.
|
||||
version: "0.1.0"
|
||||
version: "0.1.1"
|
||||
# the domain of the service
|
||||
host: localhost
|
||||
# array of all schemes that your API supports
|
||||
@ -167,7 +167,7 @@ paths:
|
||||
- name: access_log
|
||||
in: body
|
||||
schema:
|
||||
$ref: '#/definitions/AccessLog'
|
||||
$ref: '#/definitions/AccessLogFilter'
|
||||
description: Search results of access logs.
|
||||
tags:
|
||||
- Products
|
||||
@ -204,7 +204,7 @@ paths:
|
||||
schema:
|
||||
type: array
|
||||
items:
|
||||
$ref: '#/definitions/Role'
|
||||
$ref: '#/definitions/User'
|
||||
400:
|
||||
description: Illegal format of provided ID value.
|
||||
401:
|
||||
@ -567,7 +567,7 @@ paths:
|
||||
schema:
|
||||
type: array
|
||||
items:
|
||||
$ref: '#/definitions/Repository'
|
||||
type: string
|
||||
400:
|
||||
description: Invalid project ID.
|
||||
403:
|
||||
@ -719,12 +719,41 @@ definitions:
|
||||
description: Search results of the projects that matched the filter keywords.
|
||||
type: array
|
||||
items:
|
||||
$ref: '#/definitions/Project'
|
||||
$ref: '#/definitions/SearchProject'
|
||||
repositories:
|
||||
description: Search results of the repositories that matched the filter keywords.
|
||||
type: array
|
||||
items:
|
||||
$ref: '#/definitions/Repository'
|
||||
$ref: '#/definitions/SearchRepository'
|
||||
SearchProject:
|
||||
type: object
|
||||
properties:
|
||||
id:
|
||||
type: integer
|
||||
format: int64
|
||||
description: The ID of project
|
||||
name:
|
||||
type: string
|
||||
description: The name of the project
|
||||
public:
|
||||
type: integer
|
||||
format: int
|
||||
description: The flag to indicate the publicity of the project (1 is public, 0 is non-public)
|
||||
SearchRepository:
|
||||
type: object
|
||||
properties:
|
||||
repository_name:
|
||||
type: string
|
||||
description: The name of the repository
|
||||
project_name:
|
||||
type: string
|
||||
description: The name of the project that the repository belongs to
|
||||
project_id:
|
||||
type: integer
|
||||
description: The ID of the project that the repository belongs to
|
||||
project_public:
|
||||
type: integer
|
||||
description: The flag to indicate the publicity of the project that the repository belongs to (1 is public, 0 is not)
|
||||
Project:
|
||||
type: object
|
||||
properties:
|
||||
@ -736,30 +765,39 @@ definitions:
|
||||
type: integer
|
||||
format: int32
|
||||
description: The owner ID of the project always means the creator of the project.
|
||||
project_name:
|
||||
name:
|
||||
type: string
|
||||
description: The name of the project.
|
||||
creation_time:
|
||||
type: string
|
||||
description: The creation time of the project.
|
||||
update_time:
|
||||
type: string
|
||||
description: The update time of the project.
|
||||
deleted:
|
||||
type: integer
|
||||
format: int32
|
||||
description: A deletion mark of the project.
|
||||
description: A deletion mark of the project (1 means it's deleted, 0 is not)
|
||||
user_id:
|
||||
type: integer
|
||||
format: int32
|
||||
description: A relation field to the user table.
|
||||
owner_name:
|
||||
type: string
|
||||
description: The owner name of tthe project always means the creator of the project.
|
||||
description: The owner name of the project.
|
||||
public:
|
||||
type: boolean
|
||||
format: boolean
|
||||
description: The public status of the project.
|
||||
togglable:
|
||||
type: boolean
|
||||
description: Correspond to the UI about showing the public status of the project.
|
||||
description: Correspond to the UI about whether the project's publicity is updatable (for UI)
|
||||
current_user_role_id:
|
||||
type: integer
|
||||
description: The role ID of the current user who triggered the API (for UI)
|
||||
repo_count:
|
||||
type: integer
|
||||
description: The number of the repositories under this project.
|
||||
Repository:
|
||||
type: object
|
||||
properties:
|
||||
@ -794,6 +832,7 @@ definitions:
|
||||
user_id:
|
||||
type: integer
|
||||
format: int32
|
||||
description: The ID of the user.
|
||||
username:
|
||||
type: string
|
||||
email:
|
||||
@ -816,7 +855,7 @@ definitions:
|
||||
new_password:
|
||||
type: string
|
||||
description: New password for marking as to be updated.
|
||||
AccessLog:
|
||||
AccessLogFilter:
|
||||
type: object
|
||||
properties:
|
||||
username:
|
||||
@ -825,14 +864,32 @@ definitions:
|
||||
keywords:
|
||||
type: string
|
||||
description: Operation name specified when project created.
|
||||
beginTimestamp:
|
||||
begin_timestamp:
|
||||
type: integer
|
||||
format: int32
|
||||
format: int64
|
||||
description: Begin timestamp for querying access logs.
|
||||
endTimestamp:
|
||||
end_timestamp:
|
||||
type: integer
|
||||
format: int32
|
||||
format: int64
|
||||
description: End timestamp for querying accessl logs.
|
||||
AccessLog:
|
||||
type: object
|
||||
properties:
|
||||
log_id:
|
||||
type: integer
|
||||
description: The ID of the log entry.
|
||||
repo_name:
|
||||
type: string
|
||||
description: Name of the repository in this log entry.
|
||||
repo_tag:
|
||||
type: string
|
||||
description: Tag of the repository in this log entry.
|
||||
operation:
|
||||
type: string
|
||||
description: The operation against the repository in this log entry.
|
||||
op_time:
|
||||
type: time
|
||||
description: The time when this operation is triggered.
|
||||
Role:
|
||||
type: object
|
||||
properties:
|
||||
@ -855,7 +912,7 @@ definitions:
|
||||
type: integer
|
||||
format: int32
|
||||
description: Role ID for updating project role member.
|
||||
user_name:
|
||||
username:
|
||||
type: string
|
||||
description: Username relevant to a project role member.
|
||||
TopRepo:
|
||||
|
@ -21,19 +21,18 @@ import (
|
||||
|
||||
// AccessLog holds information about logs which are used to record the actions that user take to the resourses.
|
||||
type AccessLog struct {
|
||||
LogID int `orm:"pk;column(log_id)" json:"LogId"`
|
||||
UserID int `orm:"column(user_id)" json:"UserId"`
|
||||
ProjectID int64 `orm:"column(project_id)" json:"ProjectId"`
|
||||
RepoName string `orm:"column(repo_name)"`
|
||||
RepoTag string `orm:"column(repo_tag)"`
|
||||
GUID string `orm:"column(GUID)" json:"Guid"`
|
||||
Operation string `orm:"column(operation)"`
|
||||
OpTime time.Time `orm:"column(op_time)"`
|
||||
Username string
|
||||
Keywords string
|
||||
|
||||
LogID int `orm:"column(log_id)" json:"log_id"`
|
||||
UserID int `orm:"column(user_id)" json:"user_id"`
|
||||
ProjectID int64 `orm:"column(project_id)" json:"project_id"`
|
||||
RepoName string `orm:"column(repo_name)" json:"repo_name"`
|
||||
RepoTag string `orm:"column(repo_tag)" json:"repo_tag"`
|
||||
GUID string `orm:"column(GUID)" json:"guid"`
|
||||
Operation string `orm:"column(operation)" json:"operation"`
|
||||
OpTime time.Time `orm:"column(op_time)" json:"op_time"`
|
||||
Username string `json:"username"`
|
||||
Keywords string `json:"keywords"`
|
||||
BeginTime time.Time
|
||||
BeginTimestamp int64
|
||||
BeginTimestamp int64 `json:"begin_timestamp"`
|
||||
EndTime time.Time
|
||||
EndTimestamp int64
|
||||
EndTimestamp int64 `json:"end_timestamp"`
|
||||
}
|
||||
|
@ -21,19 +21,19 @@ import (
|
||||
|
||||
// Project holds the details of a project.
|
||||
type Project struct {
|
||||
ProjectID int64 `orm:"pk;column(project_id)" json:"ProjectId"`
|
||||
OwnerID int `orm:"column(owner_id)" json:"OwnerId"`
|
||||
Name string `orm:"column(name)"`
|
||||
CreationTime time.Time `orm:"column(creation_time)"`
|
||||
CreationTimeStr string
|
||||
Deleted int `orm:"column(deleted)"`
|
||||
UserID int `json:"UserId"`
|
||||
OwnerName string
|
||||
Public int `orm:"column(public)"`
|
||||
ProjectID int64 `orm:"column(project_id)" json:"project_id"`
|
||||
OwnerID int `orm:"column(owner_id)" json:"owner_id"`
|
||||
Name string `orm:"column(name)" json:"name"`
|
||||
CreationTime time.Time `orm:"column(creation_time)" json:"creation_time"`
|
||||
CreationTimeStr string `json:"creation_time_str"`
|
||||
Deleted int `orm:"column(deleted)" json:"deleted"`
|
||||
//UserID int `json:"UserId"`
|
||||
OwnerName string `json:"owner_name"`
|
||||
Public int `orm:"column(public)" json:"public"`
|
||||
//This field does not have correspondent column in DB, this is just for UI to disable button
|
||||
Togglable bool
|
||||
|
||||
UpdateTime time.Time `orm:"update_time" json:"update_time"`
|
||||
Role int `json:"role_id"`
|
||||
Role int `json:"current_user_role_id"`
|
||||
RepoCount int `json:"repo_count"`
|
||||
}
|
||||
|
@ -21,20 +21,19 @@ import (
|
||||
|
||||
// User holds the details of a user.
|
||||
type User struct {
|
||||
UserID int `orm:"pk;column(user_id)" json:"UserId"`
|
||||
UserID int `orm:"column(user_id)" json:"user_id"`
|
||||
Username string `orm:"column(username)" json:"username"`
|
||||
Email string `orm:"column(email)" json:"email"`
|
||||
Password string `orm:"column(password)" json:"password"`
|
||||
Realname string `orm:"column(realname)" json:"realname"`
|
||||
Comment string `orm:"column(comment)" json:"comment"`
|
||||
Deleted int `orm:"column(deleted)"`
|
||||
Rolename string
|
||||
RoleID int `json:"RoleId"`
|
||||
RoleList []*Role `orm:"rel(m2m)"`
|
||||
HasAdminRole int `orm:"column(sysadmin_flag)"`
|
||||
ResetUUID string `orm:"column(reset_uuid)" json:"ResetUuid"`
|
||||
Deleted int `orm:"column(deleted)" json:"deleted"`
|
||||
Rolename string `json:"role_name"`
|
||||
RoleID int `json:"role_id"`
|
||||
// RoleList []Role `json:"role_list"`
|
||||
HasAdminRole int `orm:"column(sysadmin_flag)" json:"has_admin_role"`
|
||||
ResetUUID string `orm:"column(reset_uuid)" json:"reset_uuid"`
|
||||
Salt string `orm:"column(salt)"`
|
||||
|
||||
CreationTime time.Time `orm:"creation_time" json:"creation_time"`
|
||||
UpdateTime time.Time `orm:"update_time" json:"update_time"`
|
||||
}
|
||||
|
@ -13,7 +13,6 @@
|
||||
limitations under the License.
|
||||
*/
|
||||
.footer {
|
||||
margin-top: 60px;
|
||||
width: 100%;
|
||||
/* Set the fixed height of the footer here */
|
||||
height: 60px;
|
||||
|
@ -12,6 +12,7 @@
|
||||
See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
*/
|
||||
|
||||
var AjaxUtil = function(params){
|
||||
|
||||
this.url = params.url;
|
||||
@ -154,8 +155,13 @@ jQuery(function(){
|
||||
}
|
||||
|
||||
if(settings.callback != null){
|
||||
$("#dlgConfirm").on("click", function(){
|
||||
var hasEntered = false;
|
||||
$("#dlgConfirm").on("click", function(e){
|
||||
if(!hasEntered) {
|
||||
hasEntered = true;
|
||||
settings.callback();
|
||||
|
||||
}
|
||||
});
|
||||
}
|
||||
$(self).modal('show');
|
||||
|
@ -105,7 +105,7 @@ jQuery(function(){
|
||||
$('#accordionRepo').on('show.bs.collapse', function (e) {
|
||||
$('#accordionRepo .in').collapse('hide');
|
||||
var targetId = $(e.target).attr("targetId");
|
||||
var repoName = targetId.replace(/[-]{6}/g, "/").replace(/[-]{3}/g, '.');
|
||||
var repoName = targetId.replace(/[-]{6}/g, "/").replace(/[-]{3}/g, ".");
|
||||
new AjaxUtil({
|
||||
url: "/api/repositories/tags?repo_name=" + repoName,
|
||||
type: "get",
|
||||
@ -113,7 +113,7 @@ jQuery(function(){
|
||||
$('#' + targetId +' table tbody tr').remove();
|
||||
var row = [];
|
||||
for(var i in data){
|
||||
var tagName = data[i];
|
||||
var tagName = data[i]
|
||||
row.push('<tr><td><a href="#" imageId="' + tagName + '" repoName="' + repoName + '">' + tagName + '</a></td><td><input type="text" style="width:100%" readonly value=" docker pull '+ $("#harborRegUrl").val() +'/'+ repoName + ':' + tagName +'"></td></tr>');
|
||||
}
|
||||
$('#' + targetId +' table tbody').append(row.join(""));
|
||||
@ -131,7 +131,6 @@ jQuery(function(){
|
||||
}
|
||||
}
|
||||
data.Created = moment(new Date(data.Created)).format("YYYY-MM-DD HH:mm:ss");
|
||||
|
||||
$("#dlgModal").dialogModal({"title": i18n.getMessage("image_details"), "content": data});
|
||||
}
|
||||
}
|
||||
@ -204,7 +203,7 @@ jQuery(function(){
|
||||
if(operationType == "add"){
|
||||
ajaxOpts.url = "/api/projects/" + projectId + "/members/";
|
||||
ajaxOpts.type = "post";
|
||||
ajaxOpts.data = {"roles" : checkedRoleItemList, "user_name": username};
|
||||
ajaxOpts.data = {"roles" : checkedRoleItemList, "username": username};
|
||||
}else if(operationType == "edit"){
|
||||
ajaxOpts.url = "/api/projects/" + projectId + "/members/" + userId;
|
||||
ajaxOpts.type = "put";
|
||||
@ -241,24 +240,15 @@ jQuery(function(){
|
||||
var ownerId = $("#ownerId").val();
|
||||
|
||||
$("#tblUser tbody tr").remove();
|
||||
for(var i = 0; i < userList.length; ){
|
||||
for(var i = 0; i < userList.length; i++){
|
||||
|
||||
var userId = userList[i].UserId;
|
||||
var roleId = userList[i].RoleId;
|
||||
var userId = userList[i].user_id;
|
||||
var roleId = userList[i].role_id;
|
||||
var username = userList[i].username;
|
||||
var roleNameList = [];
|
||||
|
||||
for(var j = i; j < userList.length; i++, j++){
|
||||
if(userList[j].UserId == userId){
|
||||
roleNameList.push(name_mapping[userList[j].Rolename]);
|
||||
}else{
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
var row = '<tr>' +
|
||||
'<td>' + username + '</td>' +
|
||||
'<td>' + roleNameList.join(",") + '</td>' +
|
||||
'<td>' + name_mapping[userList[i].role_name] + '</td>' +
|
||||
'<td>';
|
||||
var isShowOperations = true;
|
||||
if(loginedUserRoleId >= 3 /*role: developer guest*/){
|
||||
@ -284,11 +274,11 @@ jQuery(function(){
|
||||
$.each(LogList || [], function(i, e){
|
||||
$("#tabOperationLog tbody").append(
|
||||
'<tr>' +
|
||||
'<td>' + e.Username + '</td>' +
|
||||
'<td>' + e.RepoName + '</td>' +
|
||||
'<td>' + e.RepoTag + '</td>' +
|
||||
'<td>' + e.Operation + '</td>' +
|
||||
'<td>' + moment(new Date(e.OpTime)).format("YYYY-MM-DD HH:mm:ss") + '</td>' +
|
||||
'<td>' + e.username + '</td>' +
|
||||
'<td>' + e.repo_name + '</td>' +
|
||||
'<td>' + e.repo_tag + '</td>' +
|
||||
'<td>' + e.operation + '</td>' +
|
||||
'<td>' + moment(new Date(e.op_time)).format("YYYY-MM-DD HH:mm:ss") + '</td>' +
|
||||
'</tr>');
|
||||
});
|
||||
}
|
||||
@ -302,7 +292,7 @@ jQuery(function(){
|
||||
$("#operationType").val("edit");
|
||||
$("#editUserId").val(user.user_id);
|
||||
$("#spnSearch").hide();
|
||||
$("#txtUserName").val(user.user_name);
|
||||
$("#txtUserName").val(user.username);
|
||||
$("#txtUserName").prop("disabled", true);
|
||||
$("#btnSave").removeClass("disabled");
|
||||
$("#dlgUserTitle").text(i18n.getMessage("edit_members"));
|
||||
@ -404,7 +394,7 @@ jQuery(function(){
|
||||
|
||||
new AjaxUtil({
|
||||
url: "/api/projects/" + projectId + "/logs/filter",
|
||||
data:{"username":username, "project_id" : projectId, "keywords" : getKeyWords() , "beginTimestamp" : beginTimestamp, "endTimestamp" : endTimestamp},
|
||||
data:{"username":username, "project_id" : Number(projectId), "keywords" : getKeyWords() , "begin_timestamp" : beginTimestamp, "end_timestamp" : endTimestamp},
|
||||
type: "post",
|
||||
success: function(data, status, xhr){
|
||||
if(xhr && xhr.status == 200){
|
||||
@ -464,9 +454,9 @@ jQuery(function(){
|
||||
showClear: true
|
||||
});
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
$(document).on("keydown", function(e){
|
||||
$(document).on("keydown", function(e){
|
||||
if(e.keyCode == 13){
|
||||
e.preventDefault();
|
||||
if($("#tabItemDetail li:eq(0)").is(":focus") || $("#txtRepoName").is(":focus")){
|
||||
@ -479,5 +469,5 @@ jQuery(function(){
|
||||
$("#btnSave").trigger("click");
|
||||
}
|
||||
}
|
||||
});
|
||||
});
|
||||
})
|
@ -19,7 +19,7 @@ jQuery(function(){
|
||||
type: "get",
|
||||
success: function(data, status, xhr){
|
||||
if(xhr && xhr.status == 200){
|
||||
if(data.HasAdminRole == 1) {
|
||||
if(data.has_admin_role == 1) {
|
||||
renderForAdminRole();
|
||||
}
|
||||
renderForAnyRole();
|
||||
@ -55,16 +55,16 @@ jQuery(function(){
|
||||
$("#tblProject tbody tr").remove();
|
||||
$.each(data || [], function(i, e){
|
||||
var row = '<tr>' +
|
||||
'<td style="vertical-align: middle;"><a href="/registry/detail?project_id=' + e.ProjectId + '">' + e.Name + '</a></td>' +
|
||||
'<td style="vertical-align: middle;">' + moment(new Date(e.CreationTime)).format("YYYY-MM-DD HH:mm:ss") + '</td>';
|
||||
if(e.Public == 1 && e.Togglable){
|
||||
row += '<td><button type="button" class="btn btn-success" projectid="' + e.ProjectId + '">' + i18n.getMessage("button_on")+ '</button></td>'
|
||||
} else if (e.Public == 1) {
|
||||
row += '<td><button type="button" class="btn btn-success" projectid="' + e.ProjectId + '" disabled>' + i18n.getMessage("button_on")+ '</button></td>';
|
||||
} else if (e.Public == 0 && e.Togglable) {
|
||||
row += '<td><button type="button" class="btn btn-danger" projectid="' + e.ProjectId + '">' + i18n.getMessage("button_off")+ '</button></td>';
|
||||
} else if (e.Public == 0) {
|
||||
row += '<td><button type="button" class="btn btn-danger" projectid="' + e.ProjectId + '" disabled>' + i18n.getMessage("button_off")+ '</button></td>';
|
||||
'<td style="vertical-align: middle;"><a href="/registry/detail?project_id=' + e.project_id + '">' + e.name + '</a></td>' +
|
||||
'<td style="vertical-align: middle;">' + moment(new Date(e.creation_time)).format("YYYY-MM-DD HH:mm:ss") + '</td>';
|
||||
if(e.public == 1 && e.Togglable){
|
||||
row += '<td><button type="button" class="btn btn-success" projectid="' + e.project_id + '">' + i18n.getMessage("button_on")+ '</button></td>'
|
||||
} else if (e.public == 1) {
|
||||
row += '<td><button type="button" class="btn btn-success" projectid="' + e.project_id + '" disabled>' + i18n.getMessage("button_on")+ '</button></td>';
|
||||
} else if (e.public == 0 && e.Togglable) {
|
||||
row += '<td><button type="button" class="btn btn-danger" projectid="' + e.project_id + '">' + i18n.getMessage("button_off")+ '</button></td>';
|
||||
} else if (e.public == 0) {
|
||||
row += '<td><button type="button" class="btn btn-danger" projectid="' + e.project_id + '" disabled>' + i18n.getMessage("button_off")+ '</button></td>';
|
||||
row += '</tr>';
|
||||
}
|
||||
$("#tblProject tbody").append(row);
|
||||
@ -163,12 +163,12 @@ jQuery(function(){
|
||||
var row = '<tr>' +
|
||||
'<td style="vertical-align: middle;">' + e.username + '</td>' +
|
||||
'<td style="vertical-align: middle;">' + e.email + '</td>';
|
||||
if(e.HasAdminRole == 1){
|
||||
row += '<td style="padding-left: 30px;"><button type="button" class="btn btn-success" userid="' + e.UserId + '">' + i18n.getMessage("button_on") + '</button></td>';
|
||||
if(e.has_admin_role == 1){
|
||||
row += '<td style="padding-left: 30px;"><button type="button" class="btn btn-success" userid="' + e.user_id + '">' + i18n.getMessage("button_on") + '</button></td>';
|
||||
} else {
|
||||
row += '<td style="padding-left: 30px;"><button type="button" class="btn btn-danger" userid="' + e.UserId + '">' + i18n.getMessage("button_off") + '</button></td>';
|
||||
row += '<td style="padding-left: 30px;"><button type="button" class="btn btn-danger" userid="' + e.user_id + '">' + i18n.getMessage("button_off") + '</button></td>';
|
||||
}
|
||||
row += '<td style="padding-left: 30px; vertical-align: middle;"><a href="#" style="visibility: hidden;" class="tdDeleteUser" userid="' + e.UserId + '" username="' + e.Username + '"><span class="glyphicon glyphicon-trash"></span></a></td>';
|
||||
row += '<td style="padding-left: 30px; vertical-align: middle;"><a href="#" style="visibility: hidden;" class="tdDeleteUser" userid="' + e.user_id + '" username="' + e.username + '"><span class="glyphicon glyphicon-trash"></span></a></td>';
|
||||
row += '</tr>';
|
||||
$("#tblUser tbody").append(row);
|
||||
});
|
||||
@ -194,12 +194,12 @@ jQuery(function(){
|
||||
}
|
||||
}).exec();
|
||||
});
|
||||
$("#tblUser tbody tr").on("mouseover", function(){
|
||||
$("#tblUser tbody tr").on("mouseover", function(e){
|
||||
$(".tdDeleteUser", this).css({"visibility":"visible"});
|
||||
}).on("mouseout", function(){
|
||||
}).on("mouseout", function(e){
|
||||
$(".tdDeleteUser", this).css({"visibility":"hidden"});
|
||||
});
|
||||
$("#tblUser tbody tr .tdDeleteUser").on("click", function(){
|
||||
$("#tblUser tbody tr .tdDeleteUser").on("click", function(e){
|
||||
var userId = $(this).attr("userid");
|
||||
$("#dlgModal")
|
||||
.dialogModal({
|
||||
@ -219,6 +219,7 @@ jQuery(function(){
|
||||
}).exec();
|
||||
}
|
||||
});
|
||||
|
||||
});
|
||||
});
|
||||
}
|
||||
@ -231,4 +232,4 @@ jQuery(function(){
|
||||
listUserAdminRole(username);
|
||||
});
|
||||
}
|
||||
})
|
||||
})
|
||||
|
@ -30,7 +30,6 @@ jQuery(function(){
|
||||
}
|
||||
bindEnterKey();
|
||||
|
||||
|
||||
var spinner = new Spinner({scale:1}).spin();
|
||||
|
||||
$("#btnSubmit").on("click", function(){
|
||||
|
23
tests/apitests/apilib/accesslog.go
Normal file
23
tests/apitests/apilib/accesslog.go
Normal file
@ -0,0 +1,23 @@
|
||||
/*
|
||||
Copyright (c) 2016 VMware, Inc. All Rights Reserved.
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
You may obtain a copy of the License at
|
||||
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing, software
|
||||
distributed under the License is distributed on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
*/
|
||||
|
||||
package HarborAPI
|
||||
|
||||
type AccessLog struct {
|
||||
Username string `json:"username,omitempty"`
|
||||
Keywords string `json:"keywords,omitempty"`
|
||||
BeginTimestamp int32 `json:"beginTimestamp,omitempty"`
|
||||
EndTimestamp int32 `json:"endTimestamp,omitempty"`
|
||||
}
|
113
tests/apitests/apilib/harborapi.go
Normal file
113
tests/apitests/apilib/harborapi.go
Normal file
@ -0,0 +1,113 @@
|
||||
//Package HarborAPI
|
||||
//These APIs provide services for manipulating Harbor project.
|
||||
package HarborAPI
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
//"fmt"
|
||||
"io/ioutil"
|
||||
"net/http"
|
||||
|
||||
"github.com/dghubble/sling"
|
||||
)
|
||||
|
||||
type HarborAPI struct {
|
||||
basePath string
|
||||
}
|
||||
|
||||
func NewHarborAPI() *HarborAPI {
|
||||
return &HarborAPI{
|
||||
basePath: "http://localhost",
|
||||
}
|
||||
}
|
||||
|
||||
func NewHarborAPIWithBasePath(basePath string) *HarborAPI {
|
||||
return &HarborAPI{
|
||||
basePath: basePath,
|
||||
}
|
||||
}
|
||||
|
||||
type UsrInfo struct {
|
||||
Name string
|
||||
Passwd string
|
||||
}
|
||||
|
||||
//Search for projects and repositories
|
||||
//Implementation Notes
|
||||
//The Search endpoint returns information about the projects and repositories
|
||||
//offered at public status or related to the current logged in user.
|
||||
//The response includes the project and repository list in a proper display order.
|
||||
//@param q Search parameter for project and repository name.
|
||||
//@return []Search
|
||||
//func (a HarborAPI) SearchGet (q string) (Search, error) {
|
||||
func (a HarborAPI) SearchGet(q string) (Search, error) {
|
||||
|
||||
_sling := sling.New().Get(a.basePath)
|
||||
|
||||
// create path and map variables
|
||||
path := "/api/search"
|
||||
|
||||
_sling = _sling.Path(path)
|
||||
|
||||
type QueryParams struct {
|
||||
Query string `url:"q"`
|
||||
}
|
||||
|
||||
_sling = _sling.QueryStruct(&QueryParams{q})
|
||||
|
||||
// accept header
|
||||
accepts := []string{"application/json", "text/plain"}
|
||||
for key := range accepts {
|
||||
_sling = _sling.Set("Accept", accepts[key])
|
||||
break // only use the first Accept
|
||||
}
|
||||
|
||||
req, err := _sling.Request()
|
||||
client := &http.Client{}
|
||||
httpResponse, err := client.Do(req)
|
||||
defer httpResponse.Body.Close()
|
||||
|
||||
body, err := ioutil.ReadAll(httpResponse.Body)
|
||||
if err != nil {
|
||||
// handle error
|
||||
}
|
||||
|
||||
var successPayload = new(Search)
|
||||
err = json.Unmarshal(body, &successPayload)
|
||||
return *successPayload, err
|
||||
}
|
||||
|
||||
//Create a new project.
|
||||
//Implementation Notes
|
||||
//This endpoint is for user to create a new project.
|
||||
//@param project New created project.
|
||||
//@return void
|
||||
//func (a HarborAPI) ProjectsPost (prjUsr UsrInfo, project Project) (int, error) {
|
||||
func (a HarborAPI) ProjectsPost(prjUsr UsrInfo, project Project) (int, error) {
|
||||
|
||||
_sling := sling.New().Post(a.basePath)
|
||||
|
||||
// create path and map variables
|
||||
path := "/api/projects"
|
||||
|
||||
_sling = _sling.Path(path)
|
||||
|
||||
// accept header
|
||||
accepts := []string{"application/json", "text/plain"}
|
||||
for key := range accepts {
|
||||
_sling = _sling.Set("Accept", accepts[key])
|
||||
break // only use the first Accept
|
||||
}
|
||||
|
||||
// body params
|
||||
_sling = _sling.BodyJSON(project)
|
||||
|
||||
req, err := _sling.Request()
|
||||
req.SetBasicAuth(prjUsr.Name, prjUsr.Passwd)
|
||||
|
||||
client := &http.Client{}
|
||||
httpResponse, err := client.Do(req)
|
||||
defer httpResponse.Body.Close()
|
||||
|
||||
return httpResponse.StatusCode, err
|
||||
}
|
15
tests/apitests/apilib/harborlogout.go
Normal file
15
tests/apitests/apilib/harborlogout.go
Normal file
@ -0,0 +1,15 @@
|
||||
// HarborLogout.go
|
||||
package HarborAPI
|
||||
|
||||
import (
|
||||
"net/http"
|
||||
)
|
||||
|
||||
func (a HarborAPI) HarborLogout() (int, error) {
|
||||
|
||||
response, err := http.Get(a.basePath + "/logout")
|
||||
|
||||
defer response.Body.Close()
|
||||
|
||||
return response.StatusCode, err
|
||||
}
|
28
tests/apitests/apilib/harlogin.go
Normal file
28
tests/apitests/apilib/harlogin.go
Normal file
@ -0,0 +1,28 @@
|
||||
// HarborLogon.go
|
||||
package HarborAPI
|
||||
|
||||
import (
|
||||
"io/ioutil"
|
||||
"net/http"
|
||||
"net/url"
|
||||
"strings"
|
||||
)
|
||||
|
||||
func (a HarborAPI) HarborLogin(user UsrInfo) (int, error) {
|
||||
|
||||
v := url.Values{}
|
||||
v.Set("principal", user.Name)
|
||||
v.Set("password", user.Passwd)
|
||||
|
||||
body := ioutil.NopCloser(strings.NewReader(v.Encode())) //endode v:[body struce]
|
||||
|
||||
client := &http.Client{}
|
||||
reqest, err := http.NewRequest("POST", a.basePath+"/login", body)
|
||||
|
||||
reqest.Header.Set("Content-Type", "application/x-www-form-urlencoded;param=value") //setting post head
|
||||
|
||||
resp, err := client.Do(reqest)
|
||||
defer resp.Body.Close() //close resp.Body
|
||||
|
||||
return resp.StatusCode, err
|
||||
}
|
15
tests/apitests/apilib/project.go
Normal file
15
tests/apitests/apilib/project.go
Normal file
@ -0,0 +1,15 @@
|
||||
package HarborAPI
|
||||
|
||||
import ()
|
||||
|
||||
type Project struct {
|
||||
ProjectId int32 `json:"id,omitempty"`
|
||||
OwnerId int32 `json:"owner_id,omitempty"`
|
||||
ProjectName string `json:"project_name,omitempty"`
|
||||
CreationTime string `json:"creation_time,omitempty"`
|
||||
Deleted int32 `json:"deleted,omitempty"`
|
||||
UserId int32 `json:"user_id,omitempty"`
|
||||
OwnerName string `json:"owner_name,omitempty"`
|
||||
Public bool `json:"public,omitempty"`
|
||||
Togglable bool `json:"togglable,omitempty"`
|
||||
}
|
9
tests/apitests/apilib/projecttemp4search.go
Normal file
9
tests/apitests/apilib/projecttemp4search.go
Normal file
@ -0,0 +1,9 @@
|
||||
package HarborAPI
|
||||
|
||||
import ()
|
||||
|
||||
type Project4Search struct {
|
||||
ProjectId int32 `json:"id,omitempty"`
|
||||
ProjectName string `json:"name,omitempty"`
|
||||
Public int32 `json:"public,omitempty"`
|
||||
}
|
16
tests/apitests/apilib/repository.go
Normal file
16
tests/apitests/apilib/repository.go
Normal file
@ -0,0 +1,16 @@
|
||||
package HarborAPI
|
||||
|
||||
import (
|
||||
"time"
|
||||
)
|
||||
|
||||
type Repository struct {
|
||||
Id string `json:"id,omitempty"`
|
||||
Parent string `json:"parent,omitempty"`
|
||||
Created time.Time `json:"created,omitempty"`
|
||||
DurationDays string `json:"duration_days,omitempty"`
|
||||
Author string `json:"author,omitempty"`
|
||||
Architecture string `json:"architecture,omitempty"`
|
||||
DockerVersion string `json:"docker_version,omitempty"`
|
||||
Os string `json:"os,omitempty"`
|
||||
}
|
9
tests/apitests/apilib/repositorytemp4search.go
Normal file
9
tests/apitests/apilib/repositorytemp4search.go
Normal file
@ -0,0 +1,9 @@
|
||||
package HarborAPI
|
||||
|
||||
type Repository4Search struct {
|
||||
ProjectId int32 `json:"project_id,omitempty"`
|
||||
ProjectName string `json:"project_name,omitempty"`
|
||||
ProjectPublic int32 `json:"project_public,omitempty"`
|
||||
RepoName string `json:"repository_name,omitempty"`
|
||||
}
|
||||
|
7
tests/apitests/apilib/role.go
Normal file
7
tests/apitests/apilib/role.go
Normal file
@ -0,0 +1,7 @@
|
||||
package HarborAPI
|
||||
|
||||
type Role struct {
|
||||
RoleId int32 `json:"role_id,omitempty"`
|
||||
RoleCode string `json:"role_code,omitempty"`
|
||||
RoleName string `json:"role_name,omitempty"`
|
||||
}
|
6
tests/apitests/apilib/roleparam.go
Normal file
6
tests/apitests/apilib/roleparam.go
Normal file
@ -0,0 +1,6 @@
|
||||
package HarborAPI
|
||||
|
||||
type RoleParam struct {
|
||||
Roles []int32 `json:"roles,omitempty"`
|
||||
UserName string `json:"user_name,omitempty"`
|
||||
}
|
8
tests/apitests/apilib/search.go
Normal file
8
tests/apitests/apilib/search.go
Normal file
@ -0,0 +1,8 @@
|
||||
package HarborAPI
|
||||
|
||||
import ()
|
||||
|
||||
type Search struct {
|
||||
Projects []Project4Search `json:"project,omitempty"`
|
||||
Repositories []Repository4Search `json:"repository,omitempty"`
|
||||
}
|
11
tests/apitests/apilib/user.go
Normal file
11
tests/apitests/apilib/user.go
Normal file
@ -0,0 +1,11 @@
|
||||
package HarborAPI
|
||||
|
||||
type User struct {
|
||||
UserId int32 `json:"user_id,omitempty"`
|
||||
Username string `json:"username,omitempty"`
|
||||
Email string `json:"email,omitempty"`
|
||||
Password string `json:"password,omitempty"`
|
||||
Realname string `json:"realname,omitempty"`
|
||||
Comment string `json:"comment,omitempty"`
|
||||
Deleted int32 `json:"deleted,omitempty"`
|
||||
}
|
95
tests/apitests/hbapiaddprj_test.go
Normal file
95
tests/apitests/hbapiaddprj_test.go
Normal file
@ -0,0 +1,95 @@
|
||||
package HarborAPItest
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"github.com/stretchr/testify/assert"
|
||||
"testing"
|
||||
"github.com/vmware/harbor/tests/apitests/apilib"
|
||||
)
|
||||
|
||||
func TestAddProject(t *testing.T) {
|
||||
|
||||
fmt.Println("Test for Project Add (ProjectsPost) API\n")
|
||||
assert := assert.New(t)
|
||||
|
||||
apiTest := HarborAPI.NewHarborAPI()
|
||||
|
||||
//prepare for test
|
||||
adminEr := &HarborAPI.UsrInfo{"admin", "Harbor1234"}
|
||||
admin := &HarborAPI.UsrInfo{"admin", "Harbor12345"}
|
||||
|
||||
prjUsr := &HarborAPI.UsrInfo{"unknown", "unknown"}
|
||||
|
||||
var project HarborAPI.Project
|
||||
project.ProjectName = "testProject"
|
||||
project.Public = true
|
||||
|
||||
//case 1: admin login fail, expect project creation fail.
|
||||
fmt.Println("case 1: admin login fail, expect project creation fail.")
|
||||
resault, err := apiTest.HarborLogin(*adminEr)
|
||||
if err != nil {
|
||||
t.Error("Error while admin login", err.Error())
|
||||
t.Log(err)
|
||||
} else {
|
||||
assert.Equal(resault, int(401), "Admin login status should be 401")
|
||||
//t.Log(resault)
|
||||
}
|
||||
|
||||
resault, err = apiTest.ProjectsPost(*prjUsr, project)
|
||||
if err != nil {
|
||||
t.Error("Error while creat project", err.Error())
|
||||
t.Log(err)
|
||||
} else {
|
||||
assert.Equal(resault, int(401), "Case 1: Project creation status should be 401")
|
||||
//t.Log(resault)
|
||||
}
|
||||
|
||||
//case 2: admin successful login, expect project creation success.
|
||||
fmt.Println("case 2: admin successful login, expect project creation success.")
|
||||
resault, err = apiTest.HarborLogin(*admin)
|
||||
if err != nil {
|
||||
t.Error("Error while admin login", err.Error())
|
||||
t.Log(err)
|
||||
} else {
|
||||
assert.Equal(resault, int(200), "Admin login status should be 200")
|
||||
//t.Log(resault)
|
||||
}
|
||||
if resault != 200 {
|
||||
t.Log(resault)
|
||||
} else {
|
||||
prjUsr = admin
|
||||
}
|
||||
|
||||
resault, err = apiTest.ProjectsPost(*prjUsr, project)
|
||||
if err != nil {
|
||||
t.Error("Error while creat project", err.Error())
|
||||
t.Log(err)
|
||||
} else {
|
||||
assert.Equal(resault, int(201), "Case 2: Project creation status should be 201")
|
||||
//t.Log(resault)
|
||||
}
|
||||
|
||||
//case 3: duplicate project name, create project fail
|
||||
fmt.Println("case 3: duplicate project name, create project fail")
|
||||
resault, err = apiTest.ProjectsPost(*prjUsr, project)
|
||||
if err != nil {
|
||||
t.Error("Error while creat project", err.Error())
|
||||
t.Log(err)
|
||||
} else {
|
||||
assert.Equal(resault, int(409), "Case 3: Project creation status should be 409")
|
||||
//t.Log(resault)
|
||||
}
|
||||
|
||||
//resault1, err := apiTest.HarborLogout()
|
||||
//if err != nil {
|
||||
// t.Error("Error while admin logout", err.Error())
|
||||
// t.Log(err)
|
||||
//} else {
|
||||
// assert.Equal(resault1, int(200), "Admin logout status")
|
||||
// //t.Log(resault)
|
||||
//}
|
||||
//if resault1 != 200 {
|
||||
// t.Log(resault)
|
||||
//}
|
||||
|
||||
}
|
31
tests/apitests/hbapisearch_test.go
Normal file
31
tests/apitests/hbapisearch_test.go
Normal file
@ -0,0 +1,31 @@
|
||||
package HarborAPItest
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"github.com/stretchr/testify/assert"
|
||||
"testing"
|
||||
"github.com/vmware/harbor/tests/apitests/apilib"
|
||||
)
|
||||
|
||||
func TestSearch(t *testing.T) {
|
||||
fmt.Println("Test for Search (SearchGet) API\n")
|
||||
assert := assert.New(t)
|
||||
|
||||
apiTest := HarborAPI.NewHarborAPI()
|
||||
var resault HarborAPI.Search
|
||||
resault, err := apiTest.SearchGet("library")
|
||||
//fmt.Printf("%+v\n", resault)
|
||||
if err != nil {
|
||||
t.Error("Error while search project or repository", err.Error())
|
||||
t.Log(err)
|
||||
} else {
|
||||
assert.Equal(resault.Projects[0].ProjectId, int32(1), "Project id should be equal")
|
||||
assert.Equal(resault.Projects[0].ProjectName, "library", "Project name should be library")
|
||||
assert.Equal(resault.Projects[0].Public, int32(1), "Project public status should be 1 (true)")
|
||||
//t.Log(resault)
|
||||
}
|
||||
//if resault.Response.StatusCode != 200 {
|
||||
// t.Log(resault.Response)
|
||||
//}
|
||||
|
||||
}
|
4
tests/hostcfg.sh
Executable file
4
tests/hostcfg.sh
Executable file
@ -0,0 +1,4 @@
|
||||
#!/bin/bash
|
||||
IP=`ip addr s eth0 |grep "inet "|awk '{print $2}' |awk -F "/" '{print $1}'`
|
||||
#echo $IP
|
||||
sed "s/reg.mydomain.com/$IP/" -i Deploy/harbor.cfg
|
36
tests/startuptest.go
Normal file
36
tests/startuptest.go
Normal file
@ -0,0 +1,36 @@
|
||||
// Fetch prints the content found at a URL.
|
||||
package main
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"io/ioutil"
|
||||
"net/http"
|
||||
"os"
|
||||
"strings"
|
||||
"time"
|
||||
)
|
||||
|
||||
func main() {
|
||||
time.Sleep(60*time.Second)
|
||||
for _, url := range os.Args[1:] {
|
||||
resp, err := http.Get(url)
|
||||
if err != nil {
|
||||
fmt.Fprintf(os.Stderr, "fetch: %v\n", err)
|
||||
os.Exit(1)
|
||||
}
|
||||
b, err := ioutil.ReadAll(resp.Body)
|
||||
resp.Body.Close()
|
||||
if err != nil {
|
||||
fmt.Fprintf(os.Stderr, "fetch: reading %s: %v\n", url, err)
|
||||
os.Exit(1)
|
||||
}
|
||||
// fmt.Printf("%s", b)
|
||||
if strings.Contains(string(b), "Harbor") {
|
||||
fmt.Printf("sucess!\n")
|
||||
} else {
|
||||
os.Exit(1)
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
|
11
tests/testprepare.sh
Executable file
11
tests/testprepare.sh
Executable file
@ -0,0 +1,11 @@
|
||||
IP=`ip addr s eth0 |grep "inet "|awk '{print $2}' |awk -F "/" '{print $1}'`
|
||||
#echo $IP
|
||||
docker pull hello-world
|
||||
docker pull docker
|
||||
#docker login -u admin -p Harbor12345 $IP
|
||||
|
||||
docker tag hello-world $IP/library/hello-world
|
||||
docker push $IP/library/hello-world
|
||||
|
||||
docker tag docker $IP/library/docker
|
||||
docker push $IP/library/docker
|
55
tests/userlogintest.go
Normal file
55
tests/userlogintest.go
Normal file
@ -0,0 +1,55 @@
|
||||
package main
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"io/ioutil"
|
||||
"net/http"
|
||||
"net/url"
|
||||
"strings"
|
||||
"flag"
|
||||
)
|
||||
|
||||
func main() {
|
||||
usrNamePtr := flag.String("name","anaymous","user name")
|
||||
usrPasswdPtr := flag.String("passwd","anaymous","user password")
|
||||
flag.Parse()
|
||||
|
||||
v := url.Values{}
|
||||
v.Set("principal", *usrNamePtr)
|
||||
v.Set("password", *usrPasswdPtr)
|
||||
|
||||
body := ioutil.NopCloser(strings.NewReader(v.Encode())) //endode v:[body struce]
|
||||
fmt.Println(v)
|
||||
|
||||
client := &http.Client{}
|
||||
reqest, err := http.NewRequest("POST", "http://localhost/login", body)
|
||||
if err != nil {
|
||||
fmt.Println("Fatal error ", err.Error())
|
||||
}
|
||||
|
||||
reqest.Header.Set("Content-Type", "application/x-www-form-urlencoded;param=value") //setting post head
|
||||
|
||||
resp, err := client.Do(reqest)
|
||||
defer resp.Body.Close() //close resp.Body
|
||||
|
||||
fmt.Println("login status: ", resp.StatusCode) //print status code
|
||||
|
||||
//content_post, err := ioutil.ReadAll(resp.Body)
|
||||
//if err != nil {
|
||||
// fmt.Println("Fatal error ", err.Error())
|
||||
//}
|
||||
|
||||
//fmt.Println(string(content_post)) //print reply
|
||||
|
||||
response, err := http.Get("http://localhost/api/logout")
|
||||
if err != nil {
|
||||
fmt.Println("Fatal error ", err.Error())
|
||||
}
|
||||
|
||||
defer response.Body.Close()
|
||||
|
||||
fmt.Println("logout status: ", resp.StatusCode) //print status code
|
||||
//content_get, err := ioutil.ReadAll(response.Body)
|
||||
//fmt.Println(string(content_get))
|
||||
|
||||
}
|
@ -13,14 +13,14 @@
|
||||
limitations under the License.
|
||||
-->
|
||||
<!-- Main jumbotron for a primary marketing message or call to action -->
|
||||
<div class="jumbotron">
|
||||
<div class="jumbotron">
|
||||
<div class="container">
|
||||
<img class="pull-left" src="static/resources/image/Harbor_Logo_rec.png" alt="Harbor's Logo" height="180" width="360"/>
|
||||
<p class="pull-left" style="margin-top: 85px; color: #245580; font-size: 30pt; text-align: center; width: 60%;">{{i18n .Lang "index_title"}}</p>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="container">
|
||||
<div class="container">
|
||||
<!-- Example row of columns -->
|
||||
<div class="row">
|
||||
<div class="col-md-12">
|
||||
@ -33,5 +33,5 @@
|
||||
<p>{{i18n .Lang "index_desc_5"}}</p>
|
||||
</div>
|
||||
</div>
|
||||
</div> <!-- /container -->
|
||||
</div> <!-- /container -->
|
||||
<script src="static/resources/js/login.js"></script>
|
@ -18,7 +18,7 @@
|
||||
<li>{{.ProjectName}}</li>
|
||||
</ol>
|
||||
<div class="page-header" style="margin-top: -10px;">
|
||||
<h2>{{.ProjectName}} </h2></h4>{{i18n .Lang "owner"}}: {{.OwnerName}}</h4>
|
||||
<h2>{{.ProjectName}} </h2><h4>{{i18n .Lang "owner"}}: {{.OwnerName}}</h4>
|
||||
</div>
|
||||
<div row="tabpanel">
|
||||
<div class="row">
|
||||
@ -53,7 +53,7 @@
|
||||
</div>
|
||||
</div>
|
||||
</form>
|
||||
<p>
|
||||
<p/>
|
||||
<div class="table-responsive div-height">
|
||||
<div class="alert alert-danger" role="alert" id="divErrMsg"><center></center></div>
|
||||
<div class="panel-group" id="accordionRepo" role="tablist" aria-multiselectable="true">
|
||||
@ -76,7 +76,7 @@
|
||||
</div>
|
||||
<button type="button" class="btn btn-primary" data-toggle="modal" data-target="#dlgUser" id="btnAddUser">{{i18n .Lang "add_members"}}</button>
|
||||
</form>
|
||||
<p>
|
||||
<p/>
|
||||
<div class="table-responsive div-height">
|
||||
<table id="tblUser" class="table table-hover">
|
||||
<thead>
|
||||
@ -108,8 +108,7 @@
|
||||
<button class="btn btn-link" type="button" data-toggle="collapse" data-target="#collapseAdvance" aria-expanded="false" aria-controls="collapseAdvance">{{i18n .Lang "advance"}}</button>
|
||||
</div>
|
||||
</div>
|
||||
<form>
|
||||
<p></p>
|
||||
<p/>
|
||||
<div class="collapse" id="collapseAdvance">
|
||||
<form class="form">
|
||||
<div class="form-group">
|
||||
@ -139,7 +138,6 @@
|
||||
</span>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
</div>
|
||||
<div class="form-group">
|
||||
<div class="input-group">
|
||||
@ -151,7 +149,6 @@
|
||||
</span>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
</div>
|
||||
</form>
|
||||
</div>
|
||||
@ -170,11 +167,12 @@
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
</form>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="modal fade" id="dlgUser" tabindex="-1" role="dialog" aria-labelledby="User" aria-hidden="true">
|
||||
<div class="modal-dialog">
|
||||
<div class="modal-content">
|
||||
|
@ -78,8 +78,8 @@
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="modal fade" id="dlgAddProject" tabindex="-1" role="dialog" aria-labelledby="Add Project" aria-hidden="true">
|
||||
</div>
|
||||
<div class="modal fade" id="dlgAddProject" tabindex="-1" role="dialog" aria-labelledby="Add Project" aria-hidden="true">
|
||||
<div class="modal-dialog">
|
||||
<div class="modal-content">
|
||||
<div class="modal-header">
|
||||
@ -109,7 +109,7 @@
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<script src="static/resources/js/validate-options.js"></script>
|
||||
<script src="static/resources/js/project.js"></script>
|
||||
|
@ -14,8 +14,8 @@
|
||||
-->
|
||||
<!DOCTYPE html>
|
||||
<html>
|
||||
<body>
|
||||
<body>
|
||||
<p>{{.Hint}}:</p>
|
||||
<a href="{{.URL}}/resetPassword?reset_uuid={{.UUID}}">{{.URL}}/resetPassword?reset_uuid={{.UUID}}</a>
|
||||
</body>
|
||||
</body>
|
||||
</html>
|
@ -14,15 +14,15 @@
|
||||
-->
|
||||
<!DOCTYPE html>
|
||||
<html>
|
||||
<head>
|
||||
<head>
|
||||
{{.HeaderInc}}
|
||||
<title>{{.PageTitle}}</title>
|
||||
</head>
|
||||
<body>
|
||||
</head>
|
||||
<body>
|
||||
{{.HeaderContent}}
|
||||
{{.BodyContent}}
|
||||
{{.FooterInc}}
|
||||
{{.ModalDialog}}
|
||||
{{.FootContent}}
|
||||
</body>
|
||||
</body>
|
||||
</html>
|
@ -11,7 +11,8 @@
|
||||
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.
|
||||
-->
|
||||
-->
|
||||
|
||||
<input type="hidden" id="currentLanguage" value="{{.Lang}}">
|
||||
<input type="hidden" id="isAdmin" value="{{.IsAdmin}}">
|
||||
<nav class="navbar navbar-default" role="navigation" style="margin-bottom: 0;">
|
||||
@ -27,6 +28,7 @@
|
||||
<form class="navbar-form navbar-right">
|
||||
<div class="form-group">
|
||||
<div class="input-group">
|
||||
|
||||
<ul class="nav navbar-nav">
|
||||
<li class="dropdown">
|
||||
<a href="#" class="dropdown-toggle" data-toggle="dropdown" role="button" aria-haspopup="true" aria-expanded="false">
|
||||
@ -87,4 +89,5 @@
|
||||
{{ end }}
|
||||
</form>
|
||||
</div>
|
||||
</nav>
|
||||
</nav>
|
||||
|
||||
|
@ -13,11 +13,11 @@
|
||||
limitations under the License.
|
||||
-->
|
||||
<style>
|
||||
.center {
|
||||
.center {
|
||||
margin-left: auto;
|
||||
margin-right: auto;
|
||||
top: 10%;
|
||||
}
|
||||
}
|
||||
</style>
|
||||
<!-- Modal -->
|
||||
<div class="center modal fade" id="dlgModal" tabindex="-1" role="dialog" aria-labelledby="myModalLabel" aria-hidden="true">
|
||||
|
Loading…
Reference in New Issue
Block a user