Skip to content

Commit

Permalink
feat(api): manually delete 1 build from history #3782
Browse files Browse the repository at this point in the history
Signed-off-by: Benjamin Coenen <benjamin.coenen@corp.ovh.com>
  • Loading branch information
bnjjj committed Jun 26, 2019
1 parent 3c5e22e commit 74d0d8d
Show file tree
Hide file tree
Showing 9 changed files with 148 additions and 219 deletions.
2 changes: 1 addition & 1 deletion engine/api/api_routes.go
Original file line number Diff line number Diff line change
Expand Up @@ -232,7 +232,7 @@ func (api *API) InitRouter() {
r.Handle("/project/{key}/workflows/{permWorkflowName}/runs/latest", r.GET(api.getLatestWorkflowRunHandler))
r.Handle("/project/{key}/workflows/{permWorkflowName}/runs/tags", r.GET(api.getWorkflowRunTagsHandler))
r.Handle("/project/{key}/workflows/{permWorkflowName}/runs/num", r.GET(api.getWorkflowRunNumHandler), r.POST(api.postWorkflowRunNumHandler))
r.Handle("/project/{key}/workflows/{permWorkflowName}/runs/{number}", r.GET(api.getWorkflowRunHandler, AllowServices(true), EnableTracing()))
r.Handle("/project/{key}/workflows/{permWorkflowName}/runs/{number}", r.GET(api.getWorkflowRunHandler, AllowServices(true), EnableTracing()), r.DELETE(api.deleteWorkflowRunHandler))
r.Handle("/project/{key}/workflows/{permWorkflowName}/runs/{number}/stop", r.POSTEXECUTE(api.stopWorkflowRunHandler, EnableTracing()))
r.Handle("/project/{key}/workflows/{permWorkflowName}/runs/{number}/vcs/resync", r.POSTEXECUTE(api.postResyncVCSWorkflowRunHandler))
r.Handle("/project/{key}/workflows/{permWorkflowName}/runs/{number}/resync", r.POST(api.resyncWorkflowRunHandler))
Expand Down
13 changes: 10 additions & 3 deletions engine/api/workflow/dao_run.go
Original file line number Diff line number Diff line change
Expand Up @@ -41,6 +41,7 @@ type LoadRunOptions struct {
WithTests bool
WithLightTests bool
WithVulnerabilities bool
WithoutDeleted bool
DisableDetailledNodeRun bool
Language string
}
Expand Down Expand Up @@ -320,7 +321,7 @@ func LoadRuns(db gorp.SqlExecutor, projectkey, workflowname string, offset, limi
from workflow_run
join project on workflow_run.project_id = project.id
join workflow on workflow_run.workflow_id = workflow.id
where project.projectkey = $1`
where project.projectkey = $1 AND workflow_run.to_delete = false`

if workflowname != "" {
args = []interface{}{projectkey, workflowname}
Expand All @@ -329,7 +330,8 @@ func LoadRuns(db gorp.SqlExecutor, projectkey, workflowname string, offset, limi
join project on workflow_run.project_id = project.id
join workflow on workflow_run.workflow_id = workflow.id
where project.projectkey = $1
and workflow.name = $2`
and workflow.name = $2
AND workflow_run.to_delete = false`
}

count, errc := db.SelectInt(queryCount, args...)
Expand All @@ -345,7 +347,7 @@ func LoadRuns(db gorp.SqlExecutor, projectkey, workflowname string, offset, limi
from workflow_run
join project on workflow_run.project_id = project.id
join workflow on workflow_run.workflow_id = workflow.id
where project.projectkey = $1
where project.projectkey = $1 AND workflow_run.to_delete = false
order by workflow_run.start desc
limit $2 offset $3`, wfRunfields)

Expand All @@ -357,6 +359,7 @@ func LoadRuns(db gorp.SqlExecutor, projectkey, workflowname string, offset, limi
join workflow on workflow_run.workflow_id = workflow.id
where project.projectkey = $1
and workflow.name = $2
AND workflow_run.to_delete = false
order by workflow_run.start desc
limit $3 offset $4`, wfRunfields)
}
Expand All @@ -378,6 +381,7 @@ func LoadRuns(db gorp.SqlExecutor, projectkey, workflowname string, offset, limi
) as tags on workflow_run.id = tags.workflow_run_id
where project.projectkey = $1
and workflow.name = $2
AND workflow_run.to_delete = false
and string_to_array($5, ',') <@ string_to_array(tags.tags, ',')
order by workflow_run.start desc
limit $3 offset $4`, wfRunfields)
Expand Down Expand Up @@ -469,6 +473,9 @@ func loadRun(db gorp.SqlExecutor, loadOpts LoadRunOptions, query string, args ..
return nil, sdk.WrapError(err, "Unable to load workflow run. query:%s args:%v", query, args)
}
wr := sdk.WorkflowRun(*runDB)
if loadOpts.WithoutDeleted && wr.ToDelete {
return nil, sdk.ErrWorkflowNotFound
}

tags, errT := loadTagsByRunID(db, wr.ID)
if errT != nil {
Expand Down
45 changes: 37 additions & 8 deletions engine/api/workflow_run.go
Original file line number Diff line number Diff line change
Expand Up @@ -311,6 +311,7 @@ func (api *API) getWorkflowRunHandler() service.Handler {
// as hook service. It's needed to have the buildParameters.
run, err := workflow.LoadRun(ctx, api.mustDB(), key, name, number,
workflow.LoadRunOptions{
WithoutDeleted: true,
WithArtifacts: true,
WithLightTests: true,
DisableDetailledNodeRun: getService(ctx) == nil,
Expand Down Expand Up @@ -341,6 +342,33 @@ func (api *API) getWorkflowRunHandler() service.Handler {
}
}

func (api *API) deleteWorkflowRunHandler() service.Handler {
return func(ctx context.Context, w http.ResponseWriter, r *http.Request) error {
vars := mux.Vars(r)
key := vars["key"]
name := vars["permWorkflowName"]
number, err := requestVarInt(r, "number")
if err != nil {
return err
}

run, err := workflow.LoadRun(ctx, api.mustDB(), key, name, number,
workflow.LoadRunOptions{
DisableDetailledNodeRun: true,
},
)
if err != nil {
return sdk.WrapError(err, "Unable to load workflow %s run number %d", name, number)
}

if err := workflow.MarkWorkflowRunsAsDelete(api.mustDB(), []int64{run.ID}); err != nil {
return sdk.WrapError(err, "cannot mark workflow run %d as delete", run.ID)
}

return service.WriteJSON(w, nil, http.StatusAccepted)
}
}

func (api *API) stopWorkflowRunHandler() service.Handler {
return func(ctx context.Context, w http.ResponseWriter, r *http.Request) error {
vars := mux.Vars(r)
Expand All @@ -351,7 +379,7 @@ func (api *API) stopWorkflowRunHandler() service.Handler {
return err
}

run, errL := workflow.LoadRun(ctx, api.mustDB(), key, name, number, workflow.LoadRunOptions{})
run, errL := workflow.LoadRun(ctx, api.mustDB(), key, name, number, workflow.LoadRunOptions{WithoutDeleted: true})
if errL != nil {
return sdk.WrapError(errL, "stopWorkflowRunHandler> Unable to load last workflow run")
}
Expand Down Expand Up @@ -547,7 +575,7 @@ func (api *API) getWorkflowNodeRunHistoryHandler() service.Handler {
return err
}

run, errR := workflow.LoadRun(ctx, api.mustDB(), key, name, number, workflow.LoadRunOptions{DisableDetailledNodeRun: true})
run, errR := workflow.LoadRun(ctx, api.mustDB(), key, name, number, workflow.LoadRunOptions{DisableDetailledNodeRun: true, WithoutDeleted: true})
if errR != nil {
return sdk.WrapError(errR, "getWorkflowNodeRunHistoryHandler")
}
Expand Down Expand Up @@ -668,7 +696,7 @@ func (api *API) stopWorkflowNodeRunHandler() service.Handler {
}

// Load node run
nodeRun, err := workflow.LoadNodeRun(api.mustDB(), key, name, number, id, workflow.LoadRunOptions{})
nodeRun, err := workflow.LoadNodeRun(api.mustDB(), key, name, number, id, workflow.LoadRunOptions{WithoutDeleted: true})
if err != nil {
return sdk.WrapError(err, "Unable to load last workflow run")
}
Expand Down Expand Up @@ -701,7 +729,7 @@ func (api *API) stopWorkflowNodeRun(ctx context.Context, dbFunc func() *gorp.DbM
return nil, sdk.WrapError(errS, "stopWorkflowNodeRunHandler> Unable to stop workflow node run")
}

wr, errLw := workflow.LoadRun(ctx, tx, p.Key, workflowName, nodeRun.Number, workflow.LoadRunOptions{})
wr, errLw := workflow.LoadRun(ctx, tx, p.Key, workflowName, nodeRun.Number, workflow.LoadRunOptions{WithoutDeleted: true})
if errLw != nil {
return nil, sdk.WrapError(errLw, "stopWorkflowNodeRunHandler> Unable to load workflow run %s", workflowName)
}
Expand All @@ -726,7 +754,7 @@ func (api *API) stopWorkflowNodeRun(ctx context.Context, dbFunc func() *gorp.DbM
}

go func(ID int64) {
wRun, errLw := workflow.LoadRunByID(api.mustDB(), ID, workflow.LoadRunOptions{DisableDetailledNodeRun: true})
wRun, errLw := workflow.LoadRunByID(api.mustDB(), ID, workflow.LoadRunOptions{DisableDetailledNodeRun: true, WithoutDeleted: true})
if errLw != nil {
log.Error("workflow.stopWorkflowNodeRun> Cannot load run for resync commit status %v", errLw)
return
Expand Down Expand Up @@ -762,6 +790,7 @@ func (api *API) getWorkflowNodeRunHandler() service.Handler {
WithStaticFiles: true,
WithCoverage: true,
WithVulnerabilities: true,
WithoutDeleted: true,
})
if err != nil {
return sdk.WrapError(err, "Unable to load last workflow run")
Expand Down Expand Up @@ -1093,7 +1122,7 @@ func (api *API) getWorkflowRunArtifactsHandler() service.Handler {
return sdk.WrapError(errNu, "getWorkflowJobArtifactsHandler> Invalid node job run ID")
}

wr, errW := workflow.LoadRun(ctx, api.mustDB(), key, name, number, workflow.LoadRunOptions{WithArtifacts: true})
wr, errW := workflow.LoadRun(ctx, api.mustDB(), key, name, number, workflow.LoadRunOptions{WithArtifacts: true, WithoutDeleted: true})
if errW != nil {
return errW
}
Expand Down Expand Up @@ -1213,7 +1242,7 @@ func (api *API) getWorkflowNodeRunJobStepHandler() service.Handler {
}

// Check nodeRunID is link to workflow
nodeRun, errNR := workflow.LoadNodeRun(api.mustDB(), projectKey, workflowName, number, nodeRunID, workflow.LoadRunOptions{DisableDetailledNodeRun: true})
nodeRun, errNR := workflow.LoadNodeRun(api.mustDB(), projectKey, workflowName, number, nodeRunID, workflow.LoadRunOptions{DisableDetailledNodeRun: true, WithoutDeleted: true})
if errNR != nil {
return sdk.WrapError(errNR, "cannot find nodeRun %d/%d for workflow %s in project %s", nodeRunID, number, workflowName, projectKey)
}
Expand Down Expand Up @@ -1291,7 +1320,7 @@ func (api *API) postResyncVCSWorkflowRunHandler() service.Handler {
return sdk.WrapError(errP, "postResyncVCSWorkflowRunHandler> Cannot load project")
}

wfr, errW := workflow.LoadRun(ctx, db, key, name, number, workflow.LoadRunOptions{DisableDetailledNodeRun: true})
wfr, errW := workflow.LoadRun(ctx, db, key, name, number, workflow.LoadRunOptions{DisableDetailledNodeRun: true, WithoutDeleted: true})
if errW != nil {
return sdk.WrapError(errW, "postResyncVCSWorkflowRunHandler> Cannot load workflow run")
}
Expand Down
45 changes: 25 additions & 20 deletions ui/src/app/store/workflow.action.ts
Original file line number Diff line number Diff line change
@@ -1,13 +1,13 @@
import { GroupPermission } from 'app/model/group.model';
import { Label } from 'app/model/project.model';
import { WNode, WNodeHook, WNodeTrigger, Workflow, WorkflowNotification } from 'app/model/workflow.model';
import {WorkflowNodeRun, WorkflowRun} from 'app/model/workflow.run.model';
import { WorkflowNodeRun, WorkflowRun } from 'app/model/workflow.run.model';

// --------- MODAL -----

export class OpenEditModal {
static readonly type = '[Workflow] Open Edit Modal';
constructor(public payload: { node: WNode, hook: WNodeHook}) { }
constructor(public payload: { node: WNode, hook: WNodeHook }) { }
}

export class CloseEditModal {
Expand All @@ -17,49 +17,54 @@ export class CloseEditModal {

export class UpdateModal {
static readonly type = '[Workflow] UpdateModal';
constructor(public payload: { workflow: Workflow }) {}
constructor(public payload: { workflow: Workflow }) { }
}

// --------- Sidebar -----
export class SidebarRunsMode {
static readonly type = '[Workflow] Sidebar run mode';
constructor(public payload: {}) {}
constructor(public payload: {}) { }
}

// --------- Workflow Run ---
export class ChangeToRunView {
static readonly type = '[Workflow] Change to Run View';
constructor(public payload: {}) {}
static readonly type = '[Workflow] Change to Run View';
constructor(public payload: {}) { }
}

export class GetWorkflowRun {
static readonly type = '[Workflow] Get Workflow Run';
constructor(public payload: { projectKey: string, workflowName: string, num: number}) {}
static readonly type = '[Workflow] Get Workflow Run';
constructor(public payload: { projectKey: string, workflowName: string, num: number }) { }
}

export class GetWorkflowRuns {
static readonly type = '[Workflow] Get Workflow Runs';
constructor(public payload: { projectKey: string, workflowName: string, limit: string }) {}
static readonly type = '[Workflow] Get Workflow Runs';
constructor(public payload: { projectKey: string, workflowName: string, limit: string }) { }
}

export class DeleteWorkflowRun {
static readonly type = '[Workflow] Delete Workflow Run';
constructor(public payload: { projectKey: string, workflowName: string, num: number }) { }
}

export class CleanWorkflowRun {
static readonly type = '[Workflow] Clean Workflow Run';
constructor(public payload: {}) {}
static readonly type = '[Workflow] Clean Workflow Run';
constructor(public payload: {}) { }
}

export class GetWorkflowNodeRun {
static readonly type = '[Workflow] Get Workflow Node Run';
constructor(public payload: { projectKey: string, workflowName: string, num: number, nodeRunID: number }) {}
static readonly type = '[Workflow] Get Workflow Node Run';
constructor(public payload: { projectKey: string, workflowName: string, num: number, nodeRunID: number }) { }
}

export class SelectWorkflowNodeRun {
static readonly type = '[Workflow] Select Workflow Node Run';
constructor(public payload: { workflowNodeRun: WorkflowNodeRun, node: WNode }) {}
static readonly type = '[Workflow] Select Workflow Node Run';
constructor(public payload: { workflowNodeRun: WorkflowNodeRun, node: WNode }) { }
}

export class UpdateWorkflowRunList {
static readonly type = '[Workflow] Update Workflow Run List';
constructor(public payload: { workflowRun: WorkflowRun }) {}
static readonly type = '[Workflow] Update Workflow Run List';
constructor(public payload: { workflowRun: WorkflowRun }) { }
}

// --------- Workflow -----
Expand All @@ -75,8 +80,8 @@ export class ImportWorkflow {
}

export class GetWorkflow {
static readonly type = '[Workflow] Get Workflow';
constructor(public payload: { projectKey: string, workflowName: string }) {}
static readonly type = '[Workflow] Get Workflow';
constructor(public payload: { projectKey: string, workflowName: string }) { }
}

export class UpdateWorkflow {
Expand Down
16 changes: 16 additions & 0 deletions ui/src/app/store/workflow.state.ts
Original file line number Diff line number Diff line change
Expand Up @@ -762,6 +762,22 @@ export class WorkflowState {

}

@Action(actionWorkflow.DeleteWorkflowRun)
deleteWorkflowRun(ctx: StateContext<WorkflowStateModel>, action: actionWorkflow.DeleteWorkflowRun) {
return this._http.delete<null>(
`/project/${action.payload.projectKey}/workflows/${action.payload.workflowName}/runs/${action.payload.num}`
).pipe(tap(() => {
const state = ctx.getState();

if (state.listRuns) {
ctx.setState({
...state,
listRuns: state.listRuns.filter((run) => run.num !== action.payload.num),
});
}
}));
}

@Action(actionWorkflow.GetWorkflowRuns)
getWorkflowRuns(ctx: StateContext<WorkflowStateModel>, action: actionWorkflow.GetWorkflowRuns) {
const state = ctx.getState();
Expand Down
Original file line number Diff line number Diff line change
@@ -1,18 +1,20 @@
import {Component, EventEmitter, Input, Output} from '@angular/core';
import {TranslateService} from '@ngx-translate/core';
import {Store} from '@ngxs/store';
import { Component, EventEmitter, Input, Output } from '@angular/core';
import { Router } from '@angular/router';
import { TranslateService } from '@ngx-translate/core';
import { Store } from '@ngxs/store';
import * as AU from 'ansi_up';
import {PermissionValue} from 'app/model/permission.model';
import {PipelineStatus} from 'app/model/pipeline.model';
import {Project} from 'app/model/project.model';
import {Workflow} from 'app/model/workflow.model';
import {WorkflowRun} from 'app/model/workflow.run.model';
import {WorkflowRunService} from 'app/service/workflow/run/workflow.run.service';
import {AutoUnsubscribe} from 'app/shared/decorator/autoUnsubscribe';
import {ToastService} from 'app/shared/toast/ToastService';
import {WorkflowState, WorkflowStateModel} from 'app/store/workflow.state';
import {finalize} from 'rxjs/operators';
import {Subscription} from 'rxjs/Subscription';
import { PermissionValue } from 'app/model/permission.model';
import { PipelineStatus } from 'app/model/pipeline.model';
import { Project } from 'app/model/project.model';
import { Workflow } from 'app/model/workflow.model';
import { WorkflowRun } from 'app/model/workflow.run.model';
import { WorkflowRunService } from 'app/service/workflow/run/workflow.run.service';
import { AutoUnsubscribe } from 'app/shared/decorator/autoUnsubscribe';
import { ToastService } from 'app/shared/toast/ToastService';
import { DeleteWorkflowRun } from 'app/store/workflow.action';
import { WorkflowState, WorkflowStateModel } from 'app/store/workflow.state';
import { finalize } from 'rxjs/operators';
import { Subscription } from 'rxjs/Subscription';

@Component({
selector: 'app-workflow-run-summary',
Expand Down Expand Up @@ -42,14 +44,20 @@ export class WorkflowRunSummaryComponent {
_direction: string;
author: string;
loadingAction = false;
loadingDelete = false;
showInfos = false;
ansi_up = new AU.default;

pipelineStatusEnum = PipelineStatus;
permissionEnum = PermissionValue;

constructor(private _workflowRunService: WorkflowRunService,
private _toast: ToastService, private _translate: TranslateService, private _store: Store) {
constructor(
private _workflowRunService: WorkflowRunService,
private _toast: ToastService,
private _translate: TranslateService,
private _store: Store,
private router: Router
) {
this.subWR = this._store.select(WorkflowState.getCurrent()).subscribe((state: WorkflowStateModel) => {
this.workflow = state.workflow;
this.workflowRun = state.workflowRun;
Expand Down Expand Up @@ -94,4 +102,17 @@ export class WorkflowRunSummaryComponent {
.pipe(finalize(() => this.loadingAction = false))
.subscribe(() => this._toast.success('', this._translate.instant('workflow_vcs_resynced')));
}

delete() {
this.loadingDelete = true;
this._store.dispatch(new DeleteWorkflowRun({
projectKey: this.project.key,
workflowName: this.workflowName,
num: this.workflowRun.num
})).pipe(finalize(() => this.loadingDelete = false))
.subscribe(() => {
this._toast.success('', this._translate.instant('common_deleted'));
this.router.navigate(['/project', this.project.key, 'workflow', this.workflowName]);
});
}
}
Loading

0 comments on commit 74d0d8d

Please sign in to comment.