You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@devlake.apache.org by zh...@apache.org on 2022/10/12 12:11:30 UTC
[incubator-devlake] branch main updated: feat: new deploy task webhook (#3379)
This is an automated email from the ASF dual-hosted git repository.
zhangliang2022 pushed a commit to branch main
in repository https://gitbox.apache.org/repos/asf/incubator-devlake.git
The following commit(s) were added to refs/heads/main by this push:
new e0e35a18 feat: new deploy task webhook (#3379)
e0e35a18 is described below
commit e0e35a1897cb6457771167d1528672fa2d47318c
Author: Likyh <l...@likyh.com>
AuthorDate: Wed Oct 12 20:11:26 2022 +0800
feat: new deploy task webhook (#3379)
* feat: new deploy task webhook
* fix: fix for some bugs
* fix: use new url in config-ui's webhook page
* fix: update some validator
* fix: change some wording
Co-authored-by: linyh <ya...@meri.co>
---
.../connections/incoming-webhook/add-modal.jsx | 20 ++--
.../pages/connections/incoming-webhook/index.jsx | 3 +-
.../incoming-webhook/view-or-edit-modal.jsx | 17 +--
plugins/webhook/api/cicd_pipeline.go | 115 +++++++++++++++++++++
plugins/webhook/api/connection.go | 10 +-
plugins/webhook/impl/impl.go | 3 +
6 files changed, 136 insertions(+), 32 deletions(-)
diff --git a/config-ui/src/pages/connections/incoming-webhook/add-modal.jsx b/config-ui/src/pages/connections/incoming-webhook/add-modal.jsx
index f160ce1b..f52c34d1 100644
--- a/config-ui/src/pages/connections/incoming-webhook/add-modal.jsx
+++ b/config-ui/src/pages/connections/incoming-webhook/add-modal.jsx
@@ -54,8 +54,7 @@ export const AddModal = ({ onSubmit, onCancel }) => {
setRecord({
postIssuesEndpoint: `${postUrlPrefix}${res.postIssuesEndpoint}`,
closeIssuesEndpoint: `${postUrlPrefix}${res.closeIssuesEndpoint}`,
- postPipelineTaskEndpoint: `${postUrlPrefix}${res.postPipelineTaskEndpoint}`,
- closePipelineEndpoint: `${postUrlPrefix}${res.closePipelineEndpoint}`
+ postDeploymentsEndpoint: `${postUrlPrefix}${res.postPipelineDeployTaskEndpoint}`
})
}
@@ -106,7 +105,7 @@ export const AddModal = ({ onSubmit, onCancel }) => {
DevLake.
</p>
<h3>Incident</h3>
- <p>Send incident opened and reopened events</p>
+ <p>POST to register an incident</p>
<div className='block'>
<span>{record.postIssuesEndpoint}</span>
<CopyToClipboard
@@ -121,7 +120,7 @@ export const AddModal = ({ onSubmit, onCancel }) => {
<CopyIcon width={16} height={16} />
</CopyToClipboard>
</div>
- <p>Send incident resolved events</p>
+ <p>POST to close a registered incident</p>
<div className='block'>
<span>{record.closeIssuesEndpoint}</span>
<CopyToClipboard
@@ -137,17 +136,10 @@ export const AddModal = ({ onSubmit, onCancel }) => {
</CopyToClipboard>
</div>
<h3>Deployment</h3>
- <p>Trigger after the "deployment" jobs/builds finished</p>
+ <p>POST to register a deployment</p>
<div className='block'>
- <span>{record.postPipelineTaskEndpoint}</span>
- <CopyToClipboard text={record.postPipelineTaskEndpoint}>
- <CopyIcon width={16} height={16} />
- </CopyToClipboard>
- </div>
- <p>Trigger after all CI jobs/builds finished</p>
- <div className='block'>
- <span>{record.closePipelineEndpoint}</span>
- <CopyToClipboard text={record.closePipelineEndpoint}>
+ <span>{record.postDeploymentsEndpoint}</span>
+ <CopyToClipboard text={record.postDeploymentsEndpoint}>
<CopyIcon width={16} height={16} />
</CopyToClipboard>
</div>
diff --git a/config-ui/src/pages/connections/incoming-webhook/index.jsx b/config-ui/src/pages/connections/incoming-webhook/index.jsx
index 51546307..823682f5 100644
--- a/config-ui/src/pages/connections/incoming-webhook/index.jsx
+++ b/config-ui/src/pages/connections/incoming-webhook/index.jsx
@@ -53,8 +53,7 @@ export const IncomingWebhook = () => {
...r,
postIssuesEndpoint: `${postUrlPrefix}${r.postIssuesEndpoint}`,
closeIssuesEndpoint: `${postUrlPrefix}${r.closeIssuesEndpoint}`,
- postPipelineTaskEndpoint: `${postUrlPrefix}${r.postPipelineTaskEndpoint}`,
- closePipelineEndpoint: `${postUrlPrefix}${r.closePipelineEndpoint}`
+ postDeploymentsEndpoint: `${postUrlPrefix}${r.postPipelineDeployTaskEndpoint}`
}
: existingRecord
)
diff --git a/config-ui/src/pages/connections/incoming-webhook/view-or-edit-modal.jsx b/config-ui/src/pages/connections/incoming-webhook/view-or-edit-modal.jsx
index 874aab9c..75d0d037 100644
--- a/config-ui/src/pages/connections/incoming-webhook/view-or-edit-modal.jsx
+++ b/config-ui/src/pages/connections/incoming-webhook/view-or-edit-modal.jsx
@@ -86,7 +86,7 @@ export const ViewOrEditModal = ({ record, onSubmit, onCancel }) => {
and CI tool for Deployments by making a POST to DevLake.
</p>
<h3>Incident</h3>
- <p>Send incident opened and reopened events</p>
+ <p>POST to register an incident </p>
<div className='block'>
<span>{record.postIssuesEndpoint}</span>
<CopyToClipboard
@@ -101,7 +101,7 @@ export const ViewOrEditModal = ({ record, onSubmit, onCancel }) => {
<CopyIcon width={16} height={16} />
</CopyToClipboard>
</div>
- <p>Send incident resolved events</p>
+ <p>POST to close a registered incident</p>
<div className='block'>
<span>{record.closeIssuesEndpoint}</span>
<CopyToClipboard
@@ -117,17 +117,10 @@ export const ViewOrEditModal = ({ record, onSubmit, onCancel }) => {
</CopyToClipboard>
</div>
<h3>Deployment</h3>
- <p>Trigger after the "deployment" jobs/builds finished</p>
+ <p>POST to register a deployment</p>
<div className='block'>
- <span>{record.postPipelineTaskEndpoint}</span>
- <CopyToClipboard text={record.postPipelineTaskEndpoint}>
- <CopyIcon width={16} height={16} />
- </CopyToClipboard>
- </div>
- <p>Trigger after all CI jobs/builds finished</p>
- <div className='block'>
- <span>{record.closePipelineEndpoint}</span>
- <CopyToClipboard text={record.closePipelineEndpoint}>
+ <span>{record.postDeploymentsEndpoint}</span>
+ <CopyToClipboard text={record.postDeploymentsEndpoint}>
<CopyIcon width={16} height={16} />
</CopyToClipboard>
</div>
diff --git a/plugins/webhook/api/cicd_pipeline.go b/plugins/webhook/api/cicd_pipeline.go
index 0de18e85..d8727746 100644
--- a/plugins/webhook/api/cicd_pipeline.go
+++ b/plugins/webhook/api/cicd_pipeline.go
@@ -18,6 +18,7 @@ limitations under the License.
package api
import (
+ "crypto/md5"
"fmt"
"net/http"
"reflect"
@@ -251,3 +252,117 @@ func getTypeAndResultFromTasks(domainTasks []devops.CICDTask) (pipelineType, res
}
return
}
+
+type WebhookDeployTaskRequest struct {
+ // RepoUrl should be unique string, fill url or other unique data
+ RepoUrl string `mapstructure:"repo_url" validate:"required"`
+ CommitSha string `mapstructure:"commit_sha" validate:"required"`
+ // start_time and end_time is more readable for users,
+ // StartedDate and FinishedDate is same as columns in db.
+ // So they all keep.
+ StartedDate *time.Time `mapstructure:"start_time" validate:"required_with=FinishedDate"`
+ FinishedDate *time.Time `mapstructure:"end_time"`
+ Environment string `validate:"omitempty,oneof=PRODUCTION STAGING TESTING DEVELOPMENT"`
+}
+
+// PostCicdTask
+// @Summary create deployment pipeline by webhook
+// @Description Create deployment pipeline by webhook.<br/>
+// @Description example1: {"repo_url":"devlake","commit_sha":"015e3d3b480e417aede5a1293bd61de9b0fd051d","start_time":"2020-01-01T12:00:00+00:00","end_time":"2020-01-01T12:59:59+00:00","environment":"PRODUCTION"}<br/>
+// @Description So we suggest request before task after deployment pipeline finish.
+// @Description Both cicd_pipeline and cicd_task will be created
+// @Tags plugins/webhook
+// @Param body body WebhookDeployTaskRequest true "json body"
+// @Success 200
+// @Failure 400 {string} errcode.Error "Bad Request"
+// @Failure 403 {string} errcode.Error "Forbidden"
+// @Failure 500 {string} errcode.Error "Internal Error"
+// @Router /plugins/webhook/:connectionId/deployments [POST]
+func PostDeploymentCicdTask(input *core.ApiResourceInput) (*core.ApiResourceOutput, errors.Error) {
+ connection := &models.WebhookConnection{}
+ err := connectionHelper.First(connection, input.Params)
+ if err != nil {
+ return nil, err
+ }
+ // get request
+ request := &WebhookDeployTaskRequest{}
+ err = helper.DecodeMapStruct(input.Body, request)
+ if err != nil {
+ return &core.ApiResourceOutput{Body: err.Error(), Status: http.StatusBadRequest}, nil
+ }
+ // validate
+ vld = validator.New()
+ err = errors.Convert(vld.Struct(request))
+ if err != nil {
+ return nil, errors.BadInput.Wrap(vld.Struct(request), `input json error`)
+ }
+
+ db := basicRes.GetDal()
+ urlHash16 := fmt.Sprintf("%x", md5.Sum([]byte(request.RepoUrl)))[:16]
+ pipelineId := fmt.Sprintf("%s:%d:%s:%s:%s", "webhook", connection.ID, `pipeline`, urlHash16, request.CommitSha)
+
+ taskId := fmt.Sprintf("%s:%d:%s:%s", "webhook", connection.ID, urlHash16, request.CommitSha)
+ domainCicdTask := &devops.CICDTask{
+ DomainEntity: domainlayer.DomainEntity{
+ Id: taskId,
+ },
+ PipelineId: pipelineId,
+ Name: fmt.Sprintf(`deployment for %s`, request.CommitSha),
+ Result: devops.SUCCESS,
+ Status: devops.DONE,
+ Type: devops.DEPLOYMENT,
+ Environment: request.Environment,
+ }
+ now := time.Now()
+ if request.StartedDate != nil {
+ domainCicdTask.StartedDate = *request.StartedDate
+ if request.FinishedDate != nil {
+ domainCicdTask.FinishedDate = request.FinishedDate
+ } else {
+ domainCicdTask.FinishedDate = &now
+ }
+ domainCicdTask.DurationSec = uint64(domainCicdTask.FinishedDate.Sub(domainCicdTask.StartedDate).Seconds())
+ } else {
+ domainCicdTask.StartedDate = now
+ }
+ if domainCicdTask.Environment == `` {
+ domainCicdTask.Environment = devops.PRODUCTION
+ }
+
+ domainPipeline := &devops.CICDPipeline{
+ DomainEntity: domainlayer.DomainEntity{
+ Id: pipelineId,
+ },
+ Name: fmt.Sprintf(`pipeline for %s`, request.CommitSha),
+ Result: devops.SUCCESS,
+ Status: devops.DONE,
+ Type: devops.DEPLOYMENT,
+ CreatedDate: domainCicdTask.StartedDate,
+ FinishedDate: domainCicdTask.FinishedDate,
+ DurationSec: domainCicdTask.DurationSec,
+ Environment: domainCicdTask.Environment,
+ }
+
+ domainPipelineCommit := &devops.CiCDPipelineCommit{
+ PipelineId: pipelineId,
+ CommitSha: request.CommitSha,
+ Branch: ``,
+ RepoId: request.RepoUrl,
+ }
+
+ // save
+ err = db.CreateOrUpdate(domainCicdTask)
+ if err != nil {
+ return nil, err
+ }
+ err = db.CreateOrUpdate(domainPipeline)
+ if err != nil {
+ return nil, err
+ }
+ err = db.CreateOrUpdate(domainPipelineCommit)
+ if err != nil {
+ return nil, err
+ }
+
+ return &core.ApiResourceOutput{Body: nil, Status: http.StatusOK}, nil
+}
diff --git a/plugins/webhook/api/connection.go b/plugins/webhook/api/connection.go
index 652085ce..0cedf27d 100644
--- a/plugins/webhook/api/connection.go
+++ b/plugins/webhook/api/connection.go
@@ -83,10 +83,11 @@ func DeleteConnection(input *core.ApiResourceInput) (*core.ApiResourceOutput, er
type WebhookConnectionResponse struct {
models.WebhookConnection
- PostIssuesEndpoint string `json:"postIssuesEndpoint"`
- CloseIssuesEndpoint string `json:"closeIssuesEndpoint"`
- PostPipelineTaskEndpoint string `json:"postPipelineTaskEndpoint"`
- ClosePipelineEndpoint string `json:"closePipelineEndpoint"`
+ PostIssuesEndpoint string `json:"postIssuesEndpoint"`
+ CloseIssuesEndpoint string `json:"closeIssuesEndpoint"`
+ PostPipelineTaskEndpoint string `json:"postPipelineTaskEndpoint"`
+ PostPipelineDeployTaskEndpoint string `json:"postPipelineDeployTaskEndpoint"`
+ ClosePipelineEndpoint string `json:"closePipelineEndpoint"`
}
// ListConnections
@@ -130,6 +131,7 @@ func formatConnection(connection *models.WebhookConnection) *WebhookConnectionRe
response.PostIssuesEndpoint = fmt.Sprintf(`/plugins/webhook/%d/issues`, connection.ID)
response.CloseIssuesEndpoint = fmt.Sprintf(`/plugins/webhook/%d/issue/:boardKey/:issueKey/close`, connection.ID)
response.PostPipelineTaskEndpoint = fmt.Sprintf(`/plugins/webhook/%d/cicd_tasks`, connection.ID)
+ response.PostPipelineDeployTaskEndpoint = fmt.Sprintf(`/plugins/webhook/%d/deployments`, connection.ID)
response.ClosePipelineEndpoint = fmt.Sprintf(`/plugins/webhook/%d/cicd_pipeline/:pipelineName/finish`, connection.ID)
return response
}
diff --git a/plugins/webhook/impl/impl.go b/plugins/webhook/impl/impl.go
index a939f560..4301509a 100644
--- a/plugins/webhook/impl/impl.go
+++ b/plugins/webhook/impl/impl.go
@@ -69,6 +69,9 @@ func (plugin Webhook) ApiResources() map[string]map[string]core.ApiResourceHandl
":connectionId/cicd_pipeline/:pipelineName/finish": {
"POST": api.PostPipelineFinish,
},
+ ":connectionId/deployments": {
+ "POST": api.PostDeploymentCicdTask,
+ },
":connectionId/issues": {
"POST": api.PostIssue,
},