Skip to content

Commit

Permalink
fix(api): when you update repomanager on an application, update workf…
Browse files Browse the repository at this point in the history
…low root linked (#2976)
  • Loading branch information
bnjjj authored and fsamin committed Jul 2, 2018
1 parent b996393 commit ed99a4a
Show file tree
Hide file tree
Showing 6 changed files with 165 additions and 17 deletions.
1 change: 1 addition & 0 deletions engine/api/application.go
Original file line number Diff line number Diff line change
Expand Up @@ -342,6 +342,7 @@ func (api *API) getApplicationHandler() Handler {
}
}

// loadApplicationUsage return usage of application
func loadApplicationUsage(db gorp.SqlExecutor, projKey, appName string) (sdk.Usage, error) {
usage := sdk.Usage{}

Expand Down
126 changes: 114 additions & 12 deletions engine/api/repositories_manager.go
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,7 @@ import (
"github.com/ovh/cds/engine/api/project"
"github.com/ovh/cds/engine/api/repositoriesmanager"
"github.com/ovh/cds/engine/api/user"
"github.com/ovh/cds/engine/api/workflow"
"github.com/ovh/cds/engine/api/workflowv0"
"github.com/ovh/cds/sdk"
"github.com/ovh/cds/sdk/log"
Expand Down Expand Up @@ -377,20 +378,22 @@ func (api *API) attachRepositoriesManagerHandler() Handler {
appName := vars["permApplicationName"]
rmName := vars["name"]
fullname := r.FormValue("fullname")
db := api.mustDB()
u := getUser(ctx)

app, err := application.LoadByName(api.mustDB(), api.Cache, projectKey, appName, getUser(ctx))
app, err := application.LoadByName(db, api.Cache, projectKey, appName, u)
if err != nil {
return sdk.WrapError(err, "attachRepositoriesManager> Cannot load application %s", appName)
}

//Load the repositoriesManager for the project
rm, err := repositoriesmanager.LoadForProject(api.mustDB(), projectKey, rmName)
rm, err := repositoriesmanager.LoadForProject(db, projectKey, rmName)
if err != nil {
return sdk.WrapError(sdk.ErrNoReposManager, "attachRepositoriesManager> error loading %s-%s: %s", projectKey, rmName, err)
}

//Get an authorized Client
client, err := repositoriesmanager.AuthorizedClient(api.mustDB(), api.Cache, rm)
client, err := repositoriesmanager.AuthorizedClient(db, api.Cache, rm)
if err != nil {
return sdk.WrapError(sdk.ErrNoReposManagerClientAuth, "attachRepositoriesManager> Cannot get client got %s %s : %s", projectKey, rmName, err)
}
Expand All @@ -402,7 +405,7 @@ func (api *API) attachRepositoriesManagerHandler() Handler {
app.VCSServer = rm.Name
app.RepositoryFullname = fullname

tx, errT := api.mustDB().Begin()
tx, errT := db.Begin()
if errT != nil {
return sdk.WrapError(errT, "attachRepositoriesManager> Cannot start transaction")
}
Expand All @@ -412,15 +415,60 @@ func (api *API) attachRepositoriesManagerHandler() Handler {
return sdk.WrapError(err, "attachRepositoriesManager> Cannot insert for application")
}

if err := application.UpdateLastModified(tx, api.Cache, app, getUser(ctx)); err != nil {
if err := application.UpdateLastModified(tx, api.Cache, app, u); err != nil {
return sdk.WrapError(err, "attachRepositoriesManager> Cannot update application last modified date")
}

if err := tx.Commit(); err != nil {
return sdk.WrapError(err, "attachRepositoriesManager> Cannot commit transaction")
}

event.PublishApplicationRepositoryAdd(projectKey, *app, getUser(ctx))
usage, errU := loadApplicationUsage(db, projectKey, appName)
if errU != nil {
return sdk.WrapError(errU, "attachRepositoriesManager> Cannot load application usage")
}

// Update default payload of linked workflow root
if len(usage.Workflows) > 0 {
proj, errP := project.Load(db, api.Cache, projectKey, u)
if errP != nil {
return sdk.WrapError(errP, "attachRepositoriesManager> Cannot load project")
}

for _, wf := range usage.Workflows {
rootCtx, errNc := workflow.LoadNodeContext(db, api.Cache, proj, wf.RootID, u, workflow.LoadOptions{})
if errNc != nil {
return sdk.WrapError(errNc, "attachRepositoriesManager> Cannot DefaultPayloadToMap")
}

if rootCtx.ApplicationID != app.ID {
continue
}

wf.Root = &sdk.WorkflowNode{
Context: rootCtx,
}
payload, errD := rootCtx.DefaultPayloadToMap()
if errD != nil {
return sdk.WrapError(errP, "attachRepositoriesManager> Cannot DefaultPayloadToMap")
}

if _, ok := payload["git.branch"]; ok && payload["git.repository"] == app.RepositoryFullname {
continue
}

defaultPayload, errPay := workflow.DefaultPayload(db, api.Cache, proj, u, &wf)
if errPay != nil {
return sdk.WrapError(errPay, "attachRepositoriesManager> Cannot get defaultPayload")
}
wf.Root.Context.DefaultPayload = defaultPayload
if err := workflow.UpdateNodeContext(db, wf.Root.Context); err != nil {
return sdk.WrapError(err, "attachRepositoriesManager> Cannot update node context %d", wf.Root.Context.ID)
}
}
}

event.PublishApplicationRepositoryAdd(projectKey, *app, u)

return WriteJSON(w, app, http.StatusOK)
}
Expand All @@ -432,26 +480,28 @@ func (api *API) detachRepositoriesManagerHandler() Handler {
projectKey := vars["key"]
appName := vars["permApplicationName"]
rmName := vars["name"]
db := api.mustDB()
u := getUser(ctx)

app, errl := application.LoadByName(api.mustDB(), api.Cache, projectKey, appName, getUser(ctx), application.LoadOptions.WithHooks)
app, errl := application.LoadByName(db, api.Cache, projectKey, appName, u, application.LoadOptions.WithHooks)
if errl != nil {
return sdk.WrapError(errl, "detachRepositoriesManager> error on load project %s", projectKey)
}

//Load the repositoriesManager for the project
rm, err := repositoriesmanager.LoadForProject(api.mustDB(), projectKey, rmName)
rm, err := repositoriesmanager.LoadForProject(db, projectKey, rmName)
if err != nil {
return sdk.WrapError(sdk.ErrNoReposManager, "attachRepositoriesManager> error loading %s-%s: %s", projectKey, rmName, err)
}

//Get an authorized Client
client, err := repositoriesmanager.AuthorizedClient(api.mustDB(), api.Cache, rm)
client, err := repositoriesmanager.AuthorizedClient(db, api.Cache, rm)
if err != nil {
return sdk.WrapError(sdk.ErrNoReposManagerClientAuth, "attachRepositoriesManager> Cannot get client got %s %s : %s", projectKey, rmName, err)
}

//Remove all the things in a transaction
tx, errT := api.mustDB().Begin()
tx, errT := db.Begin()
if errT != nil {
return sdk.WrapError(errT, "detachRepositoriesManager> Cannot start transaction")
}
Expand All @@ -461,6 +511,7 @@ func (api *API) detachRepositoriesManagerHandler() Handler {
return sdk.WrapError(err, "detachRepositoriesManager> Cannot delete for application")
}

//TODO: to delete after DEPRECATED workflows are deleted
for _, h := range app.Hooks {
s := api.Config.URL.API + hook.HookLink
link := fmt.Sprintf(s, h.UID, h.Project, h.Repository)
Expand All @@ -487,15 +538,66 @@ func (api *API) detachRepositoriesManagerHandler() Handler {
return sdk.WrapError(err, "detachRepositoriesManager> error on poller.DeleteAll")
}

if err := application.UpdateLastModified(tx, api.Cache, app, getUser(ctx)); err != nil {
if err := application.UpdateLastModified(tx, api.Cache, app, u); err != nil {
return sdk.WrapError(err, "detachRepositoriesManager> Cannot update application last modified date")
}

if err := tx.Commit(); err != nil {
return sdk.WrapError(err, "detachRepositoriesManager> Cannot commit transaction")
}

event.PublishApplicationRepositoryDelete(projectKey, appName, app.VCSServer, app.RepositoryFullname, getUser(ctx))
usage, errU := loadApplicationUsage(db, projectKey, appName)
if errU != nil {
return sdk.WrapError(errU, "detachRepositoriesManager> Cannot load application usage")
}

// Update default payload of linked workflow root
if len(usage.Workflows) > 0 {
proj, errP := project.Load(db, api.Cache, projectKey, u)
if errP != nil {
return sdk.WrapError(errP, "detachRepositoriesManager> Cannot load project")
}

hookToDelete := map[string]sdk.WorkflowNodeHook{}
for _, wf := range usage.Workflows {
nodeHooks, err := workflow.LoadHooksByNodeID(db, wf.RootID)
if err != nil {
return sdk.WrapError(err, "detachRepositoriesManager> Cannot load node hook by nodeID %d", wf.RootID)
}

for _, nodeHook := range nodeHooks {
if nodeHook.WorkflowHookModel.Name != sdk.RepositoryWebHookModelName && nodeHook.WorkflowHookModel.Name != sdk.GitPollerModelName {
continue
}
hookToDelete[nodeHook.UUID] = nodeHook
}
}

if len(hookToDelete) > 0 {
txDel, errTx := db.Begin()
if errTx != nil {
return sdk.WrapError(errTx, "detachRepositoriesManager> Cannot create delete transaction")
}
defer func() {
_ = txDel.Rollback()
}()

for _, nodeHook := range hookToDelete {
if err := workflow.DeleteHook(txDel, &nodeHook); err != nil {
return sdk.WrapError(err, "detachRepositoriesManager> Cannot delete hooks")
}
}
if err := workflow.DeleteHookConfiguration(txDel, api.Cache, proj, hookToDelete); err != nil {
return sdk.WrapError(err, "detachRepositoriesManager> Cannot delete hooks vcs configuration")
}

if err := txDel.Commit(); err != nil {
return sdk.WrapError(err, "detachRepositoriesManager> Cannot commit delete transaction")
}
}
}

event.PublishApplicationRepositoryDelete(projectKey, appName, app.VCSServer, app.RepositoryFullname, u)

return WriteJSON(w, app, http.StatusOK)
}
Expand Down
2 changes: 1 addition & 1 deletion engine/api/workflow/dao.go
Original file line number Diff line number Diff line change
Expand Up @@ -653,7 +653,7 @@ func Delete(db gorp.SqlExecutor, store cache.Store, p *sdk.Project, w *sdk.Workf

hooks := w.GetHooks()
// Delete all hooks
if err := deleteHookConfiguration(db, store, p, hooks); err != nil {
if err := DeleteHookConfiguration(db, store, p, hooks); err != nil {
return sdk.WrapError(err, "Delete> Unable to delete hooks from workflow")
}

Expand Down
35 changes: 35 additions & 0 deletions engine/api/workflow/dao_hook.go
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,15 @@ func UpdateHook(db gorp.SqlExecutor, h *sdk.WorkflowNodeHook) error {
return nil
}

// DeleteHook Delete a workflow node hook
func DeleteHook(db gorp.SqlExecutor, h *sdk.WorkflowNodeHook) error {
dbhook := NodeHook(*h)
if _, err := db.Delete(&dbhook); err != nil {
return sdk.WrapError(err, "updateHook> Cannot update hook")
}
return nil
}

// insertHook inserts a hook
func insertHook(db gorp.SqlExecutor, node *sdk.WorkflowNode, hook *sdk.WorkflowNodeHook) error {
hook.WorkflowNodeID = node.ID
Expand Down Expand Up @@ -193,3 +202,29 @@ func LoadHookByUUID(db gorp.SqlExecutor, uuid string) (*sdk.WorkflowNodeHook, er

return &wNodeHook, nil
}

// LoadHooksByNodeID loads hooks linked to a nodeID
func LoadHooksByNodeID(db gorp.SqlExecutor, nodeID int64) ([]sdk.WorkflowNodeHook, error) {
query := `
SELECT id, uuid, ref, workflow_hook_model_id, workflow_node_id
FROM workflow_node_hook
WHERE workflow_node_id = $1`

res := []NodeHook{}
if _, err := db.Select(&res, query, nodeID); err != nil {
if err == sql.ErrNoRows {
return nil, nil
}
return nil, sdk.WrapError(err, "LoadHookByNodeID>")
}

nodeHooks := make([]sdk.WorkflowNodeHook, len(res))
for i, nh := range res {
if err := nh.PostGet(db); err != nil {
return nil, sdk.WrapError(err, "LoadHookByNodeID> cannot load postget")
}
nodeHooks[i] = sdk.WorkflowNodeHook(nh)
}

return nodeHooks, nil
}
5 changes: 3 additions & 2 deletions engine/api/workflow/hook.go
Original file line number Diff line number Diff line change
Expand Up @@ -155,14 +155,15 @@ func HookRegistration(db gorp.SqlExecutor, store cache.Store, oldW *sdk.Workflow
}

if len(hookToDelete) > 0 {
if err := deleteHookConfiguration(db, store, p, hookToDelete); err != nil {
if err := DeleteHookConfiguration(db, store, p, hookToDelete); err != nil {
return sdk.WrapError(err, "HookRegistration> Cannot remove hook configuration")
}
}
return nil
}

func deleteHookConfiguration(db gorp.SqlExecutor, store cache.Store, p *sdk.Project, hookToDelete map[string]sdk.WorkflowNodeHook) error {
// DeleteHookConfiguration delete hooks configuration (and their vcs configuration)
func DeleteHookConfiguration(db gorp.SqlExecutor, store cache.Store, p *sdk.Project, hookToDelete map[string]sdk.WorkflowNodeHook) error {
// Delete from vcs configuration if needed
for _, h := range hookToDelete {
if h.WorkflowHookModel.Name == sdk.RepositoryWebHookModelName {
Expand Down
13 changes: 11 additions & 2 deletions ui/src/app/service/application/application.store.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,3 @@

import {BehaviorSubject, Observable, of as observableOf} from 'rxjs';

import {Injectable} from '@angular/core';
Expand All @@ -15,6 +14,7 @@ import {Scheduler} from '../../model/scheduler.model';
import {Trigger} from '../../model/trigger.model';
import {Variable} from '../../model/variable.model';
import {ProjectStore} from '../project/project.store';
import {WorkflowStore} from '../workflow/workflow.store';
import {ApplicationService} from './application.service';

import {Key} from '../../model/keys.model';
Expand All @@ -33,7 +33,10 @@ export class ApplicationStore {
new BehaviorSubject(immutable.List<NavbarRecentData>());


constructor(private _applicationService: ApplicationService, private _projectStore: ProjectStore) {
constructor(
private _applicationService: ApplicationService,
private _projectStore: ProjectStore,
private _workflowStore: WorkflowStore) {
this.loadRecentApplication();

}
Expand Down Expand Up @@ -227,6 +230,9 @@ export class ApplicationStore {
appToUpdate.vcs_server = app.vcs_server;
appToUpdate.repository_fullname = app.repository_fullname;
this._application.next(cache.set(appKey, appToUpdate));
if (appToUpdate.usage && Array.isArray(appToUpdate.usage.workflows)) {
appToUpdate.usage.workflows.forEach((wf) => this._workflowStore.removeFromStore(key + '-' + wf.name));
}
}
return app;
}));
Expand All @@ -248,6 +254,9 @@ export class ApplicationStore {
delete pToUpdate.vcs_server;
delete pToUpdate.repository_fullname;
this._application.next(cache.set(appKey, pToUpdate));
if (pToUpdate.usage && Array.isArray(pToUpdate.usage.workflows)) {
pToUpdate.usage.workflows.forEach((wf) => this._workflowStore.removeFromStore(key + '-' + wf.name));
}
}
return app;
}));
Expand Down

0 comments on commit ed99a4a

Please sign in to comment.