/*
* 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.usergrid.chop.webapp.coordinator.rest;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.util.Collection;
import java.util.Iterator;
import com.google.common.base.Preconditions;
import com.google.inject.Inject;
import com.google.inject.Singleton;
import com.sun.jersey.multipart.FormDataParam;
import org.apache.usergrid.chop.api.RestParams;
import org.apache.usergrid.chop.api.Run;
import org.apache.usergrid.chop.api.Runner;
import org.apache.usergrid.chop.webapp.dao.RunDao;
import org.apache.usergrid.chop.webapp.dao.RunResultDao;
import org.apache.usergrid.chop.webapp.dao.RunnerDao;
import org.apache.usergrid.chop.webapp.dao.model.BasicModule;
import org.apache.usergrid.chop.webapp.dao.model.BasicRun;
import org.apache.usergrid.chop.webapp.dao.model.BasicRunResult;
import org.apache.usergrid.chop.webapp.elasticsearch.Util;
import org.elasticsearch.indices.IndexMissingException;
import org.json.simple.JSONArray;
import org.json.simple.JSONObject;
import org.json.simple.parser.JSONParser;
import org.safehaus.jettyjam.utils.TestMode;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import javax.annotation.Nullable;
import javax.ws.rs.Consumes;
import javax.ws.rs.GET;
import javax.ws.rs.POST;
import javax.ws.rs.Path;
import javax.ws.rs.Produces;
import javax.ws.rs.QueryParam;
import javax.ws.rs.core.MediaType;
import javax.ws.rs.core.Response;
/**
* REST operation to setup the Stack under test.
*/
@Singleton
@Path( RunManagerResource.ENDPOINT )
public class RunManagerResource extends TestableResource implements RestParams {
public final static String ENDPOINT = "/run";
private static final Logger LOG = LoggerFactory.getLogger( RunManagerResource.class );
@Inject
private RunDao runDao;
@Inject
private RunResultDao runResultDao;
@Inject
private RunnerDao runnerDao;
protected RunManagerResource() {
super( ENDPOINT );
}
@GET
@Path( "/next" )
@Produces( MediaType.APPLICATION_JSON )
public Response next(
@QueryParam( USERNAME ) String username,
@QueryParam( COMMIT_ID ) String commitId,
@QueryParam( MODULE_GROUPID ) String groupId,
@QueryParam( MODULE_ARTIFACTID ) String artifactId,
@QueryParam( MODULE_VERSION ) String version,
@Nullable @QueryParam( TestMode.TEST_MODE_PROPERTY ) String testMode
) throws Exception {
int next;
if ( inTestMode( testMode ) ) {
LOG.info( "Calling /run/next in test mode ..." );
return Response.ok( 1 ).build();
}
LOG.info( "Calling /run/next ..." );
Preconditions.checkNotNull( commitId, "The commitId should not be null." );
try {
next = runDao.getNextRunNumber( commitId );
}
catch ( IndexMissingException e ) {
LOG.warn( "Got an index missing exception while looking up the next run number." );
return Response.ok( 0 ).build();
}
catch ( Exception e ) {
LOG.error( "Failed to get the next run number for commitId = " + commitId, e );
return Response.status( Response.Status.INTERNAL_SERVER_ERROR ).entity( e.getMessage() ).build();
}
LOG.info( "Next run number to return is {}", next );
return Response.ok( next ).build();
}
@GET
@Path( "/completed" )
@Consumes( MediaType.APPLICATION_JSON )
@Produces( MediaType.APPLICATION_JSON )
public Response completed(
@QueryParam( USERNAME ) String username,
@QueryParam( RUNNER_HOSTNAME ) String runnerHost,
@QueryParam( COMMIT_ID ) String commitId,
@QueryParam( MODULE_GROUPID ) String groupId,
@QueryParam( MODULE_ARTIFACTID ) String artifactId,
@QueryParam( MODULE_VERSION ) String version,
@QueryParam( RUN_NUMBER ) Integer runNumber,
@QueryParam( TEST_CLASS ) String testClass,
@Nullable @QueryParam( TestMode.TEST_MODE_PROPERTY ) String testMode
) throws Exception {
LOG.warn( "Calling completed ..." );
if( inTestMode( testMode ) ) {
LOG.info( "Calling /run/completed in test mode ..." );
LOG.info( "{} is {}", RUNNER_HOSTNAME, runnerHost );
LOG.info( "{} is {}", COMMIT_ID, commitId );
LOG.info( "{} is {}", MODULE_GROUPID, groupId );
LOG.info( "{} is {}", MODULE_ARTIFACTID, artifactId );
LOG.info( "{} is {}", MODULE_VERSION, version );
LOG.info( "{} is {}", RUN_NUMBER, runNumber );
LOG.info( "{} is {}", TEST_CLASS, testClass );
return Response.status( Response.Status.CREATED ).entity( true ).build();
}
LOG.info( "/run/completed called ..." );
String moduleId = BasicModule.createId( groupId, artifactId, version );
Collection<Runner> runners = runnerDao.getRunners( username, commitId, moduleId );
Collection<Run> runs = runDao.getMap( commitId, runNumber, testClass, runners ).values() ;
Boolean allFinished = runs.size() == runners.size();
return Response.status( Response.Status.CREATED ).entity( allFinished ).build();
}
@SuppressWarnings( "unchecked" )
@POST
@Path( "/store" )
@Consumes( MediaType.MULTIPART_FORM_DATA )
@Produces( MediaType.APPLICATION_JSON )
public Response store(
@QueryParam( RUNNER_HOSTNAME ) String runnerHostName,
@QueryParam( COMMIT_ID ) String commitId,
@QueryParam( RUN_ID ) String runId,
@QueryParam( RUN_NUMBER ) int runNumber,
@FormDataParam( CONTENT ) InputStream resultsFileInputStream,
@Nullable @QueryParam( TestMode.TEST_MODE_PROPERTY ) String testMode
) throws Exception {
if( inTestMode( testMode ) ) {
LOG.info( "Calling /run/store in test mode ..." );
LOG.info( "{} is {}", RUNNER_HOSTNAME, runnerHostName );
LOG.info( "{} is {}", COMMIT_ID, commitId );
LOG.info( "{} is {}", RUN_ID, runId );
LOG.info( "{} is {}", RUN_NUMBER, runNumber );
return Response.status( Response.Status.CREATED ).entity( SUCCESSFUL_TEST_MESSAGE ).build();
}
LOG.info( "/run/store called ..." );
String message;
JSONObject object = ( JSONObject ) new JSONParser().parse( new InputStreamReader( resultsFileInputStream ) );
String testClass = Util.getString( object, "testClass" );
// First save the summary info
BasicRun run = new BasicRun( runId, commitId, runnerHostName, runNumber, testClass );
run.copyJson( object );
if ( runDao.save( run ) ) {
LOG.info( "Created new Run {} ", run );
}
else {
message = "Failed to create new Run";
LOG.warn( message );
throw new IllegalStateException( message );
}
// Here is the list of BasicRunResults
JSONArray runResults = ( JSONArray ) object.get( "runResults" );
Iterator<JSONObject> iterator = runResults.iterator();
while( iterator.hasNext() ) {
JSONObject jsonResult = iterator.next();
int failureCount = Util.getInt( jsonResult, "failureCount" );
BasicRunResult runResult = new BasicRunResult(
runId,
Util.getInt( jsonResult, "runCount"),
Util.getInt( jsonResult, "runTime" ),
Util.getInt( jsonResult, "ignoreCount" ),
failureCount
);
if( failureCount != 0 ) {
try {
for( Object result : runResults ) {
JSONObject failures = ( JSONObject ) result;
JSONArray obj = ( JSONArray ) failures.get( "failures" );
runResult.setFailures( obj.toJSONString() );
LOG.info( "Saving run results into Elastic Search." );
}
}catch ( Exception e ){
LOG.warn( "Could not serialize runResults JSON object", e );
}
}
if ( runResultDao.save( runResult ) ) {
LOG.info( "Saved run result.");
}
}
return Response.status( Response.Status.CREATED ).entity( "TRUE" ).build();
}
}