Merge pull request #3687 from ywk253100/171123_trigger_api

Add replication manual trigger API & update replication/policy API docs
This commit is contained in:
Wenkai Yin 2017-11-28 13:38:53 +08:00 committed by GitHub
commit fa85ad6d28
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
6 changed files with 229 additions and 124 deletions

View File

@ -1484,7 +1484,7 @@ paths:
description: Create new policy.
required: true
schema:
$ref: '#/definitions/RepPolicyPost'
$ref: '#/definitions/RepPolicy'
tags:
- Products
responses:
@ -1539,10 +1539,10 @@ paths:
description: policy ID
- name: policyupdate
in: body
description: 'Update policy name, description, target and enablement.'
description: 'Updated properties of the replication policy.'
required: true
schema:
$ref: '#/definitions/RepPolicyUpdate'
$ref: '#/definitions/RepPolicy'
tags:
- Products
responses:
@ -1560,35 +1560,27 @@ paths:
project and target.
'500':
description: Unexpected internal errors.
/policies/replication/{id}/enablement:
put:
summary: Put modifies enablement of the policy.
/replications:
post:
summary: Trigger the replication according to the specified policy.
description: |
This endpoint let user update policy enablement flag.
This endpoint is used to trigger a replication.
parameters:
- name: id
in: path
type: integer
format: int64
required: true
description: policy ID
- name: enabledflag
- name: policy ID
in: body
description: The policy enablement flag.
description: The ID of replication policy.
required: true
schema:
$ref: '#/definitions/RepPolicyEnablementReq'
$ref: '#/definitions/Replication'
tags:
- Products
responses:
'200':
description: Update job policy enablement successfully.
'400':
description: Invalid enabled value.
description: Trigger the replication successfully.
'401':
description: User need to log in first.
'404':
description: The specific repository ID's policy does not exist.
description: The policy does not exist.
'500':
description: Unexpected internal errors.
/targets:
@ -2376,27 +2368,22 @@ definitions:
type: integer
format: int64
description: The policy ID.
project_id:
type: integer
format: int64
description: The project ID.
project_name:
type: string
description: The project name.
target_id:
type: integer
format: int64
description: The target ID.
name:
type: string
description: The policy name.
enabled:
type: integer
format: int
description: The policy's enabled status.
description:
type: string
description: The description of the policy.
projects:
type: object
description: The project list that the policy applys to.
items:
$ref: '#/definitions/Project'
targets:
type: object
description: The target list.
items:
$ref: '#/definitions/RepTarget'
trigger:
type: object
description: The trigger for schedule job.
@ -2408,14 +2395,11 @@ definitions:
items:
$ref: '#/definitions/RepFilter'
replicate_existing_image_now:
type: string
type: boolean
description: Whether to replicate the existing images now.
replicate_deletion:
type: string
type: boolean
description: Whether to replicate the deletion operation.
start_time:
type: string
description: The start time of the policy.
creation_time:
type: string
description: The create time of the policy.
@ -2425,100 +2409,27 @@ definitions:
error_job_count:
format: int
description: The error job count number for the policy.
deleted:
type: integer
RepPolicyPost:
type: object
properties:
project_id:
type: integer
format: int64
description: The project ID.
target_id:
type: integer
format: int64
description: The target ID.
name:
type: string
description: The policy name.
trigger:
type: object
description: The trigger for schedule job.
items:
$ref: '#/definitions/RepTrigger'
filters:
type: array
description: The replication policy filter array.
items:
$ref: '#/definitions/RepFilter'
replicate_existing_image_now:
type: string
description: Whether to replicate the existing images now.
replicate_deletion:
type: string
description: Whether replication deletion operation.
enabled:
type: integer
format: int
description: '1-enable, 0-disable'
RepPolicyUpdate:
type: object
properties:
target_id:
type: integer
format: int64
description: The target ID.
name:
type: string
description: The policy name.
enabled:
type: integer
format: int
description: The policy's enabled status.
description:
type: string
description: The description of the policy.
trigger:
type: object
description: The trigger for schedule job.
items:
$ref: '#/definitions/RepTrigger'
filters:
type: array
description: The replication policy filter array.
items:
$ref: '#/definitions/RepFilter'
replicate_existing_image_now:
type: string
description: Whether to replicate the existing images now.
replicate_deletion:
type: string
description: Whether replication deletion operation.
RepTrigger:
type: object
properties:
type:
kind:
type: string
description: The replication policy trigger type.
params:
type: object
description: The map is the replication policy trigger parameters.
description: The replication policy trigger kind.
param:
type: string
description: The replication policy trigger parameters.
RepFilter:
type: object
properties:
type:
kind:
type: string
description: The replication policy filter type.
description: The replication policy filter kind.
value:
type: string
description: The replication policy filter value.
RepPolicyEnablementReq:
type: object
properties:
enabled:
type: integer
format: int
description: The policy enablement flag.
metadata:
type: object
description: This map object is the replication policy filter metadata.
RepTarget:
type: object
properties:
@ -3001,6 +2912,12 @@ definitions:
type: integer
description: The offest in seconds of UTC 0 o'clock, only valid when the policy type is "daily"
description: The parameters of the policy, the values are dependant on the type of the policy.
Replication:
type: object
properties:
policy_id:
type: integer
description: The ID of replication policy

