From e177728a823ce5974a101994c3f687a579ed8ca4 Mon Sep 17 00:00:00 2001
From: techknowlogick <techknowlogick@gitea.io>
Date: Sat, 24 Oct 2020 01:02:36 -0400
Subject: [PATCH] Store task errors following migrations and display them
 (#13246) (#13287)

* Store task errors following migrations and display them

When migrate tasks fail store the error in the task table
and ensure that they show on the status page.

Fix #13242

Signed-off-by: Andrew Thornton <art27@cantab.net>

* Update web_src/js/index.js

* Hide the failed first

Signed-off-by: Andrew Thornton <art27@cantab.net>

Co-authored-by: techknowlogick <techknowlogick@gitea.io>

Co-authored-by: zeripath <art27@cantab.net>
---
 models/task.go                        |  21 ++++++++++++++
 modules/task/migrate.go               |  39 +++++++++++++++-----------
 public/img/failed.png                 | Bin 0 -> 11009 bytes
 routers/repo/repo.go                  |  16 -----------
 routers/routes/routes.go              |   3 +-
 routers/user/task.go                  |  30 ++++++++++++++++++++
 templates/repo/migrate/migrating.tmpl |   8 +++++-
 web_src/js/index.js                   |  19 +++++++++----
 8 files changed, 95 insertions(+), 41 deletions(-)
 create mode 100644 public/img/failed.png
 create mode 100644 routers/user/task.go

diff --git a/models/task.go b/models/task.go
index 43cb2d4d9a..b86314b449 100644
--- a/models/task.go
+++ b/models/task.go
@@ -147,6 +147,27 @@ func GetMigratingTask(repoID int64) (*Task, error) {
 	return &task, nil
 }
 
+// GetMigratingTaskByID returns the migrating task by repo's id
+func GetMigratingTaskByID(id, doerID int64) (*Task, *migration.MigrateOptions, error) {
+	var task = Task{
+		ID:     id,
+		DoerID: doerID,
+		Type:   structs.TaskTypeMigrateRepo,
+	}
+	has, err := x.Get(&task)
+	if err != nil {
+		return nil, nil, err
+	} else if !has {
+		return nil, nil, ErrTaskDoesNotExist{id, 0, task.Type}
+	}
+
+	var opts migration.MigrateOptions
+	if err := json.Unmarshal([]byte(task.PayloadContent), &opts); err != nil {
+		return nil, nil, err
+	}
+	return &task, &opts, nil
+}
+
 // FindTaskOptions find all tasks
 type FindTaskOptions struct {
 	Status int
diff --git a/modules/task/migrate.go b/modules/task/migrate.go
index d25decaa00..99f0435b28 100644
--- a/modules/task/migrate.go
+++ b/modules/task/migrate.go
@@ -20,7 +20,7 @@ import (
 	"code.gitea.io/gitea/modules/util"
 )
 
-func handleCreateError(owner *models.User, err error, name string) error {
+func handleCreateError(owner *models.User, err error) error {
 	switch {
 	case models.IsErrReachLimitOfRepo(err):
 		return fmt.Errorf("You have already reached your limit of %d repositories", owner.MaxCreationLimit())
@@ -38,8 +38,8 @@ func handleCreateError(owner *models.User, err error, name string) error {
 func runMigrateTask(t *models.Task) (err error) {
 	defer func() {
 		if e := recover(); e != nil {
-			err = fmt.Errorf("PANIC whilst trying to do migrate task: %v\nStacktrace: %v", err, log.Stack(2))
-			log.Critical("PANIC during runMigrateTask[%d] by DoerID[%d] to RepoID[%d] for OwnerID[%d]: %v", t.ID, t.DoerID, t.RepoID, t.OwnerID, err)
+			err = fmt.Errorf("PANIC whilst trying to do migrate task: %v", e)
+			log.Critical("PANIC during runMigrateTask[%d] by DoerID[%d] to RepoID[%d] for OwnerID[%d]: %v\nStacktrace: %v", t.ID, t.DoerID, t.RepoID, t.OwnerID, e, log.Stack(2))
 		}
 
 		if err == nil {
@@ -55,7 +55,8 @@ func runMigrateTask(t *models.Task) (err error) {
 		t.EndTime = timeutil.TimeStampNow()
 		t.Status = structs.TaskStatusFailed
 		t.Errors = err.Error()
-		if err := t.UpdateCols("status", "errors", "end_time"); err != nil {
+		t.RepoID = 0
+		if err := t.UpdateCols("status", "errors", "repo_id", "end_time"); err != nil {
 			log.Error("Task UpdateCols failed: %v", err)
 		}
 
@@ -66,8 +67,8 @@ func runMigrateTask(t *models.Task) (err error) {
 		}
 	}()
 
-	if err := t.LoadRepo(); err != nil {
-		return err
+	if err = t.LoadRepo(); err != nil {
+		return
 	}
 
 	// if repository is ready, then just finsih the task
@@ -75,33 +76,35 @@ func runMigrateTask(t *models.Task) (err error) {
 		return nil
 	}
 
-	if err := t.LoadDoer(); err != nil {
-		return err
+	if err = t.LoadDoer(); err != nil {
+		return
 	}
-	if err := t.LoadOwner(); err != nil {
-		return err
+	if err = t.LoadOwner(); err != nil {
+		return
 	}
 	t.StartTime = timeutil.TimeStampNow()
 	t.Status = structs.TaskStatusRunning
-	if err := t.UpdateCols("start_time", "status"); err != nil {
-		return err
+	if err = t.UpdateCols("start_time", "status"); err != nil {
+		return
 	}
 
 	var opts *migration.MigrateOptions
 	opts, err = t.MigrateConfig()
 	if err != nil {
-		return err
+		return
 	}
 
 	opts.MigrateToRepoID = t.RepoID
-	repo, err := migrations.MigrateRepository(graceful.GetManager().HammerContext(), t.Doer, t.Owner.Name, *opts)
+	var repo *models.Repository
+	repo, err = migrations.MigrateRepository(graceful.GetManager().HammerContext(), t.Doer, t.Owner.Name, *opts)
 	if err == nil {
 		log.Trace("Repository migrated [%d]: %s/%s", repo.ID, t.Owner.Name, repo.Name)
-		return nil
+		return
 	}
 
 	if models.IsErrRepoAlreadyExist(err) {
-		return errors.New("The repository name is already used")
+		err = errors.New("The repository name is already used")
+		return
 	}
 
 	// remoteAddr may contain credentials, so we sanitize it
@@ -113,5 +116,7 @@ func runMigrateTask(t *models.Task) (err error) {
 		return fmt.Errorf("Migration failed: %v", err.Error())
 	}
 
-	return handleCreateError(t.Owner, err, "MigratePost")
+	// do not be tempted to coalesce this line with the return
+	err = handleCreateError(t.Owner, err)
+	return
 }
diff --git a/public/img/failed.png b/public/img/failed.png
new file mode 100644
index 0000000000000000000000000000000000000000..b37545f90ca3b304357516661efb9e1431fa96aa
GIT binary patch
literal 11009
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

literal 0
HcmV?d00001

diff --git a/routers/repo/repo.go b/routers/repo/repo.go
index 742c952f6e..4220750328 100644
--- a/routers/repo/repo.go
+++ b/routers/repo/repo.go
@@ -402,19 +402,3 @@ func Download(ctx *context.Context) {
 
 	ctx.ServeFile(archivePath, ctx.Repo.Repository.Name+"-"+refName+ext)
 }
-
-// Status returns repository's status
-func Status(ctx *context.Context) {
-	task, err := models.GetMigratingTask(ctx.Repo.Repository.ID)
-	if err != nil {
-		ctx.JSON(500, map[string]interface{}{
-			"err": err,
-		})
-		return
-	}
-
-	ctx.JSON(200, map[string]interface{}{
-		"status": ctx.Repo.Repository.Status,
-		"err":    task.Errors,
-	})
-}
diff --git a/routers/routes/routes.go b/routers/routes/routes.go
index 135c6b56a8..586474a661 100644
--- a/routers/routes/routes.go
+++ b/routers/routes/routes.go
@@ -479,6 +479,7 @@ func RegisterRoutes(m *macaron.Macaron) {
 		m.Get("/forgot_password", user.ForgotPasswd)
 		m.Post("/forgot_password", user.ForgotPasswdPost)
 		m.Post("/logout", user.SignOut)
+		m.Get("/task/:task", user.TaskStatus)
 	})
 	// ***** END: User *****
 
@@ -986,8 +987,6 @@ func RegisterRoutes(m *macaron.Macaron) {
 
 		m.Get("/archive/*", repo.MustBeNotEmpty, reqRepoCodeReader, repo.Download)
 
-		m.Get("/status", reqRepoCodeReader, repo.Status)
-
 		m.Group("/branches", func() {
 			m.Get("", repo.Branches)
 		}, repo.MustBeNotEmpty, context.RepoRef(), reqRepoCodeReader)
diff --git a/routers/user/task.go b/routers/user/task.go
new file mode 100644
index 0000000000..a88257ee50
--- /dev/null
+++ b/routers/user/task.go
@@ -0,0 +1,30 @@
+// Copyright 2020 The Gitea Authors. All rights reserved.
+// Use of this source code is governed by a MIT-style
+// license that can be found in the LICENSE file.
+
+package user
+
+import (
+	"code.gitea.io/gitea/models"
+	"code.gitea.io/gitea/modules/context"
+)
+
+// TaskStatus returns task's status
+func TaskStatus(ctx *context.Context) {
+	task, opts, err := models.GetMigratingTaskByID(ctx.ParamsInt64("task"), ctx.User.ID)
+	if err != nil {
+		ctx.JSON(500, map[string]interface{}{
+			"err": err,
+		})
+		return
+	}
+
+	ctx.JSON(200, map[string]interface{}{
+		"status":    task.Status,
+		"err":       task.Errors,
+		"repo-id":   task.RepoID,
+		"repo-name": opts.RepoName,
+		"start":     task.StartTime,
+		"end":       task.EndTime,
+	})
+}
diff --git a/templates/repo/migrate/migrating.tmpl b/templates/repo/migrate/migrating.tmpl
index 0057325e91..fdda33a2c7 100644
--- a/templates/repo/migrate/migrating.tmpl
+++ b/templates/repo/migrate/migrating.tmpl
@@ -7,11 +7,16 @@
 				{{template "base/alert" .}}
 				<div class="home">
 					<div class="ui stackable middle very relaxed page grid">
-						<div id="repo_migrating" class="sixteen wide center aligned centered column" repo="{{.Repo.Repository.FullName}}">
+						<div id="repo_migrating" class="sixteen wide center aligned centered column" task="{{.MigrateTask.ID}}">
 							<div>
 								<img src="{{StaticUrlPrefix}}/img/loading.png"/>
 							</div>
 						</div>
+						<div id="repo_migrating_failed_image" class="sixteen wide center aligned centered column" style="display: none;">
+							<div>
+								<img src="{{StaticUrlPrefix}}/img/failed.png"/>
+							</div>
+						</div>
 					</div>
 					<div class="ui stackable middle very relaxed page grid">
 						<div class="sixteen wide center aligned centered column">
@@ -20,6 +25,7 @@
 							</div>
 							<div id="repo_migrating_failed">
 								<p>{{.i18n.Tr "repo.migrate.migrating_failed" .CloneAddr | Safe}}</p>
+								<p id="repo_migrating_failed_error"></p>
 							</div>
 						</div>
 					</div>
diff --git a/web_src/js/index.js b/web_src/js/index.js
index 9fafe62d3e..94238ca3d9 100644
--- a/web_src/js/index.js
+++ b/web_src/js/index.js
@@ -191,25 +191,32 @@ function updateIssuesMeta(url, action, issueIds, elementId) {
 function initRepoStatusChecker() {
   const migrating = $('#repo_migrating');
   $('#repo_migrating_failed').hide();
+  $('#repo_migrating_failed_image').hide();
   if (migrating) {
-    const repo_name = migrating.attr('repo');
-    if (typeof repo_name === 'undefined') {
+    const task = migrating.attr('task');
+    if (typeof task === 'undefined') {
       return;
     }
     $.ajax({
       type: 'GET',
-      url: `${AppSubUrl}/${repo_name}/status`,
+      url: `${AppSubUrl}/user/task/${task}`,
       data: {
         _csrf: csrf,
       },
       complete(xhr) {
         if (xhr.status === 200) {
           if (xhr.responseJSON) {
-            if (xhr.responseJSON.status === 0) {
+            if (xhr.responseJSON.status === 4) {
               window.location.reload();
               return;
+            } else if (xhr.responseJSON.status === 3) {
+              $('#repo_migrating_progress').hide();
+              $('#repo_migrating').hide();
+              $('#repo_migrating_failed').show();
+              $('#repo_migrating_failed_image').show();
+              $('#repo_migrating_failed_error').text(xhr.responseJSON.err);
+              return;
             }
-
             setTimeout(() => {
               initRepoStatusChecker();
             }, 2000);
@@ -217,7 +224,9 @@ function initRepoStatusChecker() {
           }
         }
         $('#repo_migrating_progress').hide();
+        $('#repo_migrating').hide();
         $('#repo_migrating_failed').show();
+        $('#repo_migrating_failed_image').show();
       }
     });
   }