Reset failed tasks

This commit is contained in:
simon987 2019-02-27 20:53:09 -05:00
parent 09dd911dc1
commit 67c67090cf
17 changed files with 118 additions and 32 deletions

View File

@ -98,6 +98,7 @@ func New() *WebAPI {
api.router.POST("/project/secret/:id", LogRequestMiddleware(api.SetSecret)) api.router.POST("/project/secret/:id", LogRequestMiddleware(api.SetSecret))
api.router.GET("/project/webhook_secret/:id", LogRequestMiddleware(api.GetWebhookSecret)) api.router.GET("/project/webhook_secret/:id", LogRequestMiddleware(api.GetWebhookSecret))
api.router.POST("/project/webhook_secret/:id", LogRequestMiddleware(api.SetWebhookSecret)) api.router.POST("/project/webhook_secret/:id", LogRequestMiddleware(api.SetWebhookSecret))
api.router.POST("/project/reset_failed_tasks/:id", LogRequestMiddleware(api.ResetFailedTasks))
api.router.POST("/task/submit", LogRequestMiddleware(api.SubmitTask)) api.router.POST("/task/submit", LogRequestMiddleware(api.SubmitTask))
api.router.GET("/task/get/:project", LogRequestMiddleware(api.GetTaskFromProject)) api.router.GET("/task/get/:project", LogRequestMiddleware(api.GetTaskFromProject))

View File

@ -15,7 +15,7 @@ const (
type JsonResponse struct { type JsonResponse struct {
Ok bool `json:"ok"` Ok bool `json:"ok"`
Message string `json:"message,omitempty"` Message string `json:"message,omitempty"`
RateLimitDelay string `json:"rate_limit_delay,omitempty"` RateLimitDelay float64 `json:"rate_limit_delay,omitempty"`
Content interface{} `json:"content,omitempty"` Content interface{} `json:"content,omitempty"`
} }
@ -305,6 +305,11 @@ type GetSecretResponse struct {
type SetWebhookSecretRequest struct { type SetWebhookSecretRequest struct {
WebhookSecret string `json:"webhook_secret"` WebhookSecret string `json:"webhook_secret"`
} }
type GetWebhookSecretResponse struct { type GetWebhookSecretResponse struct {
WebhookSecret string `json:"webhook_secret"` WebhookSecret string `json:"webhook_secret"`
} }
type ResetFailedTaskResponse struct {
AffectedTasks int64 `json:"affected_tasks"`
}

View File

@ -120,7 +120,7 @@ func (api *WebAPI) CreateProject(r *Request) {
} }
api.Database.SetManagerRoleOn(manager.(*storage.Manager).Id, id, api.Database.SetManagerRoleOn(manager.(*storage.Manager).Id, id,
storage.RoleManageAccess|storage.RoleRead|storage.RoleEdit|storage.RoleSecret) storage.RoleManageAccess|storage.RoleRead|storage.RoleEdit|storage.RoleSecret|storage.RoleMaintenance)
r.OkJson(JsonResponse{ r.OkJson(JsonResponse{
Ok: true, Ok: true,
Content: CreateProjectResponse{ Content: CreateProjectResponse{
@ -209,6 +209,9 @@ func (api *WebAPI) UpdateProject(r *Request) {
"project": project, "project": project,
}).Warn("Error during project update") }).Warn("Error during project update")
} else { } else {
api.SubmitLimiters.Delete(project.Id)
api.AssignLimiters.Delete(project.Id)
r.OkJson(JsonResponse{ r.OkJson(JsonResponse{
Ok: true, Ok: true,
}) })
@ -688,3 +691,35 @@ func (api *WebAPI) SetWebhookSecret(r *Request) {
}) })
} }
} }
func (api *WebAPI) ResetFailedTasks(r *Request) {
pid, err := strconv.ParseInt(r.Ctx.UserValue("id").(string), 10, 64)
if err != nil || pid <= 0 {
r.Json(JsonResponse{
Ok: false,
Message: "Invalid project id",
}, 400)
return
}
sess := api.Session.StartFasthttp(r.Ctx)
manager := sess.Get("manager")
if !isActionOnProjectAuthorized(pid, manager, storage.RoleMaintenance, api.Database) {
r.Json(JsonResponse{
Ok: false,
Message: "Unauthorized",
}, 403)
return
}
res := api.Database.ResetFailedTasks(pid)
r.OkJson(JsonResponse{
Ok: true,
Content: ResetFailedTaskResponse{
AffectedTasks: res,
},
})
}

