Skip to content

Commit f483fe5

Browse files
authored
fix(api): clean duplicate wnode migration (#5057)
* fix(api): clean duplicate wnode migration Signed-off-by: Yvonnick Esnault <[email protected]>
1 parent fef0f25 commit f483fe5

File tree

6 files changed

+166
-35
lines changed

6 files changed

+166
-35
lines changed

engine/api/api.go

+4
Original file line numberDiff line numberDiff line change
@@ -699,6 +699,10 @@ func (a *API) Serve(ctx context.Context) error {
699699
return migrate.RefactorEnvironmentVariables(ctx, a.DBConnectionFactory.GetDBMap())
700700
}})
701701

702+
migrate.Add(ctx, sdk.Migration{Name: "CleanDuplicateNodes", Release: "0.44.0", Blocker: false, Automatic: true, ExecFunc: func(ctx context.Context) error {
703+
return migrate.CleanDuplicateNodes(ctx, a.DBConnectionFactory.GetDBMap())
704+
}})
705+
702706
isFreshInstall, errF := version.IsFreshInstall(a.mustDB())
703707
if errF != nil {
704708
return sdk.WrapError(errF, "Unable to check if it's a fresh installation of CDS")

engine/api/migrate/clean_w_nodes.go

+158
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,158 @@
1+
package migrate
2+
3+
import (
4+
"context"
5+
"database/sql"
6+
7+
"github.com/ovh/cds/sdk"
8+
"github.com/ovh/cds/sdk/log"
9+
10+
"github.com/go-gorp/gorp"
11+
)
12+
13+
// CleanDuplicateNodes .
14+
func CleanDuplicateNodes(ctx context.Context, db *gorp.DbMap) error {
15+
if err := cleanDuplicateNodesWNodeTrigger(ctx, db); err != nil {
16+
return sdk.WithStack(err)
17+
}
18+
if err := cleanDuplicateNodesWNode(ctx, db); err != nil {
19+
return sdk.WithStack(err)
20+
}
21+
return nil
22+
}
23+
24+
func cleanDuplicateNodesWNodeTrigger(ctx context.Context, db *gorp.DbMap) error {
25+
query := `WITH workflowInfo AS (
26+
SELECT id, name, CAST(workflow_data->'node'->>'id' AS BIGINT) as rootNodeID
27+
FROM workflow
28+
),
29+
oldNode as (
30+
SELECT w_node.id as nodeID, w_node.name as nodeName, workflowInfo.id as wID, workflowInfo.name as WName
31+
FROM w_node
32+
JOIN workflowInfo ON workflowInfo.id = w_node.workflow_id
33+
WHERE w_node.id < workflowInfo.rootNodeID
34+
)
35+
SELECT id FROM w_node_trigger where child_node_id IN (SELECT nodeID FROM oldNode);`
36+
rows, err := db.Query(query)
37+
if err == sql.ErrNoRows {
38+
return nil
39+
}
40+
if err != nil {
41+
return sdk.WithStack(err)
42+
}
43+
44+
var idsWNodeTrigger []int64
45+
for rows.Next() {
46+
var id int64
47+
if err := rows.Scan(&id); err != nil {
48+
rows.Close() // nolint
49+
return sdk.WithStack(err)
50+
}
51+
idsWNodeTrigger = append(idsWNodeTrigger, id)
52+
}
53+
54+
if err := rows.Close(); err != nil {
55+
return sdk.WithStack(err)
56+
}
57+
58+
var mError = new(sdk.MultiError)
59+
for _, idWNodeTrigger := range idsWNodeTrigger {
60+
if err := deleteFromWNodeTrigger(ctx, db, idWNodeTrigger); err != nil {
61+
mError.Append(err)
62+
log.Error(ctx, "migrate.cleanDuplicateNodesWNodeTrigger> unable to delete from wNodeTrigger %d: %v", idWNodeTrigger, err)
63+
}
64+
}
65+
66+
if mError.IsEmpty() {
67+
return nil
68+
}
69+
return mError
70+
}
71+
72+
func deleteFromWNodeTrigger(ctx context.Context, db *gorp.DbMap, idWNodeTrigger int64) error {
73+
tx, err := db.Begin()
74+
if err != nil {
75+
return sdk.WithStack(err)
76+
}
77+
78+
defer tx.Rollback() // nolint
79+
80+
query := "DELETE FROM w_node_trigger where id = $1"
81+
if _, err := db.Exec(query, idWNodeTrigger); err != nil {
82+
log.Error(ctx, "migrate.deleteFromWNodeTrigger> unable to delete w_node %d: %v", idWNodeTrigger, err)
83+
}
84+
85+
if err := tx.Commit(); err != nil {
86+
return err
87+
}
88+
89+
return nil
90+
}
91+
92+
func cleanDuplicateNodesWNode(ctx context.Context, db *gorp.DbMap) error {
93+
query := `WITH workflowInfo AS (
94+
SELECT id, name, CAST(workflow_data->'node'->>'id' AS BIGINT) as rootNodeID
95+
FROM workflow
96+
),
97+
oldNode as (
98+
SELECT w_node.id as nodeID, w_node.name as nodeName, workflowInfo.id as wID, workflowInfo.name as WName
99+
FROM w_node
100+
JOIN workflowInfo ON workflowInfo.id = w_node.workflow_id
101+
WHERE w_node.id < workflowInfo.rootNodeID
102+
)
103+
SELECT id FROM w_node where id IN (SELECT nodeID FROM oldNode);`
104+
rows, err := db.Query(query)
105+
if err == sql.ErrNoRows {
106+
return nil
107+
}
108+
if err != nil {
109+
return sdk.WithStack(err)
110+
}
111+
112+
var idsWNode []int64
113+
for rows.Next() {
114+
var id int64
115+
if err := rows.Scan(&id); err != nil {
116+
rows.Close() // nolint
117+
return sdk.WithStack(err)
118+
}
119+
idsWNode = append(idsWNode, id)
120+
}
121+
122+
if err := rows.Close(); err != nil {
123+
return sdk.WithStack(err)
124+
}
125+
126+
var mError = new(sdk.MultiError)
127+
for _, idWNode := range idsWNode {
128+
if err := deleteFromWNode(ctx, db, idWNode); err != nil {
129+
mError.Append(err)
130+
log.Error(ctx, "migrate.cleanDuplicateNodesWNode> unable to delete from WNode %d: %v", idWNode, err)
131+
}
132+
}
133+
134+
if mError.IsEmpty() {
135+
return nil
136+
}
137+
return mError
138+
}
139+
140+
func deleteFromWNode(ctx context.Context, db *gorp.DbMap, idWNode int64) error {
141+
tx, err := db.Begin()
142+
if err != nil {
143+
return sdk.WithStack(err)
144+
}
145+
146+
defer tx.Rollback() // nolint
147+
148+
query := "DELETE FROM w_node where id = $1"
149+
if _, err := db.Exec(query, idWNode); err != nil {
150+
log.Error(ctx, "migrate.deleteFromWNode> unable to delete w_node %d: %v", idWNode, err)
151+
}
152+
153+
if err := tx.Commit(); err != nil {
154+
return err
155+
}
156+
157+
return nil
158+
}

engine/sql/193_clean_workflow_data_node.sql

-32
This file was deleted.

ui/src/app/shared/workflow/menu/edit-node/menu.edit.node.component.ts

+1-1
Original file line numberDiff line numberDiff line change
@@ -104,7 +104,7 @@ export class WorkflowWNodeMenuEditComponent implements OnInit {
104104
return true;
105105
}
106106

107-
if (this.workflowrun && this.workflowrun.workflow && this.workflowrun.workflow.workflow_data) {
107+
if (this.workflowrun && this.workflowrun.workflow && this.workflowrun.workflow.workflow_data.node.id > 0) {
108108
let nbNodeFound = 0;
109109
let parentNodes = Workflow.getParentNodeIds(this.workflowrun, this.node.id);
110110
for (let parentNodeId of parentNodes) {

ui/src/app/views/workflow/graph/workflow.graph.component.ts

+1-1
Original file line numberDiff line numberDiff line change
@@ -89,7 +89,7 @@ export class WorkflowGraphComponent implements AfterViewInit {
8989
// https://github.com/cpettitt/dagre/wiki#configuring-the-layout
9090
this.g = new dagreD3.graphlib.Graph().setGraph({ rankdir: this.direction, nodesep: 10, ranksep: 15, edgesep: 5 });
9191
// Create all nodes
92-
if (this.workflow.workflow_data && this.workflow.workflow_data.node) {
92+
if (this.workflow.workflow_data && this.workflow.workflow_data.node && this.workflow.workflow_data.node.id > 0) {
9393
this.createNode(this.workflow.workflow_data.node);
9494
}
9595
if (this.workflow.workflow_data && this.workflow.workflow_data.joins) {

ui/src/app/views/workflow/run/workflow.run.component.ts

+2-1
Original file line numberDiff line numberDiff line change
@@ -101,7 +101,8 @@ export class WorkflowRunComponent implements OnInit {
101101
if (!this.workflowRunData) {
102102
this.workflowRunData = {};
103103
}
104-
if (!this.workflowRunData['workflow'] || !this.workflowRunData['workflow'].workflow_data) {
104+
if (!this.workflowRunData['workflow'] || !this.workflowRunData['workflow'].workflow_data
105+
|| this.workflowRunData['workflow'].workflow_data.node.id === 0) {
105106
this.workflowRunData['workflow'] = s.workflowRun.workflow;
106107
this.workflowName = s.workflowRun.workflow.name;
107108
}

0 commit comments

Comments
 (0)