You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@griffin.apache.org by gu...@apache.org on 2019/03/14 14:28:44 UTC
[griffin] branch master updated: [GRIFFIN-229] trigger the job
right now with fixing comments
This is an automated email from the ASF dual-hosted git repository.
guoyp pushed a commit to branch master
in repository https://gitbox.apache.org/repos/asf/griffin.git
The following commit(s) were added to refs/heads/master by this push:
new 2264a28 [GRIFFIN-229] trigger the job right now with fixing comments
2264a28 is described below
commit 2264a28d8fb04664a67f5107bb39b7185975e9d7
Author: Borgatin Alexandr <ab...@griddynamics.com>
AuthorDate: Thu Mar 14 22:28:36 2019 +0800
[GRIFFIN-229] trigger the job right now with fixing comments
[comment](https://github.com/apache/griffin/pull/480#issuecomment-460075951)
> dyingbleed can you please include screenshots for UI changes?
<img width="1074" alt="screen shot 2019-03-06 at 18 19 25" src="https://user-images.githubusercontent.com/43031807/53944639-d4424980-40d0-11e9-977e-29b45b879bf9.png">
<img width="1065" alt="screen shot 2019-03-06 at 18 43 23" src="https://user-images.githubusercontent.com/43031807/53944659-db695780-40d0-11e9-9114-43879cf068fa.png">
Author: Borgatin Alexandr <ab...@griddynamics.com>
Author: Alexandr Borgatin <43...@users.noreply.github.com>
Author: Anthony Li <li...@gmail.com>
Closes #485 from aborgatin/feature/GRIFFIN-229.
---
griffin-doc/service/api-guide.md | 10 ++++
griffin-doc/service/postman/griffin.json | 32 +++++++++++++
.../org/apache/griffin/core/job/JobController.java | 6 +++
.../org/apache/griffin/core/job/JobService.java | 2 +
.../apache/griffin/core/job/JobServiceImpl.java | 28 +++++++----
.../apache/griffin/core/job/JobControllerTest.java | 19 ++++++++
.../griffin/core/job/JobServiceImplTest.java | 56 ++++++++++++++++++++++
ui/angular/src/app/job/job.component.html | 5 +-
ui/angular/src/app/job/job.component.ts | 25 ++++++++++
ui/angular/src/app/service/service.service.ts | 1 +
10 files changed, 174 insertions(+), 10 deletions(-)
diff --git a/griffin-doc/service/api-guide.md b/griffin-doc/service/api-guide.md
index 30bbcc7..95113af 100644
--- a/griffin-doc/service/api-guide.md
+++ b/griffin-doc/service/api-guide.md
@@ -36,6 +36,7 @@ Apache Griffin default `BASE_PATH` is `http://<your ip>:8080`.
- [Griffin Jobs](#3)
- [Add Job](#31)
+ - [Trigger job by id](37)
- [Get Job](#32)
- [Remove Job](#33)
- [Get Job Instances](#34)
@@ -542,6 +543,15 @@ curl -k -H "Content-Type: application/json" -H "Accept: application/json" \
}'
```
+<div id = "37"></div>
+
+### Trigger job by id
+`POST /api/v1/jobs/trigger/{job_id}`
+#### API Example
+```
+curl -k -X POST http://127.0.0.1:8080/api/v1/jobs/trigger/51
+```
+
<div id = "32"></div>
### Get all jobs
diff --git a/griffin-doc/service/postman/griffin.json b/griffin-doc/service/postman/griffin.json
index ac64412..e789121 100644
--- a/griffin-doc/service/postman/griffin.json
+++ b/griffin-doc/service/postman/griffin.json
@@ -1592,6 +1592,38 @@
]
},
{
+ "name": "Trigger job by id",
+ "request": {
+ "method": "POST",
+ "header": [],
+ "body": {
+ "mode": "raw",
+ "raw": ""
+ },
+ "url": {
+ "raw": "{{BASE_PATH}}/api/v1/jobs/trigger/:id",
+ "host": [
+ "{{BASE_PATH}}"
+ ],
+ "path": [
+ "api",
+ "v1",
+ "jobs",
+ "trigger",
+ ":id"
+ ],
+ "variable": [
+ {
+ "key": "id",
+ "value": ""
+ }
+ ]
+ },
+ "description": "`POST /api/v1/jobs/trigger/{id}`\n\n#### Path Variable\n- id -`required` `Long` job id\n\n#### Response\nThe response body should be empty if no error happens, and the HTTP status is (204, \"No Content\").\n\nIt may return failed messages. For example\n```\n{\n \"timestamp\": 1517208792108,\n \"status\": 404,\n \"error\": \"Not Found\",\n \"code\": 40402,\n \"message\": \"Job id does not exist\",\n \"path\": \"/api/v1/jobs/trigger/2\"\n}\n```\nTher [...]
+ },
+ "response": []
+ },
+ {
"name": "Delete job by name",
"request": {
"method": "DELETE",
diff --git a/service/src/main/java/org/apache/griffin/core/job/JobController.java b/service/src/main/java/org/apache/griffin/core/job/JobController.java
index f4ee791..b5274a8 100644
--- a/service/src/main/java/org/apache/griffin/core/job/JobController.java
+++ b/service/src/main/java/org/apache/griffin/core/job/JobController.java
@@ -113,4 +113,10 @@ public class JobController {
.contentType(MediaType.APPLICATION_OCTET_STREAM)
.body(resource);
}
+
+ @RequestMapping(value = "/jobs/trigger/{id}", method = RequestMethod.POST)
+ @ResponseStatus(HttpStatus.NO_CONTENT)
+ public void triggerJob(@PathVariable("id") Long id) throws SchedulerException {
+ jobService.triggerJobById(id);
+ }
}
diff --git a/service/src/main/java/org/apache/griffin/core/job/JobService.java b/service/src/main/java/org/apache/griffin/core/job/JobService.java
index 58e541f..42415d9 100644
--- a/service/src/main/java/org/apache/griffin/core/job/JobService.java
+++ b/service/src/main/java/org/apache/griffin/core/job/JobService.java
@@ -45,4 +45,6 @@ public interface JobService {
JobHealth getHealthInfo();
String getJobHdfsSinksPath(String jobName, long timestamp);
+
+ void triggerJobById(Long id) throws SchedulerException;
}
diff --git a/service/src/main/java/org/apache/griffin/core/job/JobServiceImpl.java b/service/src/main/java/org/apache/griffin/core/job/JobServiceImpl.java
index 7dc7f6d..a7fc546 100644
--- a/service/src/main/java/org/apache/griffin/core/job/JobServiceImpl.java
+++ b/service/src/main/java/org/apache/griffin/core/job/JobServiceImpl.java
@@ -45,14 +45,7 @@ import org.apache.griffin.core.util.JsonUtil;
import org.apache.griffin.core.util.YarnNetUtil;
import org.json.JSONArray;
import org.json.JSONObject;
-import org.quartz.JobDataMap;
-import org.quartz.JobDetail;
-import org.quartz.JobKey;
-import org.quartz.Scheduler;
-import org.quartz.SchedulerException;
-import org.quartz.Trigger;
-import org.quartz.TriggerBuilder;
-import org.quartz.TriggerKey;
+import org.quartz.*;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
@@ -99,7 +92,7 @@ import static org.apache.griffin.core.measure.entity.GriffinMeasure.ProcessType.
import static org.quartz.CronScheduleBuilder.cronSchedule;
import static org.quartz.JobBuilder.newJob;
import static org.quartz.JobKey.jobKey;
-import static org.quartz.SimpleScheduleBuilder.simpleSchedule;
+import static org.quartz.SimpleScheduleBuilder.*;
import static org.quartz.TriggerBuilder.newTrigger;
import static org.quartz.TriggerKey.triggerKey;
@@ -661,4 +654,21 @@ public class JobServiceImpl implements JobService {
return null;
}
}
+
+ @Override
+ public void triggerJobById(Long id) throws SchedulerException {
+ AbstractJob job = jobRepo.findByIdAndDeleted(id, false);
+ validateJobExist(job);
+ Scheduler scheduler = factory.getScheduler();
+ JobKey jobKey = jobKey(job.getName(), job.getGroup());
+ if (scheduler.checkExists(jobKey)) {
+ Trigger trigger = TriggerBuilder.newTrigger()
+ .forJob(jobKey)
+ .startNow()
+ .build();
+ scheduler.scheduleJob(trigger);
+ } else {
+ LOGGER.warn("Could not trigger job id {}.", id);
+ }
+ }
}
diff --git a/service/src/test/java/org/apache/griffin/core/job/JobControllerTest.java b/service/src/test/java/org/apache/griffin/core/job/JobControllerTest.java
index 0bd74e6..ab39b16 100644
--- a/service/src/test/java/org/apache/griffin/core/job/JobControllerTest.java
+++ b/service/src/test/java/org/apache/griffin/core/job/JobControllerTest.java
@@ -28,6 +28,7 @@ import static org.mockito.Mockito.doNothing;
import static org.mockito.Mockito.doThrow;
import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.delete;
import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.get;
+import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.post;
import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.jsonPath;
import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.status;
@@ -171,4 +172,22 @@ public class JobControllerTest {
.andExpect(status().isOk())
.andExpect(jsonPath("$.healthyJobCount", is(1)));
}
+
+ @Test
+ public void testTriggerJobForSuccess() throws Exception {
+ doNothing().when(service).triggerJobById(1L);
+
+ mvc.perform(post(URLHelper.API_VERSION_PATH + "/jobs/trigger/1"))
+ .andExpect(status().isNoContent());
+ }
+
+ @Test
+ public void testTriggerJobForFailureWithException() throws Exception {
+ doThrow(new GriffinException.ServiceException("Failed to trigger job",
+ new Exception()))
+ .when(service).triggerJobById(1L);
+
+ mvc.perform(post(URLHelper.API_VERSION_PATH + "/jobs/trigger/1"))
+ .andExpect(status().isInternalServerError());
+ }
}
diff --git a/service/src/test/java/org/apache/griffin/core/job/JobServiceImplTest.java b/service/src/test/java/org/apache/griffin/core/job/JobServiceImplTest.java
new file mode 100644
index 0000000..a335de2
--- /dev/null
+++ b/service/src/test/java/org/apache/griffin/core/job/JobServiceImplTest.java
@@ -0,0 +1,56 @@
+package org.apache.griffin.core.job;
+
+import org.apache.griffin.core.exception.GriffinException;
+import org.apache.griffin.core.job.entity.AbstractJob;
+import org.apache.griffin.core.job.repo.JobRepo;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.mockito.InjectMocks;
+import org.mockito.Mock;
+import org.quartz.JobKey;
+import org.quartz.Scheduler;
+import org.quartz.SchedulerException;
+import org.springframework.scheduling.quartz.SchedulerFactoryBean;
+import org.springframework.test.context.junit4.SpringRunner;
+
+import static org.apache.griffin.core.util.EntityMocksHelper.createGriffinJob;
+import static org.mockito.BDDMockito.given;
+import static org.mockito.Matchers.any;
+import static org.mockito.Mockito.mock;
+import static org.mockito.Mockito.verify;
+import static org.mockito.internal.verification.VerificationModeFactory.times;
+
+@RunWith(SpringRunner.class)
+public class JobServiceImplTest {
+
+ @Mock
+ private JobRepo<AbstractJob> jobRepo;
+
+ @Mock
+ private SchedulerFactoryBean factory;
+
+ @InjectMocks
+ private JobServiceImpl jobService;
+
+
+ @Test
+ public void testTriggerJobById() throws SchedulerException {
+ Long jobId = 1L;
+ AbstractJob job = createGriffinJob();
+ given(jobRepo.findByIdAndDeleted(jobId,false)).willReturn(job);
+ Scheduler scheduler = mock(Scheduler.class);
+ given(scheduler.checkExists(any(JobKey.class))).willReturn(true);
+ given(factory.getScheduler()).willReturn(scheduler);
+ jobService.triggerJobById(jobId);
+
+ verify(scheduler, times(1)).scheduleJob(any());
+ }
+
+
+ @Test(expected = GriffinException.NotFoundException.class)
+ public void testTriggerJobByIdFail() throws SchedulerException {
+ Long jobId = 1L;
+ given(jobRepo.findByIdAndDeleted(jobId,false)).willReturn(null);
+ jobService.triggerJobById(jobId);
+ }
+}
diff --git a/ui/angular/src/app/job/job.component.html b/ui/angular/src/app/job/job.component.html
index 2fb87a2..2b7cd9d 100644
--- a/ui/angular/src/app/job/job.component.html
+++ b/ui/angular/src/app/job/job.component.html
@@ -77,7 +77,7 @@ under the License.
<a (click)="remove(row)" title="delete" style="text-decoration:none">
<i class="fa fa-trash-o po"></i>
- </a>
+ </a>
<a routerLink="/job/{{row.id}}" title="subscribe">
<i class="fa fa-eye"></i>
</a>
@@ -86,6 +86,9 @@ under the License.
</a>
<a *ngIf="row.action!=='START'" (click)="stateMag(row)" title="Stop" style="text-decoration:none">
<i class="fa fa-stop"></i>
+ </a>
+ <a (click)="trigger(row)" title="trigger now" style="text-decoration:none">
+ <i class="fa fa-caret-square-o-right po"></i>
</a>
</td>
<td>
diff --git a/ui/angular/src/app/job/job.component.ts b/ui/angular/src/app/job/job.component.ts
index 0a86fe5..893db64 100644
--- a/ui/angular/src/app/job/job.component.ts
+++ b/ui/angular/src/app/job/job.component.ts
@@ -45,6 +45,7 @@ export class JobComponent implements OnInit {
action: string;
modalWndMsg: string;
isStop: boolean;
+ isTrigger: boolean;
private toasterService: ToasterService;
@@ -101,6 +102,19 @@ export class JobComponent implements OnInit {
console.log("Error when manage job state");
});
}
+ else if (this.isTrigger) {
+ $("#save").attr("disabled", "true");
+ let actionUrl = this.serviceService.config.uri.triggerJobById + "/" + this.deleteId;
+ this.http.post(actionUrl, {}).subscribe(data => {
+ let self = this;
+ self.hide();
+ this.isTrigger = false;
+ },
+ err => {
+ this.toasterService.pop("error", "Error!", "Failed to trigger job!");
+ console.log("Error when trigger job");
+ });
+ }
else {
let deleteJob = this.serviceService.config.uri.deleteJob;
let deleteUrl = deleteJob + "/" + this.deleteId;
@@ -196,4 +210,15 @@ export class JobComponent implements OnInit {
this.results = Object.assign([], trans).reverse();
});
}
+
+ trigger(row): void {
+ $("#save").removeAttr("disabled");
+ this.modalWndMsg = "Trigger the job with the below information?";
+ this.visible = true;
+ setTimeout(() => (this.visibleAnimate = true), 100);
+ this.deletedRow = row;
+ this.deleteIndex = this.results.indexOf(row);
+ this.deleteId = row.id;
+ this.isTrigger = true;
+ }
}
diff --git a/ui/angular/src/app/service/service.service.ts b/ui/angular/src/app/service/service.service.ts
index 7d50b4b..57093f4 100644
--- a/ui/angular/src/app/service/service.service.ts
+++ b/ui/angular/src/app/service/service.service.ts
@@ -92,6 +92,7 @@ export class ServiceService {
addJobs: this.BACKEND_SERVER + this.API_ROOT_PATH + "/jobs",
modifyJobs: this.BACKEND_SERVER + this.API_ROOT_PATH + "/jobs",
getJobById: this.BACKEND_SERVER + this.API_ROOT_PATH + "/jobs/config",
+ triggerJobById: this.BACKEND_SERVER + this.API_ROOT_PATH + "/jobs/trigger",
getMeasuresByOwner:
this.BACKEND_SERVER + this.API_ROOT_PATH + "/measures/owner/",
deleteJob: this.BACKEND_SERVER + this.API_ROOT_PATH + "/jobs",