View File

@ -129,6 +129,7 @@ func init() {
beego.Router("/api/configurations", &ConfigAPI{})
beego.Router("/api/configurations/reset", &ConfigAPI{}, "post:Reset")
beego.Router("/api/email/ping", &EmailAPI{}, "post:Ping")
beego.Router("/api/replications", &ReplicationAPI{})
_ = updateInitPassword(1, "Harbor12345")

View File

@ -0,0 +1,31 @@
// Copyright (c) 2017 VMware, Inc. All Rights Reserved.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
package models
import (
"github.com/astaxie/beego/validation"
)
// Replication defines the properties of model used in replication API
type Replication struct {
PolicyID int64 `json:"policy_id"`
}
// Valid ...
func (r *Replication) Valid(v *validation.Validation) {
if r.PolicyID <= 0 {
v.SetError("policy_id", "invalid value")
}
}

63
src/ui/api/replication.go Normal file
View File

@ -0,0 +1,63 @@
// Copyright (c) 2017 VMware, Inc. All Rights Reserved.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
package api
import (
"fmt"
"github.com/vmware/harbor/src/replication/core"
"github.com/vmware/harbor/src/ui/api/models"
)
// ReplicationAPI handles API calls for replication
type ReplicationAPI struct {
BaseController
}
// Prepare does authentication and authorization works
func (r *ReplicationAPI) Prepare() {
r.BaseController.Prepare()
if !r.SecurityCtx.IsAuthenticated() {
r.HandleUnauthorized()
return
}
if !r.SecurityCtx.IsSysAdmin() {
r.HandleForbidden(r.SecurityCtx.GetUsername())
return
}
}
// Post trigger a replication according to the specified policy
func (r *ReplicationAPI) Post() {
replication := &models.Replication{}
r.DecodeJSONReqAndValidate(replication)
policy, err := core.DefaultController.GetPolicy(replication.PolicyID)
if err != nil {
r.HandleInternalServerError(fmt.Sprintf("failed to get replication policy %d: %v", replication.PolicyID, err))
return
}
if policy.ID == 0 {
r.HandleNotFound(fmt.Sprintf("replication policy %d not found", replication.PolicyID))
return
}
if err = core.DefaultController.Replicate(replication.PolicyID); err != nil {
r.HandleInternalServerError(fmt.Sprintf("failed to trigger the replication policy %d: %v", replication.PolicyID, err))
return
}
}

View File

@ -0,0 +1,92 @@
// Copyright (c) 2017 VMware, Inc. All Rights Reserved.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
package api
import (
"fmt"
"net/http"
"testing"
"github.com/stretchr/testify/require"
"github.com/vmware/harbor/src/common/dao"
"github.com/vmware/harbor/src/common/models"
"github.com/vmware/harbor/src/replication"
api_models "github.com/vmware/harbor/src/ui/api/models"
)
const (
replicationAPIBaseURL = "/api/replications"
)
func TestReplicationAPIPost(t *testing.T) {
targetID, err := dao.AddRepTarget(
models.RepTarget{
Name: "test_replication_target",
URL: "127.0.0.1",
Username: "username",
Password: "password",
})
require.Nil(t, err)
defer dao.DeleteRepTarget(targetID)
policyID, err := dao.AddRepPolicy(
models.RepPolicy{
Name: "test_replication_policy",
ProjectID: 1,
TargetID: targetID,
Trigger: fmt.Sprintf("{\"kind\":\"%s\"}", replication.TriggerKindManual),
})
require.Nil(t, err)
defer dao.DeleteRepPolicy(policyID)
cases := []*codeCheckingCase{
// 401
&codeCheckingCase{
request: &testingRequest{
method: http.MethodPost,
url: replicationAPIBaseURL,
bodyJSON: &api_models.Replication{
PolicyID: policyID,
},
},
code: http.StatusUnauthorized,
},
// 404
&codeCheckingCase{
request: &testingRequest{
method: http.MethodPost,
url: replicationAPIBaseURL,
bodyJSON: &api_models.Replication{
PolicyID: 10000,
},
credential: admin,
},
code: http.StatusNotFound,
},
// 200
&codeCheckingCase{
request: &testingRequest{
method: http.MethodPost,
url: replicationAPIBaseURL,
bodyJSON: &api_models.Replication{
PolicyID: policyID,
},
credential: admin,
},
code: http.StatusOK,
},
}
runCodeCheckingCases(t, cases...)
}

View File

@ -118,6 +118,7 @@ func initRouters() {
beego.Router("/api/configurations", &api.ConfigAPI{})
beego.Router("/api/configurations/reset", &api.ConfigAPI{}, "post:Reset")
beego.Router("/api/statistics", &api.StatisticAPI{})
beego.Router("/api/replications", &api.ReplicationAPI{})
beego.Router("/api/systeminfo", &api.SystemInfoAPI{}, "get:GetGeneralInfo")
beego.Router("/api/systeminfo/volumes", &api.SystemInfoAPI{}, "get:GetVolumeInfo")