This is an automated email from the ASF dual-hosted git repository. oleewere pushed a commit to branch master in repository https://gitbox.apache.org/repos/asf/ambari-infra.git
The following commit(s) were added to refs/heads/master by this push: new 07a56bc AMBARI-24969 - Infra Manager: REST api cleanup (#20) 07a56bc is described below commit 07a56bc2709c4daa3a762260604bbbe28cc24d64 Author: kasakrisz <33458261+kasakr...@users.noreply.github.com> AuthorDate: Sat Dec 1 12:49:27 2018 +0100 AMBARI-24969 - Infra Manager: REST api cleanup (#20) * AMBARI-24969 - Infra Manager: REST api cleanup * AMBARI-24969 - Infra Manager: REST api cleanup - fix build --- ambari-infra-manager-it/pom.xml | 84 ++- .../java/org/apache/ambari/infra/InfraClient.java | 133 +--- .../ambari/infra/steps/AbstractInfraSteps.java | 21 +- .../apache/ambari/infra/steps/ExportJobsSteps.java | 48 +- .../{log4j.properties => log4j2-test.properties} | 19 +- ambari-infra-manager/.gitignore | 4 +- ambari-infra-manager/docs/api/swagger.yaml | 784 --------------------- ambari-infra-manager/pom.xml | 50 +- .../ambari/infra/conf/FsPermissionConverter.java | 37 - .../infra/conf/StaticResourceConfiguration.java | 4 +- .../infra/doc/InfraManagerApiDocStorage.java | 92 --- .../ambari/infra/job/archive/DocumentExporter.java | 10 +- .../ambari/infra/job/cleanup/TaskHistoryWiper.java | 11 +- .../infra/job/deleting/DocumentWiperTasklet.java | 3 +- .../OffsetDateTimeToStringConverter.java} | 20 +- .../infra/json/StringToDurationConverter.java | 12 +- .../infra/json/StringToFsPermissionConverter.java | 11 +- .../apache/ambari/infra/manager/JobManager.java | 39 +- .../DateUtil.java} | 21 +- .../infra/model/ExecutionContextResponse.java | 4 +- ...textResponse.java => ISO8601DateFormatter.java} | 35 +- .../ambari/infra/model/JobDetailsResponse.java | 53 -- .../infra/model/JobExecutionDetailsResponse.java | 20 +- .../infra/model/JobExecutionInfoResponse.java | 183 +++-- .../infra/model/JobExecutionRestartRequest.java | 3 + .../infra/model/JobExecutionStopRequest.java | 3 + .../infra/model/JobInstanceDetailsResponse.java | 30 +- .../infra/model/JobInstanceStartRequest.java | 5 +- .../infra/model/StepExecutionContextResponse.java | 15 +- .../infra/model/StepExecutionInfoResponse.java | 127 ++-- .../infra/model/StepExecutionProgressResponse.java | 12 +- .../infra/model/wrapper/JobExecutionData.java | 118 ---- .../infra/model/wrapper/StepExecutionData.java | 133 ---- .../apache/ambari/infra/rest/ApiDocResource.java | 56 -- .../org/apache/ambari/infra/rest/JobResource.java | 10 +- .../src/main/resources/infra-manager.properties | 1 + .../src/main/resources/swagger/swagger.html | 2 +- .../infra/job/archive/DocumentExporterTest.java | 31 +- .../infra/model/ISO8601DateFormatterTest.java | 50 ++ 39 files changed, 595 insertions(+), 1699 deletions(-) diff --git a/ambari-infra-manager-it/pom.xml b/ambari-infra-manager-it/pom.xml index f1f296b..8745713 100644 --- a/ambari-infra-manager-it/pom.xml +++ b/ambari-infra-manager-it/pom.xml @@ -33,9 +33,12 @@ <properties> <jbehave.version>4.0.5</jbehave.version> - <failsafe-plugin.version>2.20</failsafe-plugin.version> - <infra-manager.docker.host>localhost</infra-manager.docker.host> + <failsafePlugin.version>2.20</failsafePlugin.version> + <infraManager.docker.host>localhost</infraManager.docker.host> <stories.location>NONE</stories.location> + <okhttp.version>2.7.5</okhttp.version> + <generatedSourcesPath>${project.build.directory}/generated-sources</generatedSourcesPath> + <generatedSourcesJavaPath>main/java</generatedSourcesJavaPath> </properties> <dependencies> @@ -167,6 +170,32 @@ <artifactId>log4j-core</artifactId> <version>2.11.1</version> </dependency> + <!-- swagger client related --> + <dependency> + <groupId>com.google.code.gson</groupId> + <artifactId>gson</artifactId> + </dependency> + <dependency> + <groupId>io.swagger</groupId> + <artifactId>swagger-annotations</artifactId> + <version>1.5.21</version> + <scope>compile</scope> + </dependency> + <dependency> + <groupId>io.gsonfire</groupId> + <artifactId>gson-fire</artifactId> + <version>1.8.3</version> + </dependency> + <dependency> + <groupId>com.squareup.okhttp</groupId> + <artifactId>okhttp</artifactId> + <version>${okhttp.version}</version> + </dependency> + <dependency> + <groupId>com.squareup.okhttp</groupId> + <artifactId>logging-interceptor</artifactId> + <version>${okhttp.version}</version> + </dependency> </dependencies> <build> @@ -182,6 +211,53 @@ <directory>src/test/resources</directory> </testResource> </testResources> + <plugins> + <plugin> + <groupId>io.swagger</groupId> + <artifactId>swagger-codegen-maven-plugin</artifactId> + <version>2.3.1</version> + <executions> + <execution> + <goals> + <goal>generate</goal> + </goals> + <configuration> + <inputSpec>${project.parent.basedir}/ambari-infra-manager/src/main/resources/swagger/swagger.yaml</inputSpec> + <language>java</language> + <generateApiTests>false</generateApiTests> + <generateModelTests>false</generateModelTests> + <output>${generatedSourcesPath}</output> + <configOptions> + <sourceFolder>${generatedSourcesJavaPath}</sourceFolder> + <apiPackage>org.apache.ambari.infra.client.api</apiPackage> + <modelPackage>org.apache.ambari.infra.client.model</modelPackage> + <invokerPackage>org.apache.ambari.infra.client.invoker</invokerPackage> + <dateLibrary>java8</dateLibrary> + </configOptions> + </configuration> + </execution> + </executions> + </plugin> + <plugin> + <groupId>org.codehaus.mojo</groupId> + <artifactId>build-helper-maven-plugin</artifactId> + <version>3.0.0</version> + <executions> + <execution> + <id>add-generated-source</id> + <phase>initialize</phase> + <goals> + <goal>add-source</goal> + </goals> + <configuration> + <sources> + <source>${generatedSourcesPath}/${generatedSourcesJavaPath}</source> + </sources> + </configuration> + </execution> + </executions> + </plugin> + </plugins> </build> <profiles> @@ -197,7 +273,7 @@ <plugin> <groupId>org.apache.maven.plugins</groupId> <artifactId>maven-failsafe-plugin</artifactId> - <version>${failsafe-plugin.version}</version> + <version>${failsafePlugin.version}</version> <executions> <execution> <id>run-integration-tests</id> @@ -212,7 +288,7 @@ </includes> <systemPropertyVariables> <log4j.configuration>file:${project.build.testOutputDirectory}/log4j.properties</log4j.configuration> - <docker.host>${infra-manager.docker.host}</docker.host> + <docker.host>${infraManager.docker.host}</docker.host> <backend.stories.location>${stories.location}</backend.stories.location> </systemPropertyVariables> </configuration> diff --git a/ambari-infra-manager-it/src/test/java/org/apache/ambari/infra/InfraClient.java b/ambari-infra-manager-it/src/test/java/org/apache/ambari/infra/InfraClient.java index 1b0b4cf..bce3c7e 100644 --- a/ambari-infra-manager-it/src/test/java/org/apache/ambari/infra/InfraClient.java +++ b/ambari-infra-manager-it/src/test/java/org/apache/ambari/infra/InfraClient.java @@ -18,134 +18,59 @@ */ package org.apache.ambari.infra; -import static org.apache.commons.lang.StringUtils.isBlank; +import java.util.List; -import java.io.IOException; -import java.io.UncheckedIOException; -import java.net.URI; -import java.net.URISyntaxException; -import java.nio.charset.Charset; -import java.util.HashMap; -import java.util.Map; +import org.apache.ambari.infra.client.api.JobsApi; +import org.apache.ambari.infra.client.invoker.ApiClient; +import org.apache.ambari.infra.client.invoker.ApiException; +import org.apache.ambari.infra.client.model.JobExecutionInfoResponse; -import org.apache.commons.io.IOUtils; -import org.apache.http.auth.AuthScope; -import org.apache.http.auth.UsernamePasswordCredentials; -import org.apache.http.client.ClientProtocolException; -import org.apache.http.client.CredentialsProvider; -import org.apache.http.client.methods.CloseableHttpResponse; -import org.apache.http.client.methods.HttpDelete; -import org.apache.http.client.methods.HttpGet; -import org.apache.http.client.methods.HttpPost; -import org.apache.http.client.methods.HttpRequestBase; -import org.apache.http.client.utils.URIBuilder; -import org.apache.http.impl.client.BasicCredentialsProvider; -import org.apache.http.impl.client.CloseableHttpClient; -import org.apache.http.impl.client.DefaultHttpRequestRetryHandler; -import org.apache.http.impl.client.HttpClientBuilder; -import org.apache.logging.log4j.LogManager; -import org.apache.logging.log4j.Logger; - -import com.fasterxml.jackson.core.JsonParseException; -import com.fasterxml.jackson.core.type.TypeReference; -import com.fasterxml.jackson.databind.JsonMappingException; -import com.fasterxml.jackson.databind.ObjectMapper; - -// TODO: use swagger -public class InfraClient implements AutoCloseable { - private static final Logger logger = LogManager.getLogger(InfraClient.class); - - private final CloseableHttpClient httpClient; - private final URI baseUrl; +public class InfraClient { + private final JobsApi jobsApi; public InfraClient(String baseUrl) { + ApiClient apiClient = new ApiClient().setBasePath(baseUrl); + apiClient.setUsername("admin"); + apiClient.setPassword("admin"); + this.jobsApi = new JobsApi(apiClient); + } + + public List<String> getJobs() { try { - this.baseUrl = new URI(baseUrl); - } catch (URISyntaxException e) { + return jobsApi.getAllJobNames(); + } catch (ApiException e) { throw new RuntimeException(e); } - - CredentialsProvider provider = new BasicCredentialsProvider(); - UsernamePasswordCredentials credentials - = new UsernamePasswordCredentials("admin", "admin"); - provider.setCredentials(AuthScope.ANY, credentials); - - httpClient = HttpClientBuilder.create() - .setRetryHandler(new DefaultHttpRequestRetryHandler(0, false)) - .setDefaultCredentialsProvider(provider) - .build(); - } - - @Override - public void close() throws Exception { - httpClient.close(); } - // TODO: return job data - public void getJobs() { - execute(new HttpGet(baseUrl)); - } - - private HttpResponse execute(HttpRequestBase post) { - try (CloseableHttpResponse response = httpClient.execute(post)) { - String responseBodyText = IOUtils.toString(response.getEntity().getContent(), Charset.defaultCharset()); - int statusCode = response.getStatusLine().getStatusCode(); - logger.info("Response code {} body {} ", statusCode, responseBodyText); - if (!(200 <= statusCode && statusCode <= 299)) - throw new RuntimeException("Error while executing http request: " + responseBodyText); - return new HttpResponse(statusCode, responseBodyText); - } catch (ClientProtocolException e) { + public JobExecutionInfoResponse startJob(String jobName, String parameters) { + try { + return jobsApi.startJob(jobName, parameters); + } catch (ApiException e) { throw new RuntimeException(e); - } catch (IOException e) { - throw new UncheckedIOException(e); } } - public JobExecutionInfo startJob(String jobName, String parameters) { - URIBuilder uriBuilder = new URIBuilder(baseUrl); - uriBuilder.setScheme("http"); - uriBuilder.setPath(uriBuilder.getPath() + "/" + jobName); - if (!isBlank(parameters)) - uriBuilder.addParameter("params", parameters); + public void restartJob(String jobName, long jobId) { try { - String responseText = execute(new HttpPost(uriBuilder.build())).getBody(); - Map<String, Object> responseContent = new ObjectMapper().readValue(responseText, new TypeReference<HashMap<String,Object>>() {}); - if (!responseContent.containsKey("jobId")) - throw new NullPointerException("jobId is not found in start job responseContent"); - if (!responseContent.containsKey("jobExecutionData")) - throw new NullPointerException("jobExecutionData is not found in start job responseContent"); - if (!((Map)responseContent.get("jobExecutionData")).containsKey("id")) - throw new NullPointerException("id is not found in jobExecutionData"); - return new JobExecutionInfo(responseContent.get("jobId").toString(), ((Map)responseContent.get("jobExecutionData")).get("id").toString()); - } catch (URISyntaxException | JsonParseException | JsonMappingException e) { + jobsApi.restartJobInstance(jobName, jobId, "RESTART"); + } catch (ApiException e) { throw new RuntimeException(e); - } catch (IOException e) { - throw new UncheckedIOException(e); } } - public void restartJob(String jobName, String jobId) { - URIBuilder uriBuilder = new URIBuilder(baseUrl); - uriBuilder.setScheme("http"); - uriBuilder.setPath(String.format("%s/%s/%s/executions", uriBuilder.getPath(), jobName, jobId)); - uriBuilder.addParameter("operation", "RESTART"); + public void stopJob(long jobExecutionId) { try { - HttpResponse httpResponse = execute(new HttpPost(uriBuilder.build())); - if (httpResponse.getCode() != 200) - throw new RuntimeException(httpResponse.getBody()); - } catch (URISyntaxException e) { + jobsApi.stopOrAbandonJobExecution(jobExecutionId, "STOP"); + } catch (ApiException e) { throw new RuntimeException(e); } } - public void stopJob(String jobExecutionId) { - URIBuilder uriBuilder = new URIBuilder(baseUrl); - uriBuilder.setScheme("http"); - uriBuilder.setPath(String.format("%s/executions/%s", uriBuilder.getPath(), jobExecutionId)); - uriBuilder.addParameter("operation", "STOP"); + public boolean isRunning(String jobName) { try { - execute(new HttpDelete(uriBuilder.build())); - } catch (URISyntaxException e) { + return !jobsApi.getExecutionIdsByJobName(jobName).isEmpty(); + } catch (ApiException e) { throw new RuntimeException(e); } } diff --git a/ambari-infra-manager-it/src/test/java/org/apache/ambari/infra/steps/AbstractInfraSteps.java b/ambari-infra-manager-it/src/test/java/org/apache/ambari/infra/steps/AbstractInfraSteps.java index 9ecf489..985fda5 100644 --- a/ambari-infra-manager-it/src/test/java/org/apache/ambari/infra/steps/AbstractInfraSteps.java +++ b/ambari-infra-manager-it/src/test/java/org/apache/ambari/infra/steps/AbstractInfraSteps.java @@ -47,17 +47,16 @@ public abstract class AbstractInfraSteps { private static final int INFRA_MANAGER_PORT = 61890; private static final int FAKE_S3_PORT = 4569; - private static final int HDFS_PORT = 9000; protected static final String S3_BUCKET_NAME = "testbucket"; private String ambariFolder; private String shellScriptLocation; - private String dockerHost; private S3Client s3client; private int documentId = 0; private Solr solr; + private InfraClient infraClient; public InfraClient getInfraClient() { - return new InfraClient(String.format("http://%s:%d/api/v1/jobs", dockerHost, INFRA_MANAGER_PORT)); + return infraClient; } public Solr getSolr() { @@ -89,13 +88,16 @@ public abstract class AbstractInfraSteps { FileUtils.cleanDirectory(new File(localDataFolder)); } + logger.info("Copy resources"); FileUtils.copyDirectory(new ClassPathResource("conf").getFile(), new File(getInfraManagerConfDir())); shellScriptLocation = ambariFolder + "/ambari-infra/ambari-infra-manager/docker/infra-manager-docker-compose.sh"; logger.info("Create new docker container for testing Ambari Infra Manager ..."); runCommand(new String[]{shellScriptLocation, "start"}); - dockerHost = getDockerHost(); + String dockerHost = getDockerHost(); + + this.infraClient = new InfraClient(String.format("http://%s:%d/api/v1", dockerHost, INFRA_MANAGER_PORT)); solr = new Solr(); solr.waitUntilSolrIsUp(); @@ -110,11 +112,10 @@ public abstract class AbstractInfraSteps { checkInfraManagerReachable(); } - private void checkInfraManagerReachable() throws Exception { - try (InfraClient httpClient = getInfraClient()) { - doWithin(30, "Start Ambari Infra Manager", httpClient::getJobs); - logger.info("Ambari Infra Manager is up and running"); - } + private void checkInfraManagerReachable() { + InfraClient infraClient = getInfraClient(); + doWithin(30, "Start Ambari Infra Manager", infraClient::getJobs); + logger.info("Ambari Infra Manager is up and running"); } protected SolrInputDocument addDocument(OffsetDateTime logtime) { @@ -149,7 +150,7 @@ public abstract class AbstractInfraSteps { solrInputDocument.addField("logtime", new Date(logtime.toInstant().toEpochMilli())); solrInputDocument.addField("evtTime", new Date(logtime.toInstant().toEpochMilli())); solrInputDocument.addField("_ttl_", "+7DAYS"); - solrInputDocument.addField("_expire_at_", "2017-12-15T10:23:19.106Z"); + solrInputDocument.addField("_expire_at_", new Date(logtime.plusDays(7).toInstant().toEpochMilli())); solr.add(solrInputDocument); return solrInputDocument; } diff --git a/ambari-infra-manager-it/src/test/java/org/apache/ambari/infra/steps/ExportJobsSteps.java b/ambari-infra-manager-it/src/test/java/org/apache/ambari/infra/steps/ExportJobsSteps.java index e67762e..0825290 100644 --- a/ambari-infra-manager-it/src/test/java/org/apache/ambari/infra/steps/ExportJobsSteps.java +++ b/ambari-infra-manager-it/src/test/java/org/apache/ambari/infra/steps/ExportJobsSteps.java @@ -40,8 +40,8 @@ import java.util.Map; import java.util.Set; import org.apache.ambari.infra.InfraClient; -import org.apache.ambari.infra.JobExecutionInfo; import org.apache.ambari.infra.S3Client; +import org.apache.ambari.infra.client.model.JobExecutionInfoResponse; import org.apache.commons.compress.compressors.bzip2.BZip2CompressorInputStream; import org.apache.logging.log4j.LogManager; import org.apache.logging.log4j.Logger; @@ -58,7 +58,7 @@ public class ExportJobsSteps extends AbstractInfraSteps { private static final Logger logger = LogManager.getLogger(ExportJobsSteps.class); private Set<String> documentIds = new HashSet<>(); - private Map<String, JobExecutionInfo> launchedJobs = new HashMap<>(); + private Map<String, JobExecutionInfoResponse> launchedJobs = new HashMap<>(); @Given("$count documents in solr") public void addDocuments(int count) { @@ -94,32 +94,24 @@ public class ExportJobsSteps extends AbstractInfraSteps { @When("start $jobName job with parameters $parameters after $waitSec seconds") public void startJob(String jobName, String parameters, int waitSec) throws Exception { Thread.sleep(waitSec * 1000); - try (InfraClient httpClient = getInfraClient()) { - JobExecutionInfo jobExecutionInfo = httpClient.startJob(jobName, parameters); - logger.info("Job {} started: {}", jobName, jobExecutionInfo); - launchedJobs.put(jobName, jobExecutionInfo); - } + JobExecutionInfoResponse jobExecutionInfo = getInfraClient().startJob(jobName, parameters); + logger.info("Job {} started: {}", jobName, jobExecutionInfo); + launchedJobs.put(jobName, jobExecutionInfo); } @When("restart $jobName job within $waitSec seconds") public void restartJob(String jobName, int waitSec) { - doWithin(waitSec, "Restarting job " + jobName, () -> { - try (InfraClient httpClient = getInfraClient()) { - httpClient.restartJob(jobName, launchedJobs.get(jobName).getJobId()); - } catch (Exception e) { - throw new RuntimeException(e); - } - }); + doWithin(waitSec, "Restarting job " + jobName, () -> + getInfraClient().restartJob(jobName, launchedJobs.get(jobName).getJobInstanceId())); } @When("stop job $jobName after at least $count file exists in s3 with filename containing text $text within $waitSec seconds") public void stopJob(String jobName, int count, String text, int waitSec) throws Exception { S3Client s3Client = getS3client(); doWithin(waitSec, "check uploaded files to s3", () -> s3Client.listObjectKeys(text).size() > count); - - try (InfraClient httpClient = getInfraClient()) { - httpClient.stopJob(launchedJobs.get(jobName).getExecutionId()); - } + InfraClient infraClient = getInfraClient(); + infraClient.stopJob(launchedJobs.get(jobName).getJobExecutionId()); + doWithin(waitSec, String.format("Wait for job %s stops", jobName), () -> infraClient.isRunning(jobName)); } @When("delete file with key $key from s3") @@ -182,7 +174,7 @@ public class ExportJobsSteps extends AbstractInfraSteps { @Then("Check $count files exists on local filesystem with filenames containing the text $text in the folder $path for job $jobName") public void checkNumberOfFilesOnLocalFilesystem(long count, String text, String path, String jobName) { - File destinationDirectory = new File(getLocalDataFolder(), path.replace("${jobId}", launchedJobs.get(jobName).getJobId())); + File destinationDirectory = new File(getLocalDataFolder(), path.replace("${jobId}", Long.toString(launchedJobs.get(jobName).getJobInstanceId()))); logger.info("Destination directory path: {}", destinationDirectory.getAbsolutePath()); doWithin(5, "Destination directory exists", destinationDirectory::exists); @@ -216,7 +208,21 @@ public class ExportJobsSteps extends AbstractInfraSteps { } @AfterScenario - public void waitABit() throws InterruptedException { - Thread.sleep(5000); + public void waitForJobStops() throws InterruptedException { + InfraClient infraClient = getInfraClient(); + doWithin(20, "Stop all launched jobs", () -> { + int runningJobCount = 0; + for (String jobName : launchedJobs.keySet()) { + if (launchedJobs.get(jobName) == null) + continue; + if (!infraClient.isRunning(jobName)) { + launchedJobs.put(jobName, null); + } + else { + ++runningJobCount; + } + } + return runningJobCount == 0; + }); } } diff --git a/ambari-infra-manager-it/src/test/resources/log4j.properties b/ambari-infra-manager-it/src/test/resources/log4j2-test.properties similarity index 57% rename from ambari-infra-manager-it/src/test/resources/log4j.properties rename to ambari-infra-manager-it/src/test/resources/log4j2-test.properties index 956bc63..4e488fd 100644 --- a/ambari-infra-manager-it/src/test/resources/log4j.properties +++ b/ambari-infra-manager-it/src/test/resources/log4j2-test.properties @@ -9,8 +9,17 @@ # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. # See the License for the specific language governing permissions and # limitations under the License. -log4j.rootLogger=INFO, stdout -log4j.appender.stdout=org.apache.log4j.ConsoleAppender -log4j.appender.stdout.Target=System.out -log4j.appender.stdout.layout=org.apache.log4j.PatternLayout -log4j.appender.stdout.layout.ConversionPattern=%d{yyyy-MM-dd HH:mm:ss} %-5p %c{1}:%L - %m%n \ No newline at end of file + +status = error +dest = err +name = PropertiesConfig + +appender.console.type = Console +appender.console.name = STDOUT +appender.console.layout.type = PatternLayout +appender.console.layout.pattern = %d{yyyy-MM-dd HH:mm:ss} %-5p %c{1}:%L - %m%n +appender.console.filter.threshold.type = ThresholdFilter +appender.console.filter.threshold.level = info + +rootLogger.level = info +rootLogger.appenderRef.stdout.ref = STDOUT diff --git a/ambari-infra-manager/.gitignore b/ambari-infra-manager/.gitignore index 4aece78..dd3661e 100644 --- a/ambari-infra-manager/.gitignore +++ b/ambari-infra-manager/.gitignore @@ -3,4 +3,6 @@ out/* Profile .env test-out -test.db \ No newline at end of file +test.db +**/swagger/swagger.json +**/swagger/swagger.yaml \ No newline at end of file diff --git a/ambari-infra-manager/docs/api/swagger.yaml b/ambari-infra-manager/docs/api/swagger.yaml deleted file mode 100644 index 6fad22d..0000000 --- a/ambari-infra-manager/docs/api/swagger.yaml +++ /dev/null @@ -1,784 +0,0 @@ ---- -swagger: "2.0" -info: - description: "Manager component for Ambari Infra" - version: "1.0.0" - title: "Infra Manager REST API" - license: - name: "Apache 2.0" - url: "http://www.apache.org/licenses/LICENSE-2.0.html" -basePath: "/api/v1" -tags: -- name: "jobs" -schemes: -- "http" -- "https" -paths: - /jobs: - get: - tags: - - "jobs" - summary: "Get all jobs" - description: "" - operationId: "getAllJobs" - produces: - - "application/json" - parameters: - - name: "page" - in: "query" - required: false - type: "integer" - default: 0 - format: "int32" - - name: "size" - in: "query" - required: false - type: "integer" - default: 20 - format: "int32" - responses: - 200: - description: "successful operation" - schema: - type: "array" - items: - $ref: "#/definitions/JobInfo" - /jobs/executions: - delete: - tags: - - "jobs" - summary: "Stop all job executions." - description: "" - operationId: "stopAll" - produces: - - "application/json" - parameters: [] - responses: - 200: - description: "successful operation" - schema: - type: "integer" - format: "int32" - /jobs/executions/{jobExecutionId}: - get: - tags: - - "jobs" - summary: "Get job and step details for job execution instance." - description: "" - operationId: "getExecutionInfo" - produces: - - "application/json" - parameters: - - name: "jobExecutionId" - in: "path" - required: true - type: "integer" - format: "int64" - responses: - 200: - description: "successful operation" - schema: - $ref: "#/definitions/JobExecutionDetailsResponse" - delete: - tags: - - "jobs" - summary: "Stop or abandon a running job execution." - description: "" - operationId: "stopOrAbandonJobExecution" - produces: - - "application/json" - parameters: - - name: "jobExecutionId" - in: "path" - required: true - type: "integer" - format: "int64" - - name: "operation" - in: "query" - required: true - type: "string" - enum: - - "STOP" - - "ABANDON" - responses: - 200: - description: "successful operation" - schema: - $ref: "#/definitions/JobExecutionInfoResponse" - /jobs/executions/{jobExecutionId}/context: - get: - tags: - - "jobs" - summary: "Get execution context for specific job." - description: "" - operationId: "getExecutionContextByJobExecId" - produces: - - "application/json" - parameters: - - name: "jobExecutionId" - in: "path" - required: true - type: "integer" - format: "int64" - responses: - 200: - description: "successful operation" - schema: - $ref: "#/definitions/ExecutionContextResponse" - /jobs/executions/{jobExecutionId}/steps/{stepExecutionId}: - get: - tags: - - "jobs" - summary: "Get step execution details." - description: "" - operationId: "getStepExecution" - produces: - - "application/json" - parameters: - - name: "jobExecutionId" - in: "path" - required: true - type: "integer" - format: "int64" - - name: "stepExecutionId" - in: "path" - required: true - type: "integer" - format: "int64" - responses: - 200: - description: "successful operation" - schema: - $ref: "#/definitions/StepExecutionInfoResponse" - /jobs/executions/{jobExecutionId}/steps/{stepExecutionId}/execution-context: - get: - tags: - - "jobs" - summary: "Get the execution context of step execution." - description: "" - operationId: "getStepExecutionContext" - produces: - - "application/json" - parameters: - - name: "jobExecutionId" - in: "path" - required: true - type: "integer" - format: "int64" - - name: "stepExecutionId" - in: "path" - required: true - type: "integer" - format: "int64" - responses: - 200: - description: "successful operation" - schema: - $ref: "#/definitions/StepExecutionContextResponse" - /jobs/executions/{jobExecutionId}/steps/{stepExecutionId}/progress: - get: - tags: - - "jobs" - summary: "Get progress of step execution." - description: "" - operationId: "getStepExecutionProgress" - produces: - - "application/json" - parameters: - - name: "jobExecutionId" - in: "path" - required: true - type: "integer" - format: "int64" - - name: "stepExecutionId" - in: "path" - required: true - type: "integer" - format: "int64" - responses: - 200: - description: "successful operation" - schema: - $ref: "#/definitions/StepExecutionProgressResponse" - /jobs/info/names: - get: - tags: - - "jobs" - summary: "Get all job names" - description: "" - operationId: "getAllJobNames" - produces: - - "application/json" - parameters: [] - responses: - 200: - description: "successful operation" - schema: - type: "array" - uniqueItems: true - items: - type: "string" - /jobs/{jobName}: - post: - tags: - - "jobs" - summary: "Start a new job instance by job name." - description: "" - operationId: "startJob" - produces: - - "application/json" - parameters: - - name: "jobName" - in: "path" - required: true - type: "string" - - name: "params" - in: "query" - required: false - type: "string" - responses: - 200: - description: "successful operation" - schema: - $ref: "#/definitions/JobExecutionInfoResponse" - /jobs/{jobName}/executions: - get: - tags: - - "jobs" - summary: "Get the id values of all the running job instances." - description: "" - operationId: "getExecutionIdsByJobName" - produces: - - "application/json" - parameters: - - name: "jobName" - in: "path" - required: true - type: "string" - responses: - 200: - description: "successful operation" - schema: - type: "array" - uniqueItems: true - items: - type: "integer" - format: "int64" - /jobs/{jobName}/info: - get: - tags: - - "jobs" - summary: "Get job details by job name." - description: "" - operationId: "getJobDetails" - produces: - - "application/json" - parameters: - - name: "page" - in: "query" - required: false - type: "integer" - default: 0 - format: "int32" - - name: "size" - in: "query" - required: false - type: "integer" - default: 20 - format: "int32" - - name: "jobName" - in: "path" - required: true - type: "string" - responses: - 200: - description: "successful operation" - schema: - $ref: "#/definitions/JobDetailsResponse" - /jobs/{jobName}/{jobInstanceId}/executions: - get: - tags: - - "jobs" - summary: "Get execution for job instance." - description: "" - operationId: "getExecutionsForInstance" - produces: - - "application/json" - parameters: - - name: "jobName" - in: "path" - required: true - type: "string" - - name: "jobInstanceId" - in: "path" - required: true - type: "integer" - format: "int64" - responses: - 200: - description: "successful operation" - schema: - type: "array" - items: - $ref: "#/definitions/JobExecutionInfoResponse" - post: - tags: - - "jobs" - summary: "Restart job instance." - description: "" - operationId: "restartJobInstance" - produces: - - "application/json" - parameters: - - in: "body" - name: "body" - required: false - schema: - $ref: "#/definitions/JobExecutionRestartRequest" - responses: - 200: - description: "successful operation" - schema: - $ref: "#/definitions/JobExecutionInfoResponse" -definitions: - JobExecutionData: - type: "object" - properties: - id: - type: "integer" - format: "int64" - executionContext: - $ref: "#/definitions/ExecutionContext" - jobInstance: - $ref: "#/definitions/JobInstance" - jobId: - type: "integer" - format: "int64" - jobParameters: - $ref: "#/definitions/JobParameters" - failureExceptions: - type: "array" - items: - $ref: "#/definitions/Throwable" - endTime: - type: "string" - format: "date-time" - exitStatus: - $ref: "#/definitions/ExitStatus" - createTime: - type: "string" - format: "date-time" - lastUpdated: - type: "string" - format: "date-time" - jobConfigurationName: - type: "string" - startTime: - type: "string" - format: "date-time" - status: - type: "string" - enum: - - "COMPLETED" - - "STARTING" - - "STARTED" - - "STOPPING" - - "STOPPED" - - "FAILED" - - "ABANDONED" - - "UNKNOWN" - stepExecutionDataList: - type: "array" - items: - $ref: "#/definitions/StepExecutionData" - JobInstance: - type: "object" - properties: - id: - type: "integer" - format: "int64" - version: - type: "integer" - format: "int32" - jobName: - type: "string" - instanceId: - type: "integer" - format: "int64" - StepExecutionData: - type: "object" - properties: - id: - type: "integer" - format: "int64" - jobExecutionId: - type: "integer" - format: "int64" - executionContext: - $ref: "#/definitions/ExecutionContext" - stepName: - type: "string" - terminateOnly: - type: "boolean" - default: false - failureExceptions: - type: "array" - items: - $ref: "#/definitions/Throwable" - endTime: - type: "string" - format: "date-time" - exitStatus: - $ref: "#/definitions/ExitStatus" - lastUpdated: - type: "string" - format: "date-time" - commitCount: - type: "integer" - format: "int32" - readCount: - type: "integer" - format: "int32" - filterCount: - type: "integer" - format: "int32" - writeCount: - type: "integer" - format: "int32" - readSkipCount: - type: "integer" - format: "int32" - writeSkipCount: - type: "integer" - format: "int32" - processSkipCount: - type: "integer" - format: "int32" - rollbackCount: - type: "integer" - format: "int32" - startTime: - type: "string" - format: "date-time" - status: - type: "string" - enum: - - "COMPLETED" - - "STARTING" - - "STARTED" - - "STOPPING" - - "STOPPED" - - "FAILED" - - "ABANDONED" - - "UNKNOWN" - StackTraceElement: - type: "object" - properties: - methodName: - type: "string" - fileName: - type: "string" - lineNumber: - type: "integer" - format: "int32" - className: - type: "string" - nativeMethod: - type: "boolean" - default: false - JobExecutionDetailsResponse: - type: "object" - properties: - jobExecutionInfoResponse: - $ref: "#/definitions/JobExecutionInfoResponse" - stepExecutionInfoList: - type: "array" - items: - $ref: "#/definitions/StepExecutionInfoResponse" - StepExecutionContextResponse: - type: "object" - properties: - executionContextMap: - type: "object" - additionalProperties: - type: "object" - jobExecutionId: - type: "integer" - format: "int64" - stepExecutionId: - type: "integer" - format: "int64" - stepName: - type: "string" - StepExecutionProgress: - type: "object" - properties: - estimatedPercentCompleteMessage: - $ref: "#/definitions/MessageSourceResolvable" - estimatedPercentComplete: - type: "number" - format: "double" - ExitStatus: - type: "object" - properties: - exitCode: - type: "string" - exitDescription: - type: "string" - running: - type: "boolean" - default: false - ExecutionContextResponse: - type: "object" - properties: - jobExecutionId: - type: "integer" - format: "int64" - executionContextMap: - type: "object" - additionalProperties: - type: "object" - StepExecutionHistory: - type: "object" - properties: - stepName: - type: "string" - count: - type: "integer" - format: "int32" - commitCount: - $ref: "#/definitions/CumulativeHistory" - rollbackCount: - $ref: "#/definitions/CumulativeHistory" - readCount: - $ref: "#/definitions/CumulativeHistory" - writeCount: - $ref: "#/definitions/CumulativeHistory" - filterCount: - $ref: "#/definitions/CumulativeHistory" - readSkipCount: - $ref: "#/definitions/CumulativeHistory" - writeSkipCount: - $ref: "#/definitions/CumulativeHistory" - processSkipCount: - $ref: "#/definitions/CumulativeHistory" - duration: - $ref: "#/definitions/CumulativeHistory" - durationPerRead: - $ref: "#/definitions/CumulativeHistory" - TimeZone: - type: "object" - properties: - displayName: - type: "string" - id: - type: "string" - dstsavings: - type: "integer" - format: "int32" - rawOffset: - type: "integer" - format: "int32" - MessageSourceResolvable: - type: "object" - properties: - arguments: - type: "array" - items: - type: "object" - codes: - type: "array" - items: - type: "string" - defaultMessage: - type: "string" - ExecutionContext: - type: "object" - properties: - dirty: - type: "boolean" - default: false - empty: - type: "boolean" - default: false - StepExecutionInfoResponse: - type: "object" - properties: - id: - type: "integer" - format: "int64" - jobExecutionId: - type: "integer" - format: "int64" - jobName: - type: "string" - name: - type: "string" - startDate: - type: "string" - startTime: - type: "string" - duration: - type: "string" - durationMillis: - type: "integer" - format: "int64" - exitCode: - type: "string" - status: - type: "string" - JobExecutionInfoResponse: - type: "object" - properties: - id: - type: "integer" - format: "int64" - stepExecutionCount: - type: "integer" - format: "int32" - jobId: - type: "integer" - format: "int64" - jobName: - type: "string" - startDate: - type: "string" - startTime: - type: "string" - duration: - type: "string" - jobExecutionData: - $ref: "#/definitions/JobExecutionData" - jobParameters: - type: "object" - additionalProperties: - type: "object" - jobParametersString: - type: "string" - restartable: - type: "boolean" - default: false - abandonable: - type: "boolean" - default: false - stoppable: - type: "boolean" - default: false - timeZone: - $ref: "#/definitions/TimeZone" - JobInfo: - type: "object" - properties: - name: - type: "string" - executionCount: - type: "integer" - format: "int32" - launchable: - type: "boolean" - default: false - incrementable: - type: "boolean" - default: false - jobInstanceId: - type: "integer" - format: "int64" - JobExecutionRestartRequest: - type: "object" - properties: - jobName: - type: "string" - jobInstanceId: - type: "integer" - format: "int64" - operation: - type: "string" - enum: - - "RESTART" - Throwable: - type: "object" - properties: - cause: - $ref: "#/definitions/Throwable" - stackTrace: - type: "array" - items: - $ref: "#/definitions/StackTraceElement" - message: - type: "string" - localizedMessage: - type: "string" - suppressed: - type: "array" - items: - $ref: "#/definitions/Throwable" - JobParameters: - type: "object" - properties: - parameters: - type: "object" - additionalProperties: - $ref: "#/definitions/JobParameter" - empty: - type: "boolean" - default: false - CumulativeHistory: - type: "object" - properties: - count: - type: "integer" - format: "int32" - min: - type: "number" - format: "double" - max: - type: "number" - format: "double" - standardDeviation: - type: "number" - format: "double" - mean: - type: "number" - format: "double" - JobInstanceDetailsResponse: - type: "object" - properties: - jobInstance: - $ref: "#/definitions/JobInstance" - jobExecutionInfoResponseList: - type: "array" - items: - $ref: "#/definitions/JobExecutionInfoResponse" - JobParameter: - type: "object" - properties: - identifying: - type: "boolean" - default: false - value: - type: "object" - type: - type: "string" - enum: - - "STRING" - - "DATE" - - "LONG" - - "DOUBLE" - StepExecutionProgressResponse: - type: "object" - properties: - stepExecutionProgress: - $ref: "#/definitions/StepExecutionProgress" - stepExecutionHistory: - $ref: "#/definitions/StepExecutionHistory" - stepExecutionInfoResponse: - $ref: "#/definitions/StepExecutionInfoResponse" - JobDetailsResponse: - type: "object" - properties: - jobInfo: - $ref: "#/definitions/JobInfo" - jobInstanceDetailsResponseList: - type: "array" - items: - $ref: "#/definitions/JobInstanceDetailsResponse" diff --git a/ambari-infra-manager/pom.xml b/ambari-infra-manager/pom.xml index 722bf51..d595f99 100644 --- a/ambari-infra-manager/pom.xml +++ b/ambari-infra-manager/pom.xml @@ -30,18 +30,19 @@ <artifactId>ambari-infra-manager</artifactId> <properties> - <spring.version>5.1.1.RELEASE</spring.version> - <spring.security.version>5.1.1.RELEASE</spring.security.version> + <spring.version>5.1.3.RELEASE</spring.version> + <spring.security.version>5.1.2.RELEASE</spring.security.version> <spring.ldap.version>2.3.2.RELEASE</spring.ldap.version> <jersey.version>2.27</jersey.version> <jetty.version>9.4.11.v20180605</jetty.version> - <spring-batch.version>3.0.7.RELEASE</spring-batch.version> + <spring-batch.version>4.1.0.RELEASE</spring-batch.version> <sqlite.version>3.8.11.2</sqlite.version> <spring-data-solr.version>3.0.10.RELEASE</spring-data-solr.version> <spring-boot.version>2.0.6.RELEASE</spring-boot.version> <swagger.version>1.5.16</swagger.version> <jjwt.version>0.6.0</jjwt.version> <aws-sdk.version>1.11.445</aws-sdk.version> + <swagger-maven-plugin-version>3.1.7</swagger-maven-plugin-version> </properties> <build> @@ -117,6 +118,49 @@ </execution> </executions> </plugin> + <plugin> + <groupId>com.github.kongchen</groupId> + <artifactId>swagger-maven-plugin</artifactId> + <version>${swagger-maven-plugin-version}</version> + <configuration> + <apiSources> + <apiSource> + <springmvc>false</springmvc> + <locations>org.apache.ambari.infra.rest</locations> + <schemes> + <scheme>http</scheme> + <scheme>https</scheme> + </schemes> + <basePath>/api/v1</basePath> + <info> + <title>Infra Manager REST API</title> + <version>1.0.0</version> + <description>Ambari Infra Manager REST APIs has inherent support for querying, sorting and pagination</description> + <license> + <url>http://www.apache.org/licenses/LICENSE-2.0</url> + <name>Apache License, Version 2.0</name> + </license> + </info> + <outputFormats>yaml,json</outputFormats> + <swaggerDirectory>${project.basedir}/src/main/resources/swagger</swaggerDirectory> + <securityDefinitions> + <securityDefinition> + <name>basicAuth</name> + <type>basic</type> + </securityDefinition> + </securityDefinitions> + </apiSource> + </apiSources> + </configuration> + <executions> + <execution> + <phase>compile</phase> + <goals> + <goal>generate</goal> + </goals> + </execution> + </executions> + </plugin> </plugins> </build> diff --git a/ambari-infra-manager/src/main/java/org/apache/ambari/infra/conf/FsPermissionConverter.java b/ambari-infra-manager/src/main/java/org/apache/ambari/infra/conf/FsPermissionConverter.java deleted file mode 100644 index 5e794a7..0000000 --- a/ambari-infra-manager/src/main/java/org/apache/ambari/infra/conf/FsPermissionConverter.java +++ /dev/null @@ -1,37 +0,0 @@ -/* - * Licensed to the Apache Software Foundation (ASF) under one - * or more contributor license agreements. See the NOTICE file - * distributed with this work for additional information - * regarding copyright ownership. The ASF licenses this file - * to you under the Apache License, Version 2.0 (the - * "License"); you may not use this file except in compliance - * with the License. You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, - * software distributed under the License is distributed on an - * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY - * KIND, either express or implied. See the License for the - * specific language governing permissions and limitations - * under the License. - */ -package org.apache.ambari.infra.conf; - -import static org.apache.ambari.infra.json.StringToFsPermissionConverter.toFsPermission; - -import javax.annotation.Nullable; -import javax.inject.Named; - -import org.apache.hadoop.fs.permission.FsPermission; -import org.springframework.boot.context.properties.ConfigurationPropertiesBinding; -import org.springframework.core.convert.converter.Converter; - -@Named -@ConfigurationPropertiesBinding -public class FsPermissionConverter implements Converter<String, FsPermission> { - @Override - public FsPermission convert(@Nullable String s) { - return toFsPermission(s); - } -} diff --git a/ambari-infra-manager/src/main/java/org/apache/ambari/infra/conf/StaticResourceConfiguration.java b/ambari-infra-manager/src/main/java/org/apache/ambari/infra/conf/StaticResourceConfiguration.java index f0cd3cf..9651271 100644 --- a/ambari-infra-manager/src/main/java/org/apache/ambari/infra/conf/StaticResourceConfiguration.java +++ b/ambari-infra-manager/src/main/java/org/apache/ambari/infra/conf/StaticResourceConfiguration.java @@ -22,11 +22,11 @@ import org.springframework.context.annotation.Configuration; import org.springframework.web.servlet.config.annotation.EnableWebMvc; import org.springframework.web.servlet.config.annotation.ResourceHandlerRegistry; import org.springframework.web.servlet.config.annotation.ViewControllerRegistry; -import org.springframework.web.servlet.config.annotation.WebMvcConfigurerAdapter; +import org.springframework.web.servlet.config.annotation.WebMvcConfigurer; @EnableWebMvc @Configuration -public class StaticResourceConfiguration extends WebMvcConfigurerAdapter { +public class StaticResourceConfiguration implements WebMvcConfigurer { private static final String[] CLASSPATH_RESOURCE_LOCATIONS = { "classpath:/static/", "classpath:/swagger/","classpath:META-INF/resources/webjars/" diff --git a/ambari-infra-manager/src/main/java/org/apache/ambari/infra/doc/InfraManagerApiDocStorage.java b/ambari-infra-manager/src/main/java/org/apache/ambari/infra/doc/InfraManagerApiDocStorage.java deleted file mode 100644 index 5ababcc..0000000 --- a/ambari-infra-manager/src/main/java/org/apache/ambari/infra/doc/InfraManagerApiDocStorage.java +++ /dev/null @@ -1,92 +0,0 @@ -/* - * Licensed to the Apache Software Foundation (ASF) under one - * or more contributor license agreements. See the NOTICE file - * distributed with this work for additional information - * regarding copyright ownership. The ASF licenses this file - * to you under the Apache License, Version 2.0 (the - * "License"); you may not use this file except in compliance - * with the License. You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, - * software distributed under the License is distributed on an - * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY - * KIND, either express or implied. See the License for the - * specific language governing permissions and limitations - * under the License. - */ -package org.apache.ambari.infra.doc; - -import java.util.Map; -import java.util.concurrent.ConcurrentHashMap; - -import javax.annotation.PostConstruct; -import javax.inject.Inject; -import javax.inject.Named; - -import org.apache.logging.log4j.LogManager; -import org.apache.logging.log4j.Logger; - -import io.swagger.jaxrs.config.BeanConfig; -import io.swagger.models.Swagger; -import io.swagger.models.auth.BasicAuthDefinition; -import io.swagger.util.Yaml; - -@Named -public class InfraManagerApiDocStorage { - - private static final Logger logger = LogManager.getLogger(InfraManagerApiDocStorage.class); - - private final Map<String, Object> swaggerMap = new ConcurrentHashMap<>(); - - @Inject - private BeanConfig beanConfig; - - @PostConstruct - private void postConstruct() { - Thread loadApiDocThread = new Thread("load_swagger_api_doc") { - @Override - public void run() { - logger.info("Start thread to scan REST API doc from endpoints."); - Swagger swagger = beanConfig.getSwagger(); - swagger.addSecurityDefinition("basicAuth", new BasicAuthDefinition()); - beanConfig.configure(swagger); - beanConfig.scanAndRead(); - setSwagger(swagger); - try { - String yaml = Yaml.mapper().writeValueAsString(swagger); - StringBuilder b = new StringBuilder(); - String[] parts = yaml.split("\n"); - for (String part : parts) { - b.append(part); - b.append("\n"); - } - setSwaggerYaml(b.toString()); - } catch (Exception e) { - e.printStackTrace(); - } - logger.info("Scanning REST API endpoints and generating docs has been successful."); - } - }; - loadApiDocThread.setDaemon(true); - loadApiDocThread.start(); - } - - public Swagger getSwagger() { - return (Swagger) swaggerMap.get("swaggerObject"); - } - - public void setSwagger(final Swagger swagger) { - swaggerMap.put("swaggerObject", swagger); - } - - public void setSwaggerYaml(final String swaggerYaml) { - swaggerMap.put("swaggerYaml", swaggerYaml); - } - - public String getSwaggerYaml() { - return (String) swaggerMap.get("swaggerYaml"); - } - -} diff --git a/ambari-infra-manager/src/main/java/org/apache/ambari/infra/job/archive/DocumentExporter.java b/ambari-infra-manager/src/main/java/org/apache/ambari/infra/job/archive/DocumentExporter.java index f61746d..8c4a067 100644 --- a/ambari-infra-manager/src/main/java/org/apache/ambari/infra/job/archive/DocumentExporter.java +++ b/ambari-infra-manager/src/main/java/org/apache/ambari/infra/job/archive/DocumentExporter.java @@ -21,7 +21,6 @@ package org.apache.ambari.infra.job.archive; import org.apache.ambari.infra.job.JobContextRepository; import org.apache.logging.log4j.LogManager; import org.apache.logging.log4j.Logger; -import org.springframework.batch.core.BatchStatus; import org.springframework.batch.core.ExitStatus; import org.springframework.batch.core.StepContribution; import org.springframework.batch.core.StepExecution; @@ -31,6 +30,7 @@ import org.springframework.batch.core.step.tasklet.Tasklet; import org.springframework.batch.item.ExecutionContext; import org.springframework.batch.item.ItemStreamReader; import org.springframework.batch.repeat.RepeatStatus; +import org.springframework.lang.NonNull; public class DocumentExporter implements Tasklet, StepExecutionListener { @@ -50,12 +50,12 @@ public class DocumentExporter implements Tasklet, StepExecutionListener { } @Override - public void beforeStep(StepExecution stepExecution) { + public void beforeStep(@NonNull StepExecution stepExecution) { } @Override - public ExitStatus afterStep(StepExecution stepExecution) { + public ExitStatus afterStep(@NonNull StepExecution stepExecution) { if (complete) { return ExitStatus.COMPLETED; } @@ -65,7 +65,7 @@ public class DocumentExporter implements Tasklet, StepExecutionListener { } @Override - public RepeatStatus execute(StepContribution contribution, ChunkContext chunkContext) throws Exception { + public RepeatStatus execute(@NonNull StepContribution contribution, @NonNull ChunkContext chunkContext) throws Exception { StepExecution stepExecution = chunkContext.getStepContext().getStepExecution(); ExecutionContext executionContext = stepExecution.getExecutionContext(); documentReader.open(executionContext); @@ -77,7 +77,7 @@ public class DocumentExporter implements Tasklet, StepExecutionListener { while ((document = documentReader.read()) != null) { if (writer != null && writtenCount >= writeBlockSize) { stepExecution = jobContextRepository.getStepExecution(stepExecution.getJobExecutionId(), stepExecution.getId()); - if (stepExecution.getJobExecution().getStatus() == BatchStatus.STOPPING) { + if (stepExecution.isTerminateOnly()) { logger.info("Received stop signal."); writer.revert(); writer = null; diff --git a/ambari-infra-manager/src/main/java/org/apache/ambari/infra/job/cleanup/TaskHistoryWiper.java b/ambari-infra-manager/src/main/java/org/apache/ambari/infra/job/cleanup/TaskHistoryWiper.java index 5a296de..2627f4c 100644 --- a/ambari-infra-manager/src/main/java/org/apache/ambari/infra/job/cleanup/TaskHistoryWiper.java +++ b/ambari-infra-manager/src/main/java/org/apache/ambari/infra/job/cleanup/TaskHistoryWiper.java @@ -28,20 +28,21 @@ import org.springframework.batch.core.StepContribution; import org.springframework.batch.core.scope.context.ChunkContext; import org.springframework.batch.core.step.tasklet.Tasklet; import org.springframework.batch.repeat.RepeatStatus; +import org.springframework.lang.NonNull; public class TaskHistoryWiper implements Tasklet { private static final Logger logger = LogManager.getLogger(TaskHistoryWiper.class); - public static final Duration DEFAULT_TTL = Duration.ofHours(1); + private static final Duration MINIMUM_TTL = Duration.ofHours(1); private final InfraJobExecutionDao infraJobExecutionDao; private final Duration ttl; public TaskHistoryWiper(InfraJobExecutionDao infraJobExecutionDao, Duration ttl) { this.infraJobExecutionDao = infraJobExecutionDao; - if (ttl == null || ttl.compareTo(DEFAULT_TTL) < 0) { - logger.info("The ttl value ({}) less than the minimum required. Using the default ({}) instead", ttl, DEFAULT_TTL); - this.ttl = DEFAULT_TTL; + if (ttl == null || ttl.compareTo(MINIMUM_TTL) < 0) { + logger.info("The ttl value ({}) less than the minimum required. Using the minimum ({}) instead", ttl, MINIMUM_TTL); + this.ttl = MINIMUM_TTL; } else { this.ttl = ttl; @@ -49,7 +50,7 @@ public class TaskHistoryWiper implements Tasklet { } @Override - public RepeatStatus execute(StepContribution contribution, ChunkContext chunkContext) { + public RepeatStatus execute(@NonNull StepContribution contribution, @NonNull ChunkContext chunkContext) { infraJobExecutionDao.deleteJobExecutions(OffsetDateTime.now().minus(ttl)); return RepeatStatus.FINISHED; } diff --git a/ambari-infra-manager/src/main/java/org/apache/ambari/infra/job/deleting/DocumentWiperTasklet.java b/ambari-infra-manager/src/main/java/org/apache/ambari/infra/job/deleting/DocumentWiperTasklet.java index cae64f4..9bde32a 100644 --- a/ambari-infra-manager/src/main/java/org/apache/ambari/infra/job/deleting/DocumentWiperTasklet.java +++ b/ambari-infra-manager/src/main/java/org/apache/ambari/infra/job/deleting/DocumentWiperTasklet.java @@ -26,6 +26,7 @@ import org.springframework.batch.core.StepContribution; import org.springframework.batch.core.scope.context.ChunkContext; import org.springframework.batch.core.step.tasklet.Tasklet; import org.springframework.batch.repeat.RepeatStatus; +import org.springframework.lang.NonNull; public class DocumentWiperTasklet extends SolrDAOBase implements Tasklet { private final DeletingProperties parameters; @@ -36,7 +37,7 @@ public class DocumentWiperTasklet extends SolrDAOBase implements Tasklet { } @Override - public RepeatStatus execute(StepContribution contribution, ChunkContext chunkContext) { + public RepeatStatus execute(@NonNull StepContribution contribution, @NonNull ChunkContext chunkContext) { delete(String.format("%s:[%s TO %s]", parameters.getFilterField(), getValue(parameters.getStart()), diff --git a/ambari-infra-manager/src/main/java/org/apache/ambari/infra/conf/DurationConverter.java b/ambari-infra-manager/src/main/java/org/apache/ambari/infra/json/OffsetDateTimeToStringConverter.java similarity index 60% rename from ambari-infra-manager/src/main/java/org/apache/ambari/infra/conf/DurationConverter.java rename to ambari-infra-manager/src/main/java/org/apache/ambari/infra/json/OffsetDateTimeToStringConverter.java index 93b4794..25bdf19 100644 --- a/ambari-infra-manager/src/main/java/org/apache/ambari/infra/conf/DurationConverter.java +++ b/ambari-infra-manager/src/main/java/org/apache/ambari/infra/json/OffsetDateTimeToStringConverter.java @@ -16,23 +16,17 @@ * specific language governing permissions and limitations * under the License. */ -package org.apache.ambari.infra.conf; +package org.apache.ambari.infra.json; -import static org.apache.ambari.infra.json.StringToDurationConverter.toDuration; +import java.time.OffsetDateTime; +import java.time.format.DateTimeFormatter; -import java.time.Duration; +import com.fasterxml.jackson.databind.util.StdConverter; -import javax.annotation.Nullable; -import javax.inject.Named; +public class OffsetDateTimeToStringConverter extends StdConverter<OffsetDateTime, String> { -import org.springframework.boot.context.properties.ConfigurationPropertiesBinding; -import org.springframework.core.convert.converter.Converter; - -@Named -@ConfigurationPropertiesBinding -public class DurationConverter implements Converter<String, Duration> { @Override - public Duration convert(@Nullable String s) { - return toDuration(s); + public String convert(OffsetDateTime value) { + return DateTimeFormatter.ISO_OFFSET_DATE_TIME.format(value); } } diff --git a/ambari-infra-manager/src/main/java/org/apache/ambari/infra/json/StringToDurationConverter.java b/ambari-infra-manager/src/main/java/org/apache/ambari/infra/json/StringToDurationConverter.java index 2a385cf..4746048 100644 --- a/ambari-infra-manager/src/main/java/org/apache/ambari/infra/json/StringToDurationConverter.java +++ b/ambari-infra-manager/src/main/java/org/apache/ambari/infra/json/StringToDurationConverter.java @@ -20,11 +20,19 @@ package org.apache.ambari.infra.json; import java.time.Duration; +import javax.inject.Named; + +import org.springframework.boot.context.properties.ConfigurationPropertiesBinding; +import org.springframework.core.convert.converter.Converter; +import org.springframework.lang.NonNull; + import com.fasterxml.jackson.databind.util.StdConverter; -public class StringToDurationConverter extends StdConverter<String, Duration> { +@Named +@ConfigurationPropertiesBinding +public class StringToDurationConverter extends StdConverter<String, Duration> implements Converter<String, Duration> { @Override - public Duration convert(String value) { + public Duration convert(@NonNull String value) { return toDuration(value); } diff --git a/ambari-infra-manager/src/main/java/org/apache/ambari/infra/json/StringToFsPermissionConverter.java b/ambari-infra-manager/src/main/java/org/apache/ambari/infra/json/StringToFsPermissionConverter.java index 70e4902..e271346 100644 --- a/ambari-infra-manager/src/main/java/org/apache/ambari/infra/json/StringToFsPermissionConverter.java +++ b/ambari-infra-manager/src/main/java/org/apache/ambari/infra/json/StringToFsPermissionConverter.java @@ -20,13 +20,20 @@ package org.apache.ambari.infra.json; import static org.apache.commons.lang.StringUtils.isBlank; +import javax.inject.Named; + import org.apache.hadoop.fs.permission.FsPermission; +import org.springframework.boot.context.properties.ConfigurationPropertiesBinding; +import org.springframework.core.convert.converter.Converter; +import org.springframework.lang.NonNull; import com.fasterxml.jackson.databind.util.StdConverter; -public class StringToFsPermissionConverter extends StdConverter<String, FsPermission> { +@Named +@ConfigurationPropertiesBinding +public class StringToFsPermissionConverter extends StdConverter<String, FsPermission> implements Converter<String, FsPermission> { @Override - public FsPermission convert(String value) { + public FsPermission convert(@NonNull String value) { return toFsPermission(value); } diff --git a/ambari-infra-manager/src/main/java/org/apache/ambari/infra/manager/JobManager.java b/ambari-infra-manager/src/main/java/org/apache/ambari/infra/manager/JobManager.java index 86ffc1a..7886452 100644 --- a/ambari-infra-manager/src/main/java/org/apache/ambari/infra/manager/JobManager.java +++ b/ambari-infra-manager/src/main/java/org/apache/ambari/infra/manager/JobManager.java @@ -18,6 +18,8 @@ */ package org.apache.ambari.infra.manager; +import static java.util.Collections.unmodifiableList; + import java.util.ArrayList; import java.util.Collection; import java.util.Comparator; @@ -26,13 +28,11 @@ import java.util.List; import java.util.Map; import java.util.Optional; import java.util.Set; -import java.util.TimeZone; import javax.inject.Inject; import javax.inject.Named; import org.apache.ambari.infra.model.ExecutionContextResponse; -import org.apache.ambari.infra.model.JobDetailsResponse; import org.apache.ambari.infra.model.JobExecutionDetailsResponse; import org.apache.ambari.infra.model.JobExecutionInfoResponse; import org.apache.ambari.infra.model.JobInstanceDetailsResponse; @@ -78,8 +78,6 @@ public class JobManager implements Jobs { @Inject private JobExplorer jobExplorer; - private TimeZone timeZone = TimeZone.getDefault(); - public Set<String> getAllJobNames() { return jobOperator.getJobNames(); } @@ -97,7 +95,7 @@ public class JobManager implements Jobs { if (!running.isEmpty()) throw new JobExecutionAlreadyRunningException("An instance of this job is already active: "+jobName); - return new JobExecutionInfoResponse(jobService.launch(jobName, jobParameters), timeZone); + return new JobExecutionInfoResponse(jobService.launch(jobName, jobParameters)); } @Override @@ -143,10 +141,10 @@ public class JobManager implements Jobs { JobExecution jobExecution = jobService.getJobExecution(jobExecutionId); List<StepExecutionInfoResponse> stepExecutionInfoList = new ArrayList<>(); for (StepExecution stepExecution : jobExecution.getStepExecutions()) { - stepExecutionInfoList.add(new StepExecutionInfoResponse(stepExecution, timeZone)); + stepExecutionInfoList.add(new StepExecutionInfoResponse(stepExecution)); } - stepExecutionInfoList.sort(Comparator.comparing(StepExecutionInfoResponse::getId)); - return new JobExecutionDetailsResponse(new JobExecutionInfoResponse(jobExecution, timeZone), stepExecutionInfoList); + stepExecutionInfoList.sort(Comparator.comparing(StepExecutionInfoResponse::getStepExecutionId)); + return new JobExecutionDetailsResponse(new JobExecutionInfoResponse(jobExecution), stepExecutionInfoList); } /** @@ -163,7 +161,7 @@ public class JobManager implements Jobs { throw new UnsupportedOperationException("Unsupported operaration"); } logger.info("Job {} was marked {}", jobExecution.getJobInstance().getJobName(), operation.name()); - return new JobExecutionInfoResponse(jobExecution, timeZone); + return new JobExecutionInfoResponse(jobExecution); } /** @@ -188,7 +186,7 @@ public class JobManager implements Jobs { Collection<JobExecution> jobExecutions = jobService.getJobExecutionsForJobInstance(jobName, jobInstanceId); JobExecution jobExecution = jobExecutions.iterator().next(); Long jobExecutionId = jobExecution.getId(); - return new JobExecutionInfoResponse(jobService.restart(jobExecutionId), timeZone); + return new JobExecutionInfoResponse(jobService.restart(jobExecutionId)); } else { throw new UnsupportedOperationException("Unsupported operation (try: RESTART)"); } @@ -223,7 +221,7 @@ public class JobManager implements Jobs { JobInstance jobInstance = jobService.getJobInstance(jobInstanceId); Collection<JobExecution> jobExecutions = jobService.getJobExecutionsForJobInstance(jobName, jobInstance.getInstanceId()); for (JobExecution jobExecution : jobExecutions) { - result.add(new JobExecutionInfoResponse(jobExecution, timeZone)); + result.add(new JobExecutionInfoResponse(jobExecution)); } return result; } @@ -231,25 +229,26 @@ public class JobManager implements Jobs { /** * Get job details for a specific job. (paged) */ - public JobDetailsResponse getJobDetails(String jobName, int page, int size) throws NoSuchJobException { + public List<JobInstanceDetailsResponse> getJobDetails(String jobName, int page, int size) throws NoSuchJobException { List<JobInstanceDetailsResponse> jobInstanceResponses = Lists.newArrayList(); Collection<JobInstance> jobInstances = jobService.listJobInstances(jobName, page, size); - int count = jobService.countJobExecutionsForJob(jobName); boolean launchable = jobService.isLaunchable(jobName); - boolean isIncrementable = jobService.isIncrementable(jobName); + boolean incrementable = jobService.isIncrementable(jobName); for (JobInstance jobInstance: jobInstances) { - List<JobExecutionInfoResponse> executionInfos = Lists.newArrayList(); + List<JobExecutionInfoResponse> executionInfoResponses = Lists.newArrayList(); Collection<JobExecution> jobExecutions = jobService.getJobExecutionsForJobInstance(jobName, jobInstance.getId()); if (jobExecutions != null) { for (JobExecution jobExecution : jobExecutions) { - executionInfos.add(new JobExecutionInfoResponse(jobExecution, timeZone)); + executionInfoResponses.add(new JobExecutionInfoResponse(jobExecution)); } } - jobInstanceResponses.add(new JobInstanceDetailsResponse(jobInstance, executionInfos)); + jobInstanceResponses.add(new JobInstanceDetailsResponse( + new JobInfo(jobName, executionInfoResponses.size(), jobInstance.getInstanceId(), launchable, incrementable), + executionInfoResponses)); } - return new JobDetailsResponse(new JobInfo(jobName, count, launchable, isIncrementable), jobInstanceResponses); + return unmodifiableList(jobInstanceResponses); } /** @@ -257,7 +256,7 @@ public class JobManager implements Jobs { */ public StepExecutionInfoResponse getStepExecution(Long jobExecutionId, Long stepExecutionId) throws NoSuchStepExecutionException, NoSuchJobExecutionException { StepExecution stepExecution = jobService.getStepExecution(jobExecutionId, stepExecutionId); - return new StepExecutionInfoResponse(stepExecution, timeZone); + return new StepExecutionInfoResponse(stepExecution); } /** @@ -277,7 +276,7 @@ public class JobManager implements Jobs { */ public StepExecutionProgressResponse getStepExecutionProgress(Long jobExecutionId, Long stepExecutionId) throws NoSuchStepExecutionException, NoSuchJobExecutionException { StepExecution stepExecution = jobService.getStepExecution(jobExecutionId, stepExecutionId); - StepExecutionInfoResponse stepExecutionInfoResponse = new StepExecutionInfoResponse(stepExecution, timeZone); + StepExecutionInfoResponse stepExecutionInfoResponse = new StepExecutionInfoResponse(stepExecution); String stepName = stepExecution.getStepName(); if (stepName.contains(":partition")) { stepName = stepName.replaceAll("(:partition).*", "$1*"); diff --git a/ambari-infra-manager/src/main/java/org/apache/ambari/infra/json/StringToDurationConverter.java b/ambari-infra-manager/src/main/java/org/apache/ambari/infra/model/DateUtil.java similarity index 66% copy from ambari-infra-manager/src/main/java/org/apache/ambari/infra/json/StringToDurationConverter.java copy to ambari-infra-manager/src/main/java/org/apache/ambari/infra/model/DateUtil.java index 2a385cf..810a95b 100644 --- a/ambari-infra-manager/src/main/java/org/apache/ambari/infra/json/StringToDurationConverter.java +++ b/ambari-infra-manager/src/main/java/org/apache/ambari/infra/model/DateUtil.java @@ -16,19 +16,16 @@ * specific language governing permissions and limitations * under the License. */ -package org.apache.ambari.infra.json; +package org.apache.ambari.infra.model; -import java.time.Duration; +import java.time.OffsetDateTime; +import java.time.ZoneOffset; +import java.util.Date; -import com.fasterxml.jackson.databind.util.StdConverter; - -public class StringToDurationConverter extends StdConverter<String, Duration> { - @Override - public Duration convert(String value) { - return toDuration(value); - } - - public static Duration toDuration(String value) { - return value == null ? null : Duration.parse(value); +public class DateUtil { + public static OffsetDateTime toOffsetDateTime(Date date) { + if (date == null) + return null; + return date.toInstant().atOffset(ZoneOffset.UTC); } } diff --git a/ambari-infra-manager/src/main/java/org/apache/ambari/infra/model/ExecutionContextResponse.java b/ambari-infra-manager/src/main/java/org/apache/ambari/infra/model/ExecutionContextResponse.java index 2d46c54..0eb1a16 100644 --- a/ambari-infra-manager/src/main/java/org/apache/ambari/infra/model/ExecutionContextResponse.java +++ b/ambari-infra-manager/src/main/java/org/apache/ambari/infra/model/ExecutionContextResponse.java @@ -18,6 +18,8 @@ */ package org.apache.ambari.infra.model; +import static java.util.Collections.unmodifiableMap; + import java.util.Map; public class ExecutionContextResponse { @@ -27,7 +29,7 @@ public class ExecutionContextResponse { public ExecutionContextResponse(Long jobExecutionId, Map<String, Object> executionContextMap) { this.jobExecutionId = jobExecutionId; - this.executionContextMap = executionContextMap; + this.executionContextMap = unmodifiableMap(executionContextMap); } public Long getJobExecutionId() { diff --git a/ambari-infra-manager/src/main/java/org/apache/ambari/infra/model/ExecutionContextResponse.java b/ambari-infra-manager/src/main/java/org/apache/ambari/infra/model/ISO8601DateFormatter.java similarity index 52% copy from ambari-infra-manager/src/main/java/org/apache/ambari/infra/model/ExecutionContextResponse.java copy to ambari-infra-manager/src/main/java/org/apache/ambari/infra/model/ISO8601DateFormatter.java index 2d46c54..5f945b6 100644 --- a/ambari-infra-manager/src/main/java/org/apache/ambari/infra/model/ExecutionContextResponse.java +++ b/ambari-infra-manager/src/main/java/org/apache/ambari/infra/model/ISO8601DateFormatter.java @@ -6,9 +6,9 @@ * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at - * + * * http://www.apache.org/licenses/LICENSE-2.0 - * + * * Unless required by applicable law or agreed to in writing, * software distributed under the License is distributed on an * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY @@ -18,23 +18,24 @@ */ package org.apache.ambari.infra.model; -import java.util.Map; +import java.text.DateFormat; +import java.text.FieldPosition; +import java.text.ParsePosition; +import java.time.OffsetDateTime; +import java.time.format.DateTimeFormatter; +import java.util.Date; -public class ExecutionContextResponse { - - private final Long jobExecutionId; - private final Map<String, Object> executionContextMap; - - public ExecutionContextResponse(Long jobExecutionId, Map<String, Object> executionContextMap) { - this.jobExecutionId = jobExecutionId; - this.executionContextMap = executionContextMap; - } - - public Long getJobExecutionId() { - return jobExecutionId; +public class ISO8601DateFormatter extends DateFormat { + @Override + public StringBuffer format(Date date, StringBuffer toAppendTo, FieldPosition fieldPosition) { + toAppendTo.append(DateTimeFormatter.ISO_OFFSET_DATE_TIME.format(DateUtil.toOffsetDateTime(date))); + return toAppendTo; } - public Map<String, Object> getExecutionContextMap() { - return executionContextMap; + @Override + public Date parse(String source, ParsePosition pos) { + OffsetDateTime offsetDateTime = OffsetDateTime.parse(source, DateTimeFormatter.ISO_OFFSET_DATE_TIME); + pos.setIndex(pos.getIndex() + source.length()); + return Date.from(offsetDateTime.toInstant()); } } diff --git a/ambari-infra-manager/src/main/java/org/apache/ambari/infra/model/JobDetailsResponse.java b/ambari-infra-manager/src/main/java/org/apache/ambari/infra/model/JobDetailsResponse.java deleted file mode 100644 index cd34fef..0000000 --- a/ambari-infra-manager/src/main/java/org/apache/ambari/infra/model/JobDetailsResponse.java +++ /dev/null @@ -1,53 +0,0 @@ -/* - * Licensed to the Apache Software Foundation (ASF) under one - * or more contributor license agreements. See the NOTICE file - * distributed with this work for additional information - * regarding copyright ownership. The ASF licenses this file - * to you under the Apache License, Version 2.0 (the - * "License"); you may not use this file except in compliance - * with the License. You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, - * software distributed under the License is distributed on an - * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY - * KIND, either express or implied. See the License for the - * specific language governing permissions and limitations - * under the License. - */ -package org.apache.ambari.infra.model; - -import org.springframework.batch.admin.web.JobInfo; - -import java.util.List; - -public class JobDetailsResponse { - - private JobInfo jobInfo; - private List<JobInstanceDetailsResponse> jobInstanceDetailsResponseList; - - public JobDetailsResponse() { - } - - public JobDetailsResponse(JobInfo jobInfo, List<JobInstanceDetailsResponse> jobInstanceDetailsResponseList) { - this.jobInfo = jobInfo; - this.jobInstanceDetailsResponseList = jobInstanceDetailsResponseList; - } - - public JobInfo getJobInfo() { - return jobInfo; - } - - public void setJobInfo(JobInfo jobInfo) { - this.jobInfo = jobInfo; - } - - public List<JobInstanceDetailsResponse> getJobInstanceDetailsResponseList() { - return jobInstanceDetailsResponseList; - } - - public void setJobInstanceDetailsResponseList(List<JobInstanceDetailsResponse> jobInstanceDetailsResponseList) { - this.jobInstanceDetailsResponseList = jobInstanceDetailsResponseList; - } -} diff --git a/ambari-infra-manager/src/main/java/org/apache/ambari/infra/model/JobExecutionDetailsResponse.java b/ambari-infra-manager/src/main/java/org/apache/ambari/infra/model/JobExecutionDetailsResponse.java index 695b57f..9e19344 100644 --- a/ambari-infra-manager/src/main/java/org/apache/ambari/infra/model/JobExecutionDetailsResponse.java +++ b/ambari-infra-manager/src/main/java/org/apache/ambari/infra/model/JobExecutionDetailsResponse.java @@ -18,32 +18,30 @@ */ package org.apache.ambari.infra.model; +import static java.util.Collections.unmodifiableList; + import java.util.List; +import com.fasterxml.jackson.annotation.JsonProperty; + public class JobExecutionDetailsResponse { - private JobExecutionInfoResponse jobExecutionInfoResponse; + @JsonProperty("jobExecution") + private final JobExecutionInfoResponse jobExecutionInfoResponse; - private List<StepExecutionInfoResponse> stepExecutionInfoList; + @JsonProperty("stepExecutionList") + private final List<StepExecutionInfoResponse> stepExecutionInfoList; public JobExecutionDetailsResponse(JobExecutionInfoResponse jobExecutionInfoResponse, List<StepExecutionInfoResponse> stepExecutionInfoList) { this.jobExecutionInfoResponse = jobExecutionInfoResponse; - this.stepExecutionInfoList = stepExecutionInfoList; + this.stepExecutionInfoList = unmodifiableList(stepExecutionInfoList); } public JobExecutionInfoResponse getJobExecutionInfoResponse() { return jobExecutionInfoResponse; } - public void setJobExecutionInfoResponse(JobExecutionInfoResponse jobExecutionInfoResponse) { - this.jobExecutionInfoResponse = jobExecutionInfoResponse; - } - public List<StepExecutionInfoResponse> getStepExecutionInfoList() { return stepExecutionInfoList; } - - public void setStepExecutionInfoList(List<StepExecutionInfoResponse> stepExecutionInfoList) { - this.stepExecutionInfoList = stepExecutionInfoList; - } } diff --git a/ambari-infra-manager/src/main/java/org/apache/ambari/infra/model/JobExecutionInfoResponse.java b/ambari-infra-manager/src/main/java/org/apache/ambari/infra/model/JobExecutionInfoResponse.java index a7e4a4f..1034eb8 100644 --- a/ambari-infra-manager/src/main/java/org/apache/ambari/infra/model/JobExecutionInfoResponse.java +++ b/ambari-infra-manager/src/main/java/org/apache/ambari/infra/model/JobExecutionInfoResponse.java @@ -18,111 +18,122 @@ */ package org.apache.ambari.infra.model; -import org.apache.ambari.infra.model.wrapper.JobExecutionData; -import org.springframework.batch.admin.web.JobParametersExtractor; +import static java.util.Collections.unmodifiableList; +import static org.apache.ambari.infra.model.DateUtil.toOffsetDateTime; + +import java.time.Duration; +import java.time.OffsetDateTime; +import java.util.List; +import java.util.Properties; + +import org.apache.ambari.infra.json.DurationToStringConverter; +import org.apache.ambari.infra.json.OffsetDateTimeToStringConverter; import org.springframework.batch.core.BatchStatus; import org.springframework.batch.core.JobExecution; import org.springframework.batch.core.JobInstance; import org.springframework.batch.core.converter.DefaultJobParametersConverter; -import org.springframework.batch.core.converter.JobParametersConverter; -import java.text.SimpleDateFormat; -import java.util.Date; -import java.util.Properties; -import java.util.TimeZone; +import com.fasterxml.jackson.databind.annotation.JsonSerialize; + +import io.swagger.annotations.ApiModelProperty; public class JobExecutionInfoResponse { - private Long id; - private int stepExecutionCount; - private Long jobId; - private String jobName; - private String startDate = ""; - private String startTime = ""; - private String duration = ""; - private JobExecutionData jobExecutionData; - private Properties jobParameters; - private String jobParametersString; - private boolean restartable = false; - private boolean abandonable = false; - private boolean stoppable = false; - private final TimeZone timeZone; - - - public JobExecutionInfoResponse(JobExecution jobExecution, TimeZone timeZone) { - JobParametersConverter converter = new DefaultJobParametersConverter(); - this.jobExecutionData = new JobExecutionData(jobExecution); - this.timeZone = timeZone; - this.id = jobExecutionData.getId(); - this.jobId = jobExecutionData.getJobId(); - this.stepExecutionCount = jobExecutionData.getStepExecutions().size(); - this.jobParameters = converter.getProperties(jobExecutionData.getJobParameters()); - this.jobParametersString = (new JobParametersExtractor()).fromJobParameters(jobExecutionData.getJobParameters()); - JobInstance jobInstance = jobExecutionData.getJobInstance(); + private static final DefaultJobParametersConverter DEFAULT_JOB_PARAMETERS_CONVERTER = new DefaultJobParametersConverter(); + + static { + DEFAULT_JOB_PARAMETERS_CONVERTER.setDateFormat(new ISO8601DateFormatter()); + } + + private final Long jobExecutionId; + private final Long jobInstanceId; + private final String jobName; + @JsonSerialize(converter = OffsetDateTimeToStringConverter.class) + private final OffsetDateTime creationTime; + @JsonSerialize(converter = OffsetDateTimeToStringConverter.class) + private final OffsetDateTime startTime; + @JsonSerialize(converter = OffsetDateTimeToStringConverter.class) + private final OffsetDateTime lastUpdatedTime; + @JsonSerialize(converter = OffsetDateTimeToStringConverter.class) + private final OffsetDateTime endTime; + @JsonSerialize(converter = DurationToStringConverter.class) + @ApiModelProperty(dataType = "java.lang.String", example = "PT5.311S") + private final Duration duration; + private final Properties jobParameters; + private final BatchStatus batchStatus; + @ApiModelProperty(example = "COMPLETED", allowableValues = "UNKNOWN, EXECUTING, COMPLETED, NOOP, FAILED, STOPPED") + private final String exitCode; + private final String exitDescription; + private final boolean restartable; + private final boolean abandonable; + private final boolean stoppable; + private final List<Throwable> failureExceptions; + private final String jobConfigurationName; + + + public JobExecutionInfoResponse(JobExecution jobExecution) { + this.jobExecutionId = jobExecution.getId(); + this.jobInstanceId = jobExecution.getJobId(); + this.jobParameters = DEFAULT_JOB_PARAMETERS_CONVERTER.getProperties(jobExecution.getJobParameters()); + this.creationTime = toOffsetDateTime(jobExecution.getCreateTime()); + this.startTime = toOffsetDateTime(jobExecution.getStartTime()); + this.lastUpdatedTime = toOffsetDateTime(jobExecution.getLastUpdated()); + this.endTime = toOffsetDateTime(jobExecution.getEndTime()); + JobInstance jobInstance = jobExecution.getJobInstance(); + this.batchStatus = jobExecution.getStatus(); + this.restartable = batchStatus.isGreaterThan(BatchStatus.STOPPING) && batchStatus.isLessThan(BatchStatus.ABANDONED); + this.abandonable = batchStatus.isGreaterThan(BatchStatus.STARTED) && batchStatus != BatchStatus.ABANDONED; + this.stoppable = batchStatus.isLessThan(BatchStatus.STOPPING); + + if (jobExecution.getExitStatus() != null) { + this.exitCode = jobExecution.getExitStatus().getExitCode(); + this.exitDescription = jobExecution.getExitStatus().getExitDescription(); + } + else { + this.exitCode = null; + this.exitDescription = null; + } + if(jobInstance != null) { this.jobName = jobInstance.getJobName(); - BatchStatus endTime = jobExecutionData.getStatus(); - this.restartable = endTime.isGreaterThan(BatchStatus.STOPPING) && endTime.isLessThan(BatchStatus.ABANDONED); - this.abandonable = endTime.isGreaterThan(BatchStatus.STARTED) && endTime != BatchStatus.ABANDONED; - this.stoppable = endTime.isLessThan(BatchStatus.STOPPING); } else { this.jobName = "?"; } - SimpleDateFormat dateFormat = new SimpleDateFormat("yyyy-MM-dd"); - SimpleDateFormat timeFormat = new SimpleDateFormat("HH:mm:ss"); - SimpleDateFormat durationFormat = new SimpleDateFormat("HH:mm:ss"); - - durationFormat.setTimeZone(TimeZone.getTimeZone("GMT")); - timeFormat.setTimeZone(timeZone); - dateFormat.setTimeZone(timeZone); - if(jobExecutionData.getStartTime() != null) { - this.startDate = dateFormat.format(jobExecutionData.getStartTime()); - this.startTime = timeFormat.format(jobExecutionData.getStartTime()); - Date endTime1 = jobExecutionData.getEndTime() != null? jobExecutionData.getEndTime():new Date(); - this.duration = durationFormat.format(new Date(endTime1.getTime() - jobExecutionData.getStartTime().getTime())); + if(startTime != null && endTime != null) { + this.duration = Duration.between(startTime, endTime); + } + else { + this.duration = null; } - } - public Long getId() { - return id; + this.failureExceptions = unmodifiableList(jobExecution.getFailureExceptions()); + this.jobConfigurationName = jobExecution.getJobConfigurationName(); } - public int getStepExecutionCount() { - return stepExecutionCount; + public Long getJobExecutionId() { + return jobExecutionId; } - public Long getJobId() { - return jobId; + public Long getJobInstanceId() { + return jobInstanceId; } public String getJobName() { return jobName; } - public String getStartDate() { - return startDate; - } - - public String getStartTime() { + public OffsetDateTime getStartTime() { return startTime; } - public String getDuration() { + public Duration getDuration() { return duration; } - public JobExecutionData getJobExecutionData() { - return jobExecutionData; - } - public Properties getJobParameters() { return jobParameters; } - public String getJobParametersString() { - return jobParametersString; - } - public boolean isRestartable() { return restartable; } @@ -135,7 +146,35 @@ public class JobExecutionInfoResponse { return stoppable; } - public TimeZone getTimeZone() { - return timeZone; + public BatchStatus getBatchStatus() { + return batchStatus; + } + + public OffsetDateTime getCreationTime() { + return creationTime; + } + + public OffsetDateTime getEndTime() { + return endTime; + } + + public OffsetDateTime getLastUpdatedTime() { + return lastUpdatedTime; + } + + public String getExitCode() { + return exitCode; + } + + public String getExitDescription() { + return exitDescription; + } + + public List<Throwable> getFailureExceptions() { + return this.failureExceptions; + } + + public String getJobConfigurationName() { + return this.jobConfigurationName; } } diff --git a/ambari-infra-manager/src/main/java/org/apache/ambari/infra/model/JobExecutionRestartRequest.java b/ambari-infra-manager/src/main/java/org/apache/ambari/infra/model/JobExecutionRestartRequest.java index 3eab25f..fe36c5b 100644 --- a/ambari-infra-manager/src/main/java/org/apache/ambari/infra/model/JobExecutionRestartRequest.java +++ b/ambari-infra-manager/src/main/java/org/apache/ambari/infra/model/JobExecutionRestartRequest.java @@ -22,6 +22,8 @@ import javax.validation.constraints.NotNull; import javax.ws.rs.PathParam; import javax.ws.rs.QueryParam; +import io.swagger.annotations.ApiParam; + public class JobExecutionRestartRequest { @PathParam("jobName") @@ -34,6 +36,7 @@ public class JobExecutionRestartRequest { @QueryParam("operation") @NotNull + @ApiParam(required = true) private JobOperationParams.JobRestartOperationParam operation; public String getJobName() { diff --git a/ambari-infra-manager/src/main/java/org/apache/ambari/infra/model/JobExecutionStopRequest.java b/ambari-infra-manager/src/main/java/org/apache/ambari/infra/model/JobExecutionStopRequest.java index b176f12..510e694 100644 --- a/ambari-infra-manager/src/main/java/org/apache/ambari/infra/model/JobExecutionStopRequest.java +++ b/ambari-infra-manager/src/main/java/org/apache/ambari/infra/model/JobExecutionStopRequest.java @@ -22,6 +22,8 @@ import javax.validation.constraints.NotNull; import javax.ws.rs.PathParam; import javax.ws.rs.QueryParam; +import io.swagger.annotations.ApiParam; + public class JobExecutionStopRequest { @PathParam("jobExecutionId") @@ -30,6 +32,7 @@ public class JobExecutionStopRequest { @QueryParam("operation") @NotNull + @ApiParam(required = true) private JobOperationParams.JobStopOrAbandonOperationParam operation; public Long getJobExecutionId() { diff --git a/ambari-infra-manager/src/main/java/org/apache/ambari/infra/model/JobInstanceDetailsResponse.java b/ambari-infra-manager/src/main/java/org/apache/ambari/infra/model/JobInstanceDetailsResponse.java index af88654..9bb7994 100644 --- a/ambari-infra-manager/src/main/java/org/apache/ambari/infra/model/JobInstanceDetailsResponse.java +++ b/ambari-infra-manager/src/main/java/org/apache/ambari/infra/model/JobInstanceDetailsResponse.java @@ -18,37 +18,31 @@ */ package org.apache.ambari.infra.model; -import org.springframework.batch.core.JobInstance; +import static java.util.Collections.unmodifiableList; import java.util.List; -public class JobInstanceDetailsResponse { +import org.springframework.batch.admin.web.JobInfo; - private JobInstance jobInstance; +import com.fasterxml.jackson.annotation.JsonProperty; - private List<JobExecutionInfoResponse> jobExecutionInfoResponseList; - public JobInstanceDetailsResponse() { - } +public class JobInstanceDetailsResponse { - public JobInstanceDetailsResponse(JobInstance jobInstance, List<JobExecutionInfoResponse> jobExecutionInfoResponseList) { - this.jobInstance = jobInstance; - this.jobExecutionInfoResponseList = jobExecutionInfoResponseList; - } + private final JobInfo jobInfo; + @JsonProperty("jobExecutionList") + private final List<JobExecutionInfoResponse> jobExecutionInfoResponseList; - public JobInstance getJobInstance() { - return jobInstance; + public JobInstanceDetailsResponse(JobInfo jobInfo, List<JobExecutionInfoResponse> jobExecutionInfoResponseList) { + this.jobInfo = jobInfo; + this.jobExecutionInfoResponseList = unmodifiableList(jobExecutionInfoResponseList); } - public void setJobInstance(JobInstance jobInstance) { - this.jobInstance = jobInstance; + public JobInfo getJobInfo() { + return jobInfo; } public List<JobExecutionInfoResponse> getJobExecutionInfoResponseList() { return jobExecutionInfoResponseList; } - - public void setJobExecutionInfoResponseList(List<JobExecutionInfoResponse> jobExecutionInfoResponseList) { - this.jobExecutionInfoResponseList = jobExecutionInfoResponseList; - } } diff --git a/ambari-infra-manager/src/main/java/org/apache/ambari/infra/model/JobInstanceStartRequest.java b/ambari-infra-manager/src/main/java/org/apache/ambari/infra/model/JobInstanceStartRequest.java index 905a4fa..5c760f6 100644 --- a/ambari-infra-manager/src/main/java/org/apache/ambari/infra/model/JobInstanceStartRequest.java +++ b/ambari-infra-manager/src/main/java/org/apache/ambari/infra/model/JobInstanceStartRequest.java @@ -22,14 +22,17 @@ import javax.validation.constraints.NotNull; import javax.ws.rs.PathParam; import javax.ws.rs.QueryParam; +import io.swagger.annotations.ApiParam; + public class JobInstanceStartRequest { @PathParam("jobName") @NotNull + @ApiParam(required = true) private String jobName; @QueryParam("params") - String params; + private String params; public String getJobName() { return jobName; diff --git a/ambari-infra-manager/src/main/java/org/apache/ambari/infra/model/StepExecutionContextResponse.java b/ambari-infra-manager/src/main/java/org/apache/ambari/infra/model/StepExecutionContextResponse.java index 0e67a87..9ce56af 100644 --- a/ambari-infra-manager/src/main/java/org/apache/ambari/infra/model/StepExecutionContextResponse.java +++ b/ambari-infra-manager/src/main/java/org/apache/ambari/infra/model/StepExecutionContextResponse.java @@ -18,23 +18,22 @@ */ package org.apache.ambari.infra.model; +import static java.util.Collections.unmodifiableMap; + import java.util.Map; public class StepExecutionContextResponse { - private Map<String, Object> executionContextMap; - - private Long jobExecutionId; + private final Map<String, Object> executionContextMap; - private Long stepExecutionId; + private final Long jobExecutionId; - private String stepName; + private final Long stepExecutionId; - public StepExecutionContextResponse() { - } + private final String stepName; public StepExecutionContextResponse(Map<String, Object> executionContextMap, Long jobExecutionId, Long stepExecutionId, String stepName) { - this.executionContextMap = executionContextMap; + this.executionContextMap = unmodifiableMap(executionContextMap); this.jobExecutionId = jobExecutionId; this.stepExecutionId = stepExecutionId; this.stepName = stepName; diff --git a/ambari-infra-manager/src/main/java/org/apache/ambari/infra/model/StepExecutionInfoResponse.java b/ambari-infra-manager/src/main/java/org/apache/ambari/infra/model/StepExecutionInfoResponse.java index ed04767..011eaea 100644 --- a/ambari-infra-manager/src/main/java/org/apache/ambari/infra/model/StepExecutionInfoResponse.java +++ b/ambari-infra-manager/src/main/java/org/apache/ambari/infra/model/StepExecutionInfoResponse.java @@ -18,98 +18,101 @@ */ package org.apache.ambari.infra.model; -import com.fasterxml.jackson.annotation.JsonIgnore; -import org.apache.ambari.infra.model.wrapper.StepExecutionData; -import org.springframework.batch.core.JobExecution; +import static org.apache.ambari.infra.model.DateUtil.toOffsetDateTime; + +import java.time.Duration; +import java.time.OffsetDateTime; + +import org.apache.ambari.infra.json.DurationToStringConverter; +import org.apache.ambari.infra.json.OffsetDateTimeToStringConverter; +import org.springframework.batch.core.BatchStatus; import org.springframework.batch.core.StepExecution; -import java.text.SimpleDateFormat; -import java.util.Date; -import java.util.TimeZone; +import com.fasterxml.jackson.databind.annotation.JsonSerialize; -public class StepExecutionInfoResponse { - private Long id; - private Long jobExecutionId; - private String jobName; - private String name; - private String startDate = "-"; - private String startTime = "-"; - private String duration = "-"; - private StepExecutionData stepExecutionData; - private long durationMillis; - - public StepExecutionInfoResponse(String jobName, Long jobExecutionId, String name, TimeZone timeZone) { - this.jobName = jobName; - this.jobExecutionId = jobExecutionId; - this.name = name; - this.stepExecutionData = new StepExecutionData(new StepExecution(name, new JobExecution(jobExecutionId))); - } +import io.swagger.annotations.ApiModelProperty; - public StepExecutionInfoResponse(StepExecution stepExecution, TimeZone timeZone) { - this.stepExecutionData = new StepExecutionData(stepExecution); - this.id = stepExecutionData.getId(); - this.name = stepExecutionData.getStepName(); - this.jobName = stepExecutionData.getJobExecution() != null && stepExecutionData.getJobExecution().getJobInstance() != null? stepExecutionData.getJobExecution().getJobInstance().getJobName():"?"; - this.jobExecutionId = stepExecutionData.getJobExecutionId(); - SimpleDateFormat dateFormat = new SimpleDateFormat("yyyy-MM-dd"); - SimpleDateFormat timeFormat = new SimpleDateFormat("HH:mm:ss"); - SimpleDateFormat durationFormat = new SimpleDateFormat("HH:mm:ss"); - - durationFormat.setTimeZone(TimeZone.getTimeZone("GMT")); - timeFormat.setTimeZone(timeZone); - dateFormat.setTimeZone(timeZone); - if(stepExecutionData.getStartTime() != null) { - this.startDate = dateFormat.format(stepExecutionData.getStartTime()); - this.startTime = timeFormat.format(stepExecutionData.getStartTime()); - Date endTime = stepExecutionData.getEndTime() != null? stepExecutionData.getEndTime():new Date(); - this.durationMillis = endTime.getTime() - stepExecutionData.getStartTime().getTime(); - this.duration = durationFormat.format(new Date(this.durationMillis)); +public class StepExecutionInfoResponse { + private final Long stepExecutionId; + private final Long jobExecutionId; + private final String jobName; + private final String stepName; + @JsonSerialize(converter = OffsetDateTimeToStringConverter.class) + private final OffsetDateTime startTime; + @JsonSerialize(converter = OffsetDateTimeToStringConverter.class) + private final OffsetDateTime endTime; + @JsonSerialize(converter = DurationToStringConverter.class) + @ApiModelProperty(dataType = "java.lang.String", example = "PT5.311S") + private final Duration duration; + private final BatchStatus batchStatus; + @ApiModelProperty(example = "COMPLETED", allowableValues = "UNKNOWN, EXECUTING, COMPLETED, NOOP, FAILED, STOPPED") + private final String exitCode; + private final String exitDescription; + + + public StepExecutionInfoResponse(StepExecution stepExecution) { + this.stepExecutionId = stepExecution.getId(); + this.stepName = stepExecution.getStepName(); + this.jobName = stepExecution.getJobExecution() != null && stepExecution.getJobExecution().getJobInstance() != null ? stepExecution.getJobExecution().getJobInstance().getJobName() : "?"; + this.jobExecutionId = stepExecution.getJobExecutionId(); + this.startTime = toOffsetDateTime(stepExecution.getStartTime()); + this.endTime = toOffsetDateTime(stepExecution.getEndTime()); + + if(this.startTime != null && this.endTime != null) { + this.duration = Duration.between(this.startTime, this.endTime); + } + else { + this.duration = null; } + this.batchStatus = stepExecution.getStatus(); + if (stepExecution.getExitStatus() != null) { + this.exitCode = stepExecution.getExitStatus().getExitCode(); + this.exitDescription = stepExecution.getExitStatus().getExitDescription(); + } + else { + this.exitCode = null; + this.exitDescription = null; + } } - public Long getId() { - return this.id; + public Long getStepExecutionId() { + return this.stepExecutionId; } public Long getJobExecutionId() { return this.jobExecutionId; } - public String getName() { - return this.name; + public String getStepName() { + return this.stepName; } public String getJobName() { return this.jobName; } - public String getStartDate() { - return this.startDate; - } - - public String getStartTime() { - return this.startTime; + public OffsetDateTime getStartTime() { + return startTime; } - public String getDuration() { - return this.duration; + public OffsetDateTime getEndTime() { + return endTime; } - public long getDurationMillis() { - return this.durationMillis; + public Duration getDuration() { + return duration; } - public String getStatus() { - return this.id != null?this.stepExecutionData.getStatus().toString():"NONE"; + public BatchStatus getBatchStatus() { + return batchStatus; } public String getExitCode() { - return this.id != null?this.stepExecutionData.getExitStatus().getExitCode():"NONE"; + return exitCode; } - @JsonIgnore - public StepExecutionData getStepExecution() { - return this.stepExecutionData; + public String getExitDescription() { + return exitDescription; } } diff --git a/ambari-infra-manager/src/main/java/org/apache/ambari/infra/model/StepExecutionProgressResponse.java b/ambari-infra-manager/src/main/java/org/apache/ambari/infra/model/StepExecutionProgressResponse.java index 26f9ed4..d2404e5 100644 --- a/ambari-infra-manager/src/main/java/org/apache/ambari/infra/model/StepExecutionProgressResponse.java +++ b/ambari-infra-manager/src/main/java/org/apache/ambari/infra/model/StepExecutionProgressResponse.java @@ -21,16 +21,16 @@ package org.apache.ambari.infra.model; import org.springframework.batch.admin.history.StepExecutionHistory; import org.springframework.batch.admin.web.StepExecutionProgress; -public class StepExecutionProgressResponse { +import com.fasterxml.jackson.annotation.JsonProperty; - private StepExecutionProgress stepExecutionProgress; +public class StepExecutionProgressResponse { - private StepExecutionHistory stepExecutionHistory; + private final StepExecutionProgress stepExecutionProgress; - private StepExecutionInfoResponse stepExecutionInfoResponse; + private final StepExecutionHistory stepExecutionHistory; - public StepExecutionProgressResponse() { - } + @JsonProperty("stepExecution") + private final StepExecutionInfoResponse stepExecutionInfoResponse; public StepExecutionProgressResponse(StepExecutionProgress stepExecutionProgress, StepExecutionHistory stepExecutionHistory, StepExecutionInfoResponse stepExecutionInfoResponse) { diff --git a/ambari-infra-manager/src/main/java/org/apache/ambari/infra/model/wrapper/JobExecutionData.java b/ambari-infra-manager/src/main/java/org/apache/ambari/infra/model/wrapper/JobExecutionData.java deleted file mode 100644 index 28e262a..0000000 --- a/ambari-infra-manager/src/main/java/org/apache/ambari/infra/model/wrapper/JobExecutionData.java +++ /dev/null @@ -1,118 +0,0 @@ -/* - * Licensed to the Apache Software Foundation (ASF) under one - * or more contributor license agreements. See the NOTICE file - * distributed with this work for additional information - * regarding copyright ownership. The ASF licenses this file - * to you under the Apache License, Version 2.0 (the - * "License"); you may not use this file except in compliance - * with the License. You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, - * software distributed under the License is distributed on an - * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY - * KIND, either express or implied. See the License for the - * specific language governing permissions and limitations - * under the License. - */ -package org.apache.ambari.infra.model.wrapper; - -import com.fasterxml.jackson.annotation.JsonIgnore; -import com.google.common.collect.Lists; -import org.springframework.batch.core.BatchStatus; -import org.springframework.batch.core.ExitStatus; -import org.springframework.batch.core.JobExecution; -import org.springframework.batch.core.JobInstance; -import org.springframework.batch.core.JobParameters; -import org.springframework.batch.core.StepExecution; -import org.springframework.batch.item.ExecutionContext; - -import java.util.Collection; -import java.util.Date; -import java.util.List; - -/** - * Wrapper for #{{@link JobExecution}} - */ -public class JobExecutionData { - - private JobExecution jobExecution; - - public JobExecutionData(JobExecution jobExecution) { - this.jobExecution = jobExecution; - } - - @JsonIgnore - public JobExecution getJobExecution() { - return jobExecution; - } - - @JsonIgnore - public Collection<StepExecution> getStepExecutions() { - return jobExecution.getStepExecutions(); - } - - public JobParameters getJobParameters() { - return jobExecution.getJobParameters(); - } - - public JobInstance getJobInstance() { - return jobExecution.getJobInstance(); - } - - public Collection<StepExecutionData> getStepExecutionDataList() { - List<StepExecutionData> stepExecutionDataList = Lists.newArrayList(); - Collection<StepExecution> stepExecutions = getStepExecutions(); - if (stepExecutions != null) { - for (StepExecution stepExecution : stepExecutions) { - stepExecutionDataList.add(new StepExecutionData(stepExecution)); - } - } - return stepExecutionDataList; - } - - public BatchStatus getStatus() { - return jobExecution.getStatus(); - } - - public Date getStartTime() { - return jobExecution.getStartTime(); - } - - public Date getCreateTime() { - return jobExecution.getCreateTime(); - } - - public Date getEndTime() { - return jobExecution.getEndTime(); - } - - public Date getLastUpdated() { - return jobExecution.getLastUpdated(); - } - - public ExitStatus getExitStatus() { - return jobExecution.getExitStatus(); - } - - public ExecutionContext getExecutionContext() { - return jobExecution.getExecutionContext(); - } - - public List<Throwable> getFailureExceptions() { - return jobExecution.getFailureExceptions(); - } - - public String getJobConfigurationName() { - return jobExecution.getJobConfigurationName(); - } - - public Long getId() { - return jobExecution.getId(); - } - - public Long getJobId() { - return jobExecution.getJobId(); - } -} diff --git a/ambari-infra-manager/src/main/java/org/apache/ambari/infra/model/wrapper/StepExecutionData.java b/ambari-infra-manager/src/main/java/org/apache/ambari/infra/model/wrapper/StepExecutionData.java deleted file mode 100644 index 26552ae..0000000 --- a/ambari-infra-manager/src/main/java/org/apache/ambari/infra/model/wrapper/StepExecutionData.java +++ /dev/null @@ -1,133 +0,0 @@ -/* - * Licensed to the Apache Software Foundation (ASF) under one - * or more contributor license agreements. See the NOTICE file - * distributed with this work for additional information - * regarding copyright ownership. The ASF licenses this file - * to you under the Apache License, Version 2.0 (the - * "License"); you may not use this file except in compliance - * with the License. You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, - * software distributed under the License is distributed on an - * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY - * KIND, either express or implied. See the License for the - * specific language governing permissions and limitations - * under the License. - */ -package org.apache.ambari.infra.model.wrapper; - -import com.fasterxml.jackson.annotation.JsonIgnore; -import org.springframework.batch.core.BatchStatus; -import org.springframework.batch.core.ExitStatus; -import org.springframework.batch.core.JobExecution; -import org.springframework.batch.core.StepExecution; -import org.springframework.batch.item.ExecutionContext; - -import java.util.Date; -import java.util.List; - -/** - * Wrapper for #{{@link StepExecution}} - */ -public class StepExecutionData { - - @JsonIgnore - private final JobExecution jobExecution; - - @JsonIgnore - private final StepExecution stepExecution; - - - public StepExecutionData(StepExecution stepExecution) { - this.stepExecution = stepExecution; - this.jobExecution = stepExecution.getJobExecution(); - } - - @JsonIgnore - public JobExecution getJobExecution() { - return jobExecution; - } - - @JsonIgnore - public StepExecution getStepExecution() { - return stepExecution; - } - - public String getStepName() { - return stepExecution.getStepName(); - } - - public int getReadCount() { - return stepExecution.getReadCount(); - } - - public BatchStatus getStatus() { - return stepExecution.getStatus(); - } - - public int getWriteCount() { - return stepExecution.getWriteCount(); - } - - public int getCommitCount() { - return stepExecution.getCommitCount(); - } - - public int getRollbackCount() { - return stepExecution.getRollbackCount(); - } - - public int getReadSkipCount() { - return stepExecution.getReadSkipCount(); - } - - public int getProcessSkipCount() { - return stepExecution.getProcessSkipCount(); - } - - public Date getStartTime() { - return stepExecution.getStartTime(); - } - - public int getWriteSkipCount() { - return stepExecution.getWriteSkipCount(); - } - - public Date getEndTime() { - return stepExecution.getEndTime(); - } - - public Date getLastUpdated() { - return stepExecution.getLastUpdated(); - } - - public ExecutionContext getExecutionContext() { - return stepExecution.getExecutionContext(); - } - - public ExitStatus getExitStatus() { - return stepExecution.getExitStatus(); - } - - public boolean isTerminateOnly() { - return stepExecution.isTerminateOnly(); - } - - public int getFilterCount() { - return stepExecution.getFilterCount(); - } - - public List<Throwable> getFailureExceptions() { - return stepExecution.getFailureExceptions(); - } - - public Long getId() { - return stepExecution.getId(); - } - - public Long getJobExecutionId() { - return stepExecution.getJobExecutionId(); - } -} diff --git a/ambari-infra-manager/src/main/java/org/apache/ambari/infra/rest/ApiDocResource.java b/ambari-infra-manager/src/main/java/org/apache/ambari/infra/rest/ApiDocResource.java deleted file mode 100644 index 18dfdd9..0000000 --- a/ambari-infra-manager/src/main/java/org/apache/ambari/infra/rest/ApiDocResource.java +++ /dev/null @@ -1,56 +0,0 @@ -/* - * Licensed to the Apache Software Foundation (ASF) under one - * or more contributor license agreements. See the NOTICE file - * distributed with this work for additional information - * regarding copyright ownership. The ASF licenses this file - * to you under the Apache License, Version 2.0 (the - * "License"); you may not use this file except in compliance - * with the License. You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, - * software distributed under the License is distributed on an - * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY - * KIND, either express or implied. See the License for the - * specific language governing permissions and limitations - * under the License. - */ -package org.apache.ambari.infra.rest; - -import io.swagger.annotations.ApiOperation; -import org.apache.ambari.infra.doc.InfraManagerApiDocStorage; -import org.springframework.context.annotation.Scope; - -import javax.inject.Inject; -import javax.inject.Named; -import javax.ws.rs.GET; -import javax.ws.rs.Path; -import javax.ws.rs.PathParam; -import javax.ws.rs.Produces; -import javax.ws.rs.core.MediaType; -import javax.ws.rs.core.Response; - -@Path("swagger.{type:json|yaml}") -@Named -@Scope("request") -public class ApiDocResource { - - @Inject - private InfraManagerApiDocStorage infraManagerApiDocStorage; - - @GET - @Produces({MediaType.APPLICATION_JSON, "application/yaml"}) - @ApiOperation(value = "The swagger definition in either JSON or YAML", hidden = true) - public Response swaggerDefinitionResponse(@PathParam("type") String type) { - Response response = Response.status(404).build(); - if (infraManagerApiDocStorage.getSwagger() != null) { - if ("yaml".equalsIgnoreCase(type)) { - response = Response.ok().entity(infraManagerApiDocStorage.getSwaggerYaml()).type("application/yaml").build(); - } else { - response = Response.ok().entity(infraManagerApiDocStorage.getSwagger()).build(); - } - } - return response; - } -} diff --git a/ambari-infra-manager/src/main/java/org/apache/ambari/infra/rest/JobResource.java b/ambari-infra-manager/src/main/java/org/apache/ambari/infra/rest/JobResource.java index 43f7c41..f5de3c0 100644 --- a/ambari-infra-manager/src/main/java/org/apache/ambari/infra/rest/JobResource.java +++ b/ambari-infra-manager/src/main/java/org/apache/ambari/infra/rest/JobResource.java @@ -35,12 +35,12 @@ import javax.ws.rs.Produces; import org.apache.ambari.infra.manager.JobManager; import org.apache.ambari.infra.model.ExecutionContextResponse; -import org.apache.ambari.infra.model.JobDetailsResponse; import org.apache.ambari.infra.model.JobExecutionDetailsResponse; import org.apache.ambari.infra.model.JobExecutionInfoResponse; import org.apache.ambari.infra.model.JobExecutionRequest; import org.apache.ambari.infra.model.JobExecutionRestartRequest; import org.apache.ambari.infra.model.JobExecutionStopRequest; +import org.apache.ambari.infra.model.JobInstanceDetailsResponse; import org.apache.ambari.infra.model.JobInstanceStartRequest; import org.apache.ambari.infra.model.JobRequest; import org.apache.ambari.infra.model.PageRequest; @@ -120,7 +120,7 @@ public class JobResource { @Produces({"application/json"}) @Path("{jobName}/info") @ApiOperation("Get job details by job name.") - public JobDetailsResponse getJobDetails(@BeanParam @Valid JobRequest jobRequest) throws NoSuchJobException { + public List<JobInstanceDetailsResponse> getJobDetails(@BeanParam @Valid JobRequest jobRequest) throws NoSuchJobException { return jobManager.getJobDetails(jobRequest.getJobName(), jobRequest.getPage(), jobRequest.getSize()); } @@ -136,7 +136,7 @@ public class JobResource { @Produces({"application/json"}) @Path("/executions/{jobExecutionId}") @ApiOperation("Get job and step details for job execution instance.") - public JobExecutionDetailsResponse getExectionInfo(@PathParam("jobExecutionId") @Valid Long jobExecutionId) throws NoSuchJobExecutionException { + public JobExecutionDetailsResponse getExecutionInfo(@PathParam("jobExecutionId") @Valid Long jobExecutionId) throws NoSuchJobExecutionException { return jobManager.getExecutionInfo(jobExecutionId); } @@ -169,8 +169,8 @@ public class JobResource { @GET @Produces({"application/json"}) @Path("/{jobName}/{jobInstanceId}/executions") - @ApiOperation("Get execution for job instance.") - public List<JobExecutionInfoResponse> getExecutionsForInstance(@BeanParam @Valid JobExecutionRequest request) throws + @ApiOperation("Get execution of job instance.") + public List<JobExecutionInfoResponse> getExecutionsOfInstance(@BeanParam @Valid JobExecutionRequest request) throws NoSuchJobException, NoSuchJobInstanceException { return jobManager.getExecutionsForJobInstance(request.getJobName(), request.getJobInstanceId()); } diff --git a/ambari-infra-manager/src/main/resources/infra-manager.properties b/ambari-infra-manager/src/main/resources/infra-manager.properties index 8133905..c3ec9d0 100644 --- a/ambari-infra-manager/src/main/resources/infra-manager.properties +++ b/ambari-infra-manager/src/main/resources/infra-manager.properties @@ -18,6 +18,7 @@ infra-manager.batch.db.init=false infra-manager.batch.db.username=admin infra-manager.batch.db.password=admin infra-manager.server.data.folder=/tmp/ambariInfraManager +infra-manager.admin-user.password=admin # Archive Service Logs infra-manager.jobs.solr_data_archiving.archive_service_logs.enabled=true diff --git a/ambari-infra-manager/src/main/resources/swagger/swagger.html b/ambari-infra-manager/src/main/resources/swagger/swagger.html index b24dd25..69478fd 100644 --- a/ambari-infra-manager/src/main/resources/swagger/swagger.html +++ b/ambari-infra-manager/src/main/resources/swagger/swagger.html @@ -57,7 +57,7 @@ var urlPrefix = location.protocol +'//'+ location.hostname+(location.port ? ':'+location.port: ''); // Build a system const ui = SwaggerUIBundle({ - url: urlPrefix + "/api/v1/swagger.yaml", + url: urlPrefix + "/swagger.yaml", dom_id: '#swagger-ui', deepLinking: true, presets: [ diff --git a/ambari-infra-manager/src/test/java/org/apache/ambari/infra/job/archive/DocumentExporterTest.java b/ambari-infra-manager/src/test/java/org/apache/ambari/infra/job/archive/DocumentExporterTest.java index d2e7b04..fe2b037 100644 --- a/ambari-infra-manager/src/test/java/org/apache/ambari/infra/job/archive/DocumentExporterTest.java +++ b/ambari-infra-manager/src/test/java/org/apache/ambari/infra/job/archive/DocumentExporterTest.java @@ -36,8 +36,8 @@ import org.junit.After; import org.junit.Before; import org.junit.Test; import org.junit.runner.RunWith; -import org.springframework.batch.core.BatchStatus; import org.springframework.batch.core.JobExecution; +import org.springframework.batch.core.StepContribution; import org.springframework.batch.core.StepExecution; import org.springframework.batch.core.scope.context.ChunkContext; import org.springframework.batch.core.scope.context.StepContext; @@ -56,6 +56,8 @@ public class DocumentExporterTest extends EasyMockSupport { private static final Document DOCUMENT_3 = new Document(new HashMap<String, Object>() {{ put("id", "3"); }}); + private static final StepContribution ANY_STEP_CONTRIBUTION = new StepContribution(new StepExecution("any", new JobExecution(1L))); + private DocumentExporter documentExporter; @Mock private ItemStreamReader<Document> reader; @@ -74,8 +76,8 @@ public class DocumentExporterTest extends EasyMockSupport { private static final Document DOCUMENT = new Document(new HashMap<String, Object>() {{ put("id", "1"); }}); @Before - public void setUp() throws Exception { - chunkContext = chunkContext(BatchStatus.STARTED); + public void setUp() { + chunkContext = chunkContext(false); documentExporter = documentExporter(2); } @@ -83,15 +85,16 @@ public class DocumentExporterTest extends EasyMockSupport { return new DocumentExporter(reader, documentDestination, writeBlockSize, jobContextRepository); } - private ChunkContext chunkContext(BatchStatus batchStatus) { + private ChunkContext chunkContext(boolean terminate) { StepExecution stepExecution = new StepExecution("exportDoc", new JobExecution(JOB_EXECUTION_ID)); stepExecution.setId(STEP_EXECUTION_ID); - stepExecution.getJobExecution().setStatus(batchStatus); + if (terminate) + stepExecution.setTerminateOnly(); return new ChunkContext(new StepContext(stepExecution)); } @After - public void tearDown() throws Exception { + public void tearDown() { verifyAll(); } @@ -102,7 +105,7 @@ public class DocumentExporterTest extends EasyMockSupport { reader.close(); expectLastCall(); replayAll(); - documentExporter.execute(null, chunkContext); + documentExporter.execute(ANY_STEP_CONTRIBUTION, chunkContext); } private ExecutionContext executionContext(ChunkContext chunkContext) { @@ -120,7 +123,7 @@ public class DocumentExporterTest extends EasyMockSupport { documentItemWriter.close(); expectLastCall(); replayAll(); - assertThat(documentExporter.execute(null, chunkContext), is(RepeatStatus.FINISHED)); + assertThat(documentExporter.execute(ANY_STEP_CONTRIBUTION, chunkContext), is(RepeatStatus.FINISHED)); } @Test @@ -143,7 +146,7 @@ public class DocumentExporterTest extends EasyMockSupport { documentItemWriter2.close(); expectLastCall(); replayAll(); - assertThat(documentExporter.execute(null, chunkContext), is(RepeatStatus.FINISHED)); + assertThat(documentExporter.execute(ANY_STEP_CONTRIBUTION, chunkContext), is(RepeatStatus.FINISHED)); } @Test(expected = IOException.class) @@ -157,7 +160,7 @@ public class DocumentExporterTest extends EasyMockSupport { reader.close(); expectLastCall(); replayAll(); - documentExporter.execute(null, chunkContext); + documentExporter.execute(ANY_STEP_CONTRIBUTION, chunkContext); } @Test(expected = UncheckedIOException.class) @@ -170,12 +173,12 @@ public class DocumentExporterTest extends EasyMockSupport { reader.close(); expectLastCall(); replayAll(); - documentExporter.execute(null, chunkContext); + documentExporter.execute(ANY_STEP_CONTRIBUTION, chunkContext); } @Test public void testStopAndRestartExportsAllDocuments() throws Exception { - ChunkContext stoppingChunkContext = chunkContext(BatchStatus.STOPPING); + ChunkContext stoppingChunkContext = chunkContext(true); DocumentExporter documentExporter = documentExporter(1); reader.open(executionContext(chunkContext)); expectLastCall(); @@ -206,9 +209,9 @@ public class DocumentExporterTest extends EasyMockSupport { reader.close(); expectLastCall(); replayAll(); - RepeatStatus repeatStatus = documentExporter.execute(null, this.chunkContext); + RepeatStatus repeatStatus = documentExporter.execute(ANY_STEP_CONTRIBUTION, this.chunkContext); assertThat(repeatStatus, is(RepeatStatus.CONTINUABLE)); - repeatStatus = documentExporter.execute(null, this.chunkContext); + repeatStatus = documentExporter.execute(ANY_STEP_CONTRIBUTION, this.chunkContext); assertThat(repeatStatus, is(RepeatStatus.FINISHED)); } } \ No newline at end of file diff --git a/ambari-infra-manager/src/test/java/org/apache/ambari/infra/model/ISO8601DateFormatterTest.java b/ambari-infra-manager/src/test/java/org/apache/ambari/infra/model/ISO8601DateFormatterTest.java new file mode 100644 index 0000000..b2bb0e8 --- /dev/null +++ b/ambari-infra-manager/src/test/java/org/apache/ambari/infra/model/ISO8601DateFormatterTest.java @@ -0,0 +1,50 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ +package org.apache.ambari.infra.model; + +import static org.hamcrest.core.Is.is; +import static org.junit.Assert.assertThat; + +import java.text.ParseException; +import java.time.OffsetDateTime; +import java.time.ZoneOffset; +import java.util.Date; + +import org.junit.Test; + +public class ISO8601DateFormatterTest { + + @Test + public void testFormat() { + OffsetDateTime offsetDateTime = OffsetDateTime.of( + 2018, 11, 30, + 2, 30, 11, 0, + ZoneOffset.ofHoursMinutes(1, 30)); + String text = new ISO8601DateFormatter().format(Date.from(offsetDateTime.toInstant())); + assertThat(text, is("2018-11-30T01:00:11Z")); + } + + @Test + public void testParse() throws ParseException { + Date now = new Date(); + ISO8601DateFormatter iso8601DateFormatter = new ISO8601DateFormatter(); + Date parsed = iso8601DateFormatter.parse(iso8601DateFormatter.format(now)); + assertThat(parsed, is(now)); + } +} \ No newline at end of file