View File

@ -60,7 +60,7 @@ func (api *WebAPI) SubmitTask(r *Request) {
r.Json(JsonResponse{ r.Json(JsonResponse{
Ok: false, Ok: false,
Message: "Too many requests", Message: "Too many requests",
RateLimitDelay: strconv.FormatFloat(delay, 'f', -1, 64), RateLimitDelay: delay,
}, 429) }, 429)
return return
} }
@ -111,7 +111,7 @@ func (api *WebAPI) GetTaskFromProject(r *Request) {
r.Json(JsonResponse{ r.Json(JsonResponse{
Ok: false, Ok: false,
Message: "Too many requests", Message: "Too many requests",
RateLimitDelay: strconv.FormatFloat(delay, 'f', -1, 64), RateLimitDelay: delay,
}, 429) }, 429)
return return
} }

View File

@ -48,7 +48,7 @@ CREATE TABLE task
project INTEGER REFERENCES project (id), project INTEGER REFERENCES project (id),
assignee INTEGER REFERENCES worker (id), assignee INTEGER REFERENCES worker (id),
max_assign_time INTEGER DEFAULT 0, max_assign_time INTEGER DEFAULT 0,
assign_time INTEGER DEFAULT 0, assign_time INTEGER DEFAULT NULL,
verification_count INTEGER DEFAULT 0, verification_count INTEGER DEFAULT 0,
priority SMALLINT DEFAULT 0, priority SMALLINT DEFAULT 0,
retries SMALLINT DEFAULT 0, retries SMALLINT DEFAULT 0,

View File

@ -16,6 +16,7 @@ const (
RoleEdit ManagerRole = 2 RoleEdit ManagerRole = 2
RoleManageAccess ManagerRole = 4 RoleManageAccess ManagerRole = 4
RoleSecret ManagerRole = 8 RoleSecret ManagerRole = 8
RoleMaintenance ManagerRole = 16
) )
type Manager struct { type Manager struct {

13
storage/maintenance.go Normal file
View File

@ -0,0 +1,13 @@
package storage
func (database *Database) ResetFailedTasks(pid int64) int64 {
db := database.getDB()
res, err := db.Exec(`UPDATE task SET status=1, retries=0, assign_time=NULL, assignee=NULL
WHERE project=$1 AND status=2`, pid)
handleErr(err)
rowsAffected, _ := res.RowsAffected()
return rowsAffected
}

View File

@ -16,19 +16,19 @@ CREATE TABLE project
id SERIAL PRIMARY KEY NOT NULL, id SERIAL PRIMARY KEY NOT NULL,
priority INTEGER DEFAULT 0 NOT NULL, priority INTEGER DEFAULT 0 NOT NULL,
closed_task_count INT DEFAULT 0 NOT NULL, closed_task_count INT DEFAULT 0 NOT NULL,
chain INT DEFAULT NULL REFERENCES project (id), chain INT DEFAULT NULL REFERENCES project (id),
public boolean NOT NULL, public boolean NOT NULL,
hidden boolean NOT NULL, hidden boolean NOT NULL,
paused boolean NOT NULL, paused boolean NOT NULL,
name TEXT UNIQUE NOT NULL, name TEXT UNIQUE NOT NULL,
clone_url TEXT NOT NULL, clone_url TEXT NOT NULL,
git_repo TEXT NOT NULL, git_repo TEXT NOT NULL,
version TEXT NOT NULL, version TEXT NOT NULL,
motd TEXT NOT NULL, motd TEXT NOT NULL,
secret TEXT NOT NULL DEFAULT '{}', secret TEXT NOT NULL DEFAULT '{}',
webhook_secret TEXT NOT NULL, webhook_secret TEXT NOT NULL,
assign_rate DOUBLE PRECISION NOT NULL, assign_rate DOUBLE PRECISION NOT NULL,
submit_rate DOUBLE PRECISION NOT NULL submit_rate DOUBLE PRECISION NOT NULL
); );
CREATE TABLE worker_access CREATE TABLE worker_access
@ -48,7 +48,7 @@ CREATE TABLE task
project INTEGER REFERENCES project (id), project INTEGER REFERENCES project (id),
assignee INTEGER REFERENCES worker (id), assignee INTEGER REFERENCES worker (id),
max_assign_time INTEGER DEFAULT 0, max_assign_time INTEGER DEFAULT 0,
assign_time INTEGER DEFAULT 0, assign_time INTEGER DEFAULT NULL,
verification_count INTEGER DEFAULT 0, verification_count INTEGER DEFAULT 0,
priority SMALLINT DEFAULT 0, priority SMALLINT DEFAULT 0,
retries SMALLINT DEFAULT 0, retries SMALLINT DEFAULT 0,

