This is an automated email from the ASF dual-hosted git repository.
abeizn pushed a commit to branch release-v0.20
in repository https://gitbox.apache.org/repos/asf/incubator-devlake.git
The following commit(s) were added to refs/heads/release-v0.20 by this push:
new 7d6ee36cd cherry pick #6698 fix(dora): add deployment generator to
v0.20 (#6703)
7d6ee36cd is described below
commit 7d6ee36cd36153ad90bb59e8cd6fc2e2130d37d9
Author: Lynwee <[email protected]>
AuthorDate: Thu Dec 28 15:20:56 2023 +0800
cherry pick #6698 fix(dora): add deployment generator to v0.20 (#6703)
* fix(dora): add deployment generator
* fix(test): fix unit test
* fix(deployments): check and update convertor that can generate
cicd_deployments
* fix(dora): make code better
* fix(plugins): fix errors when cherrying pick
---
backend/plugins/bamboo/e2e/deploy_build_test.go | 2 +-
backend/plugins/bamboo/impl/impl.go | 3 +-
...deploy_build_to_deployment_commit_convertor.go} | 12 +-
.../tasks/deploy_build_to_deployment_convertor.go | 117 ++++++++++++++++++
.../bitbucket/tasks/deployment_convertor.go | 2 +
backend/plugins/dora/impl/impl.go | 2 +
backend/plugins/dora/impl/impl_test.go | 1 +
.../dora/tasks/deployment_commits_generator.go | 5 +-
backend/plugins/dora/tasks/deployment_generator.go | 135 +++++++++++++++++++++
.../plugins/github/tasks/deployment_convertor.go | 2 +
.../plugins/gitlab/tasks/deployment_convertor.go | 3 +-
11 files changed, 273 insertions(+), 11 deletions(-)
diff --git a/backend/plugins/bamboo/e2e/deploy_build_test.go
b/backend/plugins/bamboo/e2e/deploy_build_test.go
index 730e36a1d..ef40e1e90 100644
--- a/backend/plugins/bamboo/e2e/deploy_build_test.go
+++ b/backend/plugins/bamboo/e2e/deploy_build_test.go
@@ -97,7 +97,7 @@ func TestBambooDeployBuildDataFlow(t *testing.T) {
dataflowTester.FlushTabler(&devops.CicdDeploymentCommit{})
dataflowTester.FlushTabler(&devops.CICDDeployment{})
- dataflowTester.Subtask(tasks.ConvertDeployBuildsMeta, taskData)
+
dataflowTester.Subtask(tasks.ConvertDeployBuildsToDeploymentCommitsMeta,
taskData)
dataflowTester.VerifyTableWithOptions(&devops.CicdDeploymentCommit{},
e2ehelper.TableOptions{
CSVRelPath: "./snapshot_tables/cicd_deployment_commits.csv",
IgnoreTypes: []interface{}{common.NoPKModel{}},
diff --git a/backend/plugins/bamboo/impl/impl.go
b/backend/plugins/bamboo/impl/impl.go
index 05965c456..d600b99aa 100644
--- a/backend/plugins/bamboo/impl/impl.go
+++ b/backend/plugins/bamboo/impl/impl.go
@@ -117,7 +117,8 @@ func (p Bamboo) SubTaskMetas() []plugin.SubTaskMeta {
tasks.ConvertJobBuildsMeta,
tasks.ConvertPlanBuildsMeta,
tasks.ConvertPlanVcsMeta,
- tasks.ConvertDeployBuildsMeta,
+ tasks.ConvertDeployBuildsToDeploymentCommitsMeta,
+ tasks.ConvertDeployBuildsToDeploymentMeta,
}
}
diff --git a/backend/plugins/bamboo/tasks/deploy_build_convertor.go
b/backend/plugins/bamboo/tasks/deploy_build_to_deployment_commit_convertor.go
similarity index 94%
rename from backend/plugins/bamboo/tasks/deploy_build_convertor.go
rename to
backend/plugins/bamboo/tasks/deploy_build_to_deployment_commit_convertor.go
index 885206137..963e568b2 100644
--- a/backend/plugins/bamboo/tasks/deploy_build_convertor.go
+++
b/backend/plugins/bamboo/tasks/deploy_build_to_deployment_commit_convertor.go
@@ -34,11 +34,11 @@ import (
"github.com/apache/incubator-devlake/plugins/bamboo/models"
)
-var ConvertDeployBuildsMeta = plugin.SubTaskMeta{
- Name: "convertDeployBuilds",
- EntryPoint: ConvertDeployBuilds,
+var ConvertDeployBuildsToDeploymentCommitsMeta = plugin.SubTaskMeta{
+ Name: "convertDeployBuildsToDeploymentCommits",
+ EntryPoint: ConvertDeployBuildsToDeploymentCommits,
EnabledByDefault: true,
- Description: "Convert tool layer table bamboo_deploy_builds into
domain layer table deployBuilds",
+ Description: "Convert tool layer table bamboo_deploy_builds into
domain layer table cicd_deployment_commits",
DomainTypes: []string{plugin.DOMAIN_TYPE_CICD},
}
@@ -77,7 +77,7 @@ func (deployBuildWithVcsRevision deployBuildWithVcsRevision)
GenerateCICDDeploym
return deployBuildWithVcsRevision.DeploymentVersionName
}
-func ConvertDeployBuilds(taskCtx plugin.SubTaskContext) errors.Error {
+func ConvertDeployBuildsToDeploymentCommits(taskCtx plugin.SubTaskContext)
errors.Error {
db := taskCtx.GetDal()
logger := taskCtx.GetLogger()
rawDataSubTaskArgs, data := CreateRawDataSubTaskArgs(taskCtx,
RAW_JOB_BUILD_TABLE)
@@ -150,7 +150,7 @@ func ConvertDeployBuilds(taskCtx plugin.SubTaskContext)
errors.Error {
deploymentCommit.RepoUrl = fakeRepoUrl
}
- return []interface{}{deploymentCommit,
deploymentCommit.ToDeployment()}, nil
+ return []interface{}{deploymentCommit}, nil
},
})
diff --git
a/backend/plugins/bamboo/tasks/deploy_build_to_deployment_convertor.go
b/backend/plugins/bamboo/tasks/deploy_build_to_deployment_convertor.go
new file mode 100644
index 000000000..692cf9f1f
--- /dev/null
+++ b/backend/plugins/bamboo/tasks/deploy_build_to_deployment_convertor.go
@@ -0,0 +1,117 @@
+/*
+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 tasks
+
+import (
+ "fmt"
+ "reflect"
+ "time"
+
+ "github.com/apache/incubator-devlake/core/dal"
+ "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/models/domainlayer/didgen"
+ "github.com/apache/incubator-devlake/core/plugin"
+ "github.com/apache/incubator-devlake/helpers/pluginhelper/api"
+ "github.com/apache/incubator-devlake/plugins/bamboo/models"
+)
+
+var ConvertDeployBuildsToDeploymentMeta = plugin.SubTaskMeta{
+ Name: "convertDeployBuildsToDeployments",
+ EntryPoint: ConvertDeployBuildsToDeployments,
+ EnabledByDefault: true,
+ Description: "Convert tool layer table bamboo_deploy_builds into
domain layer table cicd_deployments",
+ DomainTypes: []string{plugin.DOMAIN_TYPE_CICD},
+}
+
+type bambooDeployBuildEx struct {
+ models.BambooDeployBuild
+ ProjectPlanName string
+ ProjectName string
+}
+
+func ConvertDeployBuildsToDeployments(taskCtx plugin.SubTaskContext)
errors.Error {
+ db := taskCtx.GetDal()
+ rawDataSubTaskArgs, data := CreateRawDataSubTaskArgs(taskCtx,
RAW_JOB_BUILD_TABLE)
+ cursor, err := db.Cursor(
+ dal.Select("db.*, p.name as project_plan_name, p.project_name"),
+ dal.From("_tool_bamboo_deploy_builds AS db"),
+ dal.Join("LEFT JOIN _tool_bamboo_plans as p ON db.plan_key =
p.plan_key"),
+ dal.Where("db.connection_id = ? and db.plan_key = ?",
data.Options.ConnectionId, data.Options.PlanKey),
+ )
+ if err != nil {
+ return err
+ }
+ defer cursor.Close()
+
+ planIdGen := didgen.NewDomainIdGenerator(&models.BambooPlan{})
+ converter, err := api.NewDataConverter(api.DataConverterArgs{
+ InputRowType: reflect.TypeOf(bambooDeployBuildEx{}),
+ Input: cursor,
+ RawDataSubTaskArgs: *rawDataSubTaskArgs,
+ Convert: func(inputRow interface{}) ([]interface{},
errors.Error) {
+ input := inputRow.(*bambooDeployBuildEx)
+ deploymentCommitId :=
didgen.NewDomainIdGenerator(&bambooDeployBuildEx{}).Generate(data.Options.ConnectionId,
input.DeployBuildId)
+ createdDate := time.Now()
+ if input.StartedDate != nil {
+ createdDate = *input.StartedDate
+ }
+ name := input.DeploymentVersionName
+ if input.ProjectPlanName != "" {
+ name = fmt.Sprintf("%s/%s",
input.ProjectPlanName, input.DeploymentVersionName)
+ }
+
+ deployment := &devops.CICDDeployment{
+ DomainEntity: domainlayer.DomainEntity{
+ Id: deploymentCommitId,
+ },
+ CicdScopeId:
planIdGen.Generate(data.Options.ConnectionId, data.Options.PlanKey),
+ Name: name,
+ Result: devops.GetResult(&devops.ResultRule{
+ Success: []string{ResultSuccess,
ResultSuccessful},
+ Failure: []string{ResultFailed},
+ Default: devops.RESULT_DEFAULT,
+ }, input.DeploymentState),
+ Status: devops.GetStatus(&devops.StatusRule{
+ Done: []string{StatusFinished},
+ InProgress: []string{StatusInProgress,
StatusPending, StatusQueued},
+ Default: devops.STATUS_OTHER,
+ }, input.LifeCycleState),
+ Environment: input.Environment,
+ CreatedDate: createdDate,
+ StartedDate: input.ExecutedDate,
+ FinishedDate: input.FinishedDate,
+ }
+ if
data.RegexEnricher.ReturnNameIfMatched(devops.ENV_NAME_PATTERN,
input.Environment) != "" {
+ deployment.Environment = devops.PRODUCTION
+ }
+ if input.FinishedDate != nil && input.ExecutedDate !=
nil {
+ duration :=
uint64(input.FinishedDate.Sub(*input.ExecutedDate).Milliseconds() / 1e3)
+ deployment.DurationSec = &duration
+ }
+ return []interface{}{deployment}, nil
+ },
+ })
+
+ if err != nil {
+ return err
+ }
+
+ return converter.Execute()
+}
diff --git a/backend/plugins/bitbucket/tasks/deployment_convertor.go
b/backend/plugins/bitbucket/tasks/deployment_convertor.go
index da11e9dd7..dff35d99b 100644
--- a/backend/plugins/bitbucket/tasks/deployment_convertor.go
+++ b/backend/plugins/bitbucket/tasks/deployment_convertor.go
@@ -44,6 +44,8 @@ type bitbucketDeploymentWithRefName struct {
RefName string
}
+// ConvertDeployments should be split into two task theoretically
+// But in BitBucket, all deployments have commits, and we use "LEFT JOIN" to
get "ref_name" only, so there is no need to change it.
func ConvertDeployments(taskCtx plugin.SubTaskContext) errors.Error {
rawDataSubTaskArgs, data := CreateRawDataSubTaskArgs(taskCtx,
RAW_PIPELINE_TABLE)
db := taskCtx.GetDal()
diff --git a/backend/plugins/dora/impl/impl.go
b/backend/plugins/dora/impl/impl.go
index c0d798143..b20ce189d 100644
--- a/backend/plugins/dora/impl/impl.go
+++ b/backend/plugins/dora/impl/impl.go
@@ -89,6 +89,7 @@ func (p Dora) Settings() interface{} {
func (p Dora) SubTaskMetas() []plugin.SubTaskMeta {
return []plugin.SubTaskMeta{
+ tasks.DeploymentGeneratorMeta,
tasks.DeploymentCommitsGeneratorMeta,
tasks.EnrichPrevSuccessDeploymentCommitMeta,
tasks.EnrichTaskEnvMeta,
@@ -130,6 +131,7 @@ func (p Dora) MakeMetricPluginPipelinePlanV200(projectName
string, options json.
"projectName": projectName,
},
Subtasks: []string{
+ "generateDeployments",
"generateDeploymentCommits",
"enrichPrevSuccessDeploymentCommits",
},
diff --git a/backend/plugins/dora/impl/impl_test.go
b/backend/plugins/dora/impl/impl_test.go
index 587cc517e..461897dc2 100644
--- a/backend/plugins/dora/impl/impl_test.go
+++ b/backend/plugins/dora/impl/impl_test.go
@@ -41,6 +41,7 @@ func TestMakeMetricPluginPipelinePlanV200(t *testing.T) {
{
Plugin: "dora",
Subtasks: []string{
+ "generateDeployments",
"generateDeploymentCommits",
"enrichPrevSuccessDeploymentCommits",
},
diff --git a/backend/plugins/dora/tasks/deployment_commits_generator.go
b/backend/plugins/dora/tasks/deployment_commits_generator.go
index 34237e141..3df31fff0 100644
--- a/backend/plugins/dora/tasks/deployment_commits_generator.go
+++ b/backend/plugins/dora/tasks/deployment_commits_generator.go
@@ -62,7 +62,8 @@ func GenerateDeploymentCommits(taskCtx plugin.SubTaskContext)
errors.Error {
cursor, err := db.Cursor(
dal.Select(
`
- pc.*, p.name as pipeline_name,
+ pc.*,
+ p.name as pipeline_name,
p.result,
p.status,
p.duration_sec,
@@ -157,7 +158,7 @@ func GenerateDeploymentCommits(taskCtx
plugin.SubTaskContext) errors.Error {
domainDeployCommit.Environment =
devops.TESTING
}
}
- return []interface{}{domainDeployCommit,
domainDeployCommit.ToDeployment()}, nil
+ return []interface{}{domainDeployCommit}, nil
},
})
if err != nil {
diff --git a/backend/plugins/dora/tasks/deployment_generator.go
b/backend/plugins/dora/tasks/deployment_generator.go
new file mode 100644
index 000000000..5f4f44baf
--- /dev/null
+++ b/backend/plugins/dora/tasks/deployment_generator.go
@@ -0,0 +1,135 @@
+/*
+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 tasks
+
+import (
+ "reflect"
+ "time"
+
+ "github.com/apache/incubator-devlake/core/dal"
+ "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"
+)
+
+var DeploymentGeneratorMeta = plugin.SubTaskMeta{
+ Name: "generateDeployments",
+ EntryPoint: GenerateDeployment,
+ EnabledByDefault: true,
+ Description: "Generate cicd_deployments from cicd_pipelines if
cicd_pipeline.type == DEPLOYMENT or any of its cicd_tasks is a deployment task",
+ DomainTypes: []string{plugin.DOMAIN_TYPE_CICD},
+}
+
+type pipelineEx struct {
+ devops.CICDPipeline
+ HasTestingTasks bool
+ HasStagingTasks bool
+ HasProductionTasks bool
+}
+
+func GenerateDeployment(taskCtx plugin.SubTaskContext) errors.Error {
+ db := taskCtx.GetDal()
+ data := taskCtx.GetData().(*DoraTaskData)
+ // Note that failed records shall be included as well
+ noneSkippedResult := []string{devops.RESULT_FAILURE,
devops.RESULT_SUCCESS}
+ cursor, err := db.Cursor(
+ dal.Select(
+ `
+ p.*,
+ EXISTS(SELECT 1 FROM cicd_tasks t WHERE
t.pipeline_id = p.id AND t.environment = ? AND t.result IN ?)
+ as has_testing_tasks,
+ EXISTS(SELECT 1 FROM cicd_tasks t WHERE
t.pipeline_id = p.id AND t.environment = ? AND t.result IN ?)
+ as has_staging_tasks,
+ EXISTS( SELECT 1 FROM cicd_tasks t WHERE
t.pipeline_id = p.id AND t.environment = ? AND t.result IN ?)
+ as has_production_tasks
+ `,
+ devops.TESTING, noneSkippedResult,
+ devops.STAGING, noneSkippedResult,
+ devops.PRODUCTION, noneSkippedResult,
+ ),
+ dal.From("cicd_pipelines p"),
+ dal.Join("LEFT JOIN project_mapping pm ON (pm.table =
'cicd_scopes' AND pm.row_id = p.cicd_scope_id)"),
+ dal.Where(
+ `
+ pm.project_name = ? AND (
+ p.type = ? OR EXISTS(
+ SELECT 1 FROM cicd_tasks t WHERE
t.pipeline_id = p.id AND t.type = ? AND t.result IN ?
+ )
+ ) AND p.result IN ?
+ `,
+ data.Options.ProjectName,
+ devops.DEPLOYMENT,
+ devops.DEPLOYMENT,
+ noneSkippedResult,
+ noneSkippedResult,
+ ),
+ )
+ if err != nil {
+ return err
+ }
+ defer cursor.Close()
+
+ enricher, err := api.NewDataConverter(api.DataConverterArgs{
+ RawDataSubTaskArgs: api.RawDataSubTaskArgs{
+ Ctx: taskCtx,
+ Params: DoraApiParams{
+ ProjectName: data.Options.ProjectName,
+ },
+ Table: devops.CICDPipeline{}.TableName(),
+ },
+ InputRowType: reflect.TypeOf(pipelineEx{}),
+ Input: cursor,
+ Convert: func(inputRow interface{}) ([]interface{},
errors.Error) {
+ pipelineExInfo := inputRow.(*pipelineEx)
+ domainDeployment := &devops.CICDDeployment{
+ DomainEntity: domainlayer.DomainEntity{
+ Id: pipelineExInfo.Id,
+ },
+ CicdScopeId: pipelineExInfo.CicdScopeId,
+ Name: pipelineExInfo.Name,
+ Result: pipelineExInfo.Result,
+ Status: pipelineExInfo.Status,
+ Environment: pipelineExInfo.Environment,
+ CreatedDate: pipelineExInfo.CreatedDate,
+ FinishedDate: pipelineExInfo.FinishedDate,
+ DurationSec: &pipelineExInfo.DurationSec,
+ }
+ if pipelineExInfo.FinishedDate != nil &&
pipelineExInfo.DurationSec != 0 {
+ s :=
pipelineExInfo.FinishedDate.Add(-time.Duration(pipelineExInfo.DurationSec) *
time.Second)
+ domainDeployment.StartedDate = &s
+ }
+ if pipelineExInfo.Environment == "" {
+ if pipelineExInfo.HasProductionTasks {
+ domainDeployment.Environment =
devops.PRODUCTION
+ } else if pipelineExInfo.HasStagingTasks {
+ domainDeployment.Environment =
devops.STAGING
+ } else if pipelineExInfo.HasTestingTasks {
+ domainDeployment.Environment =
devops.TESTING
+ }
+ }
+ return []interface{}{domainDeployment}, nil
+ },
+ })
+ if err != nil {
+ return err
+ }
+
+ return enricher.Execute()
+}
diff --git a/backend/plugins/github/tasks/deployment_convertor.go
b/backend/plugins/github/tasks/deployment_convertor.go
index fa49b913a..4904ca774 100644
--- a/backend/plugins/github/tasks/deployment_convertor.go
+++ b/backend/plugins/github/tasks/deployment_convertor.go
@@ -48,6 +48,8 @@ var ConvertDeploymentsMeta = plugin.SubTaskMeta{
ProductTables: []string{devops.CicdDeploymentCommit{}.TableName(),
devops.CICDDeployment{}.TableName()},
}
+// ConvertDeployment should be split into two task theoretically
+// But in GitHub, all deployments have commits, so there is no need to change
it.
func ConvertDeployment(taskCtx plugin.SubTaskContext) errors.Error {
db := taskCtx.GetDal()
rawDataSubTaskArgs, data := CreateRawDataSubTaskArgs(taskCtx,
RAW_DEPLOYMENT_TABLE)
diff --git a/backend/plugins/gitlab/tasks/deployment_convertor.go
b/backend/plugins/gitlab/tasks/deployment_convertor.go
index bf3cfdc40..629e1385b 100644
--- a/backend/plugins/gitlab/tasks/deployment_convertor.go
+++ b/backend/plugins/gitlab/tasks/deployment_convertor.go
@@ -47,6 +47,8 @@ var ConvertDeploymentMeta = plugin.SubTaskMeta{
Dependencies: []*plugin.SubTaskMeta{&ExtractDeploymentMeta},
}
+// ConvertDeployment should be split into two task theoretically
+// But in GitLab, all deployments have commits, so there is no need to change
it.
func ConvertDeployment(taskCtx plugin.SubTaskContext) errors.Error {
rawDataSubTaskArgs, data := CreateRawDataSubTaskArgs(taskCtx,
RAW_DEPLOYMENT)
db := taskCtx.GetDal()
@@ -69,7 +71,6 @@ func ConvertDeployment(taskCtx plugin.SubTaskContext)
errors.Error {
defer cursor.Close()
idGen := didgen.NewDomainIdGenerator(&models.GitlabDeployment{})
- //pipelineIdGen :=
didgen.NewDomainIdGenerator(&models.BitbucketPipeline{})
converter, err := api.NewDataConverter(api.DataConverterArgs{
InputRowType: reflect.TypeOf(models.GitlabDeployment{}),