blob: 33bc63285236599b798e10ff12b7b897931da8d9 [file] [log] [blame]
/*
Licensed to the Apache Software Foundation (ASF) under one or more
contributor license agreements. See the NOTICE file distributed with
this work for additional information regarding copyright ownership.
The ASF licenses this file to You 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 (
"crypto/md5"
"fmt"
"net/http"
"time"
"github.com/apache/incubator-devlake/core/errors"
"github.com/apache/incubator-devlake/core/models/domainlayer"
"github.com/apache/incubator-devlake/core/models/domainlayer/devops"
"github.com/apache/incubator-devlake/core/plugin"
"github.com/apache/incubator-devlake/helpers/pluginhelper/api"
"github.com/apache/incubator-devlake/plugins/webhook/models"
"github.com/go-playground/validator/v10"
)
type WebhookDeployTaskRequest struct {
PipelineId string `mapstructure:"pipeline_id"`
// RepoUrl should be unique string, fill url or other unique data
RepoId string `mapstructure:"repo_id"`
RepoUrl string `mapstructure:"repo_url" validate:"required"`
CommitSha string `mapstructure:"commit_sha" validate:"required"`
RefName string `mapstructure:"ref_name"`
Result string `mapstructure:"result"`
// start_time and end_time is more readable for users,
// StartedDate and FinishedDate is same as columns in db.
// So they all keep.
CreatedDate *time.Time `mapstructure:"create_time"`
StartedDate *time.Time `mapstructure:"start_time" validate:"required"`
FinishedDate *time.Time `mapstructure:"end_time"`
Environment string `validate:"omitempty,oneof=PRODUCTION STAGING TESTING DEVELOPMENT"`
}
// PostDeploymentCicdTask
// @Summary create deployment 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 *plugin.ApiResourceInput) (*plugin.ApiResourceOutput, errors.Error) {
connection := &models.WebhookConnection{}
err := connectionHelper.First(connection, input.Params)
if err != nil {
return nil, err
}
// get request
request := &WebhookDeployTaskRequest{}
err = api.DecodeMapStruct(input.Body, request, true)
if err != nil {
return &plugin.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]
scopeId := fmt.Sprintf("%s:%d", "webhook", connection.ID)
deploymentCommitId := fmt.Sprintf("%s:%d:%s:%s", "webhook", connection.ID, urlHash16, request.CommitSha)
pipelineId := request.PipelineId
if pipelineId == "" {
pipelineId = deploymentCommitId
}
if request.CreatedDate == nil {
request.CreatedDate = request.StartedDate
}
if request.Environment == "" {
request.Environment = devops.PRODUCTION
}
if request.FinishedDate == nil {
now := time.Now()
request.FinishedDate = &now
}
if request.Result == "" {
request.Result = devops.RESULT_SUCCESS
}
duration := uint64(request.FinishedDate.Sub(*request.StartedDate).Seconds())
// create a deployment_commit record
deploymentCommit := &devops.CicdDeploymentCommit{
DomainEntity: domainlayer.DomainEntity{
Id: deploymentCommitId,
},
CicdDeploymentId: pipelineId,
CicdScopeId: scopeId,
Name: fmt.Sprintf(`deployment for %s`, request.CommitSha),
Result: request.Result,
Status: devops.STATUS_DONE,
Environment: request.Environment,
CreatedDate: *request.CreatedDate,
StartedDate: request.StartedDate,
FinishedDate: request.FinishedDate,
DurationSec: &duration,
CommitSha: request.CommitSha,
RefName: request.RefName,
RepoId: request.RepoId,
RepoUrl: request.RepoUrl,
}
err = db.CreateOrUpdate(deploymentCommit)
if err != nil {
return nil, err
}
// TODO: create a deployment record when the table is ready
return &plugin.ApiResourceOutput{Body: nil, Status: http.StatusOK}, nil
}