View File

@ -99,19 +99,23 @@ export class ApiService {
} }
getSecret(pid: number) { getSecret(pid: number) {
return this.http.get(this.url + `/project/secret/${pid}`,) return this.http.get(this.url + `/project/secret/${pid}`, this.options)
} }
setSecret(pid: number, secret: string) { setSecret(pid: number, secret: string) {
return this.http.post(this.url + `/project/secret/${pid}`, {"secret": secret}) return this.http.post(this.url + `/project/secret/${pid}`, {"secret": secret}, this.options)
} }
getWebhookSecret(pid: number) { getWebhookSecret(pid: number) {
return this.http.get(this.url + `/project/webhook_secret/${pid}`,) return this.http.get(this.url + `/project/webhook_secret/${pid}`, this.options)
} }
setWebhookSecret(pid: number, secret: string) { setWebhookSecret(pid: number, secret: string) {
return this.http.post(this.url + `/project/webhook_secret/${pid}`, {"webhook_secret": secret}) return this.http.post(this.url + `/project/webhook_secret/${pid}`, {"webhook_secret": secret}, this.options)
}
resetFailedTasks(pid: number) {
return this.http.post(this.url + `/project/reset_failed_tasks/${pid}`, null, this.options)
} }
} }

View File

@ -10,4 +10,6 @@ export interface Project {
chain: number; chain: number;
hidden: boolean; hidden: boolean;
paused: boolean; paused: boolean;
assign_rate: number;
submit_rate: number;
} }

View File

@ -84,7 +84,7 @@
</mat-card-content> </mat-card-content>
<mat-card-actions> <mat-card-actions>
<button mat-raised-button [routerLink]="'/projects'">Back</button> <button mat-raised-button [routerLink]="'../'">{{"nav.back"|translate}}</button>
<button mat-raised-button color="primary" *ngIf="project && auth.logged" <button mat-raised-button color="primary" *ngIf="project && auth.logged"
[routerLink]="'/project/' + project.id + '/update'">{{"project.update" | translate}}</button> [routerLink]="'/project/' + project.id + '/update'">{{"project.update" | translate}}</button>
<button mat-raised-button color="primary" *ngIf="project && auth.logged" <button mat-raised-button color="primary" *ngIf="project && auth.logged"

View File

@ -349,7 +349,16 @@ export class ProjectDashboardComponent implements OnInit {
width: '250px', width: '250px',
}).afterClosed().subscribe(result => { }).afterClosed().subscribe(result => {
if (result) { if (result) {
alert("yes") this.apiService.resetFailedTasks(this.projectId).subscribe(
data => {
this.translate.get("project.reset_response").subscribe(t =>
this.messenger.show(t + data["content"]["affected_tasks"]))
},
error => {
this.translate.get("messenger.unauthorized").subscribe(t =>
this.messenger.show(t))
}
)
} }
}); });
} }

View File

@ -70,7 +70,7 @@
</mat-card-content> </mat-card-content>
<mat-card-actions> <mat-card-actions>
<button mat-raised-button [routerLink]="'../'">Back</button> <button mat-raised-button [routerLink]="'../'">{{"nav.back"|translate}}</button>
</mat-card-actions> </mat-card-actions>
</mat-card> </mat-card>
</div> </div>

View File

@ -39,7 +39,7 @@
</mat-card-content> </mat-card-content>
<mat-card-actions> <mat-card-actions>
<button mat-raised-button [routerLink]="'../'">Back</button> <button mat-raised-button [routerLink]="'../'">{{"nav.back"|translate}}</button>
<button mat-raised-button color="primary" <button mat-raised-button color="primary"
(click)="onWebhookUpdate()">{{"secret.update" | translate}}</button> (click)="onWebhookUpdate()">{{"secret.update" | translate}}</button>
</mat-card-actions> </mat-card-actions>

View File

@ -38,10 +38,18 @@
<mat-hint align="start">{{'project.git_repo_hint'|translate}}</mat-hint> <mat-hint align="start">{{'project.git_repo_hint'|translate}}</mat-hint>
</mat-form-field> </mat-form-field>
<project-select [(project)]="selectedProject"></project-select> <project-select [(project)]="selectedProject"></project-select>
<mat-form-field appearance="outline">
<mat-label>{{"project.assign_rate"|translate}}</mat-label>
<input matInput [(ngModel)]="project.assign_rate" name="assign_rate" type="number">
</mat-form-field>
<mat-form-field appearance="outline">
<mat-label>{{"project.submit_rate"|translate}}</mat-label>
<input matInput [(ngModel)]="project.submit_rate" name="submit_rate" type="number">
</mat-form-field>
</form> </form>
</mat-card-content> </mat-card-content>
<mat-card-actions> <mat-card-actions>
<button mat-raised-button [routerLink]="'../'">Back</button> <button mat-raised-button [routerLink]="'../'">{{"nav.back"|translate}}</button>
<button type="submit" form="uf" mat-raised-button color="primary">{{"project.update" | translate}}</button> <button type="submit" form="uf" mat-raised-button color="primary">{{"project.update" | translate}}</button>
</mat-card-actions> </mat-card-actions>
</mat-card> </mat-card>

View File

@ -9,7 +9,7 @@
"worker_dashboard": "Workers", "worker_dashboard": "Workers",
"account": "Account", "account": "Account",
"manager_list": "Managers", "manager_list": "Managers",
"back": "back" "back": "Back"
}, },
"logs": { "logs": {
"title": "Logs", "title": "Logs",
@ -69,7 +69,11 @@
"chain": "Chain tasks to", "chain": "Chain tasks to",
"manager_select": "Give access to manager", "manager_select": "Give access to manager",
"version": "Git version (commit hash)", "version": "Git version (commit hash)",
"secret": "Secret" "secret": "Secret",
"reset_response": "Reset failed tasks: ",
"assign_rate": "Task assign rate limit",
"submit_rate": "Task submit rate limit",
"rate": "per second"
}, },
"dashboard": { "dashboard": {
"title": "Dashboard for", "title": "Dashboard for",

View File

@ -9,7 +9,7 @@
"worker_dashboard": "Workers", "worker_dashboard": "Workers",
"account": "Compte", "account": "Compte",
"manager_list": "Managers", "manager_list": "Managers",
"back": "Arrière" "back": "Retour"
}, },
"logs": { "logs": {
"title": "Journaux", "title": "Journaux",
@ -69,7 +69,11 @@
"perms": "Permissions", "perms": "Permissions",
"chain": "Enchainer les tâches vers", "chain": "Enchainer les tâches vers",
"manager_select": "Donner accès à", "manager_select": "Donner accès à",
"version": "Version git (hash du commit)" "version": "Version git (hash du commit)",
"reset_response": "Réinitialisé les tâches en échec: ",
"assign_rate": "Taux d'assignation de tâches",
"submit_rate": "Taux de soumission de tâches",
"rate": "par seconde"
}, },
"dashboard": { "dashboard": {
"title": "Tableau de bord pour ", "title": "Tableau de bord pour ",