/*
* Copyright © 2015 Cask Data, Inc.
*
* Licensed 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 co.cask.cdap.internal.app.services;
import co.cask.cdap.WordCountApp;
import co.cask.cdap.app.runtime.ProgramController;
import co.cask.cdap.app.runtime.ProgramRuntimeService;
import co.cask.cdap.app.runtime.ProgramRuntimeService.RuntimeInfo;
import co.cask.cdap.app.store.Store;
import co.cask.cdap.common.app.RunIds;
import co.cask.cdap.common.conf.Constants;
import co.cask.cdap.common.utils.Tasks;
import co.cask.cdap.internal.app.services.http.AppFabricTestBase;
import co.cask.cdap.internal.app.store.DefaultStore;
import co.cask.cdap.internal.app.store.RunRecordMeta;
import co.cask.cdap.proto.Id;
import co.cask.cdap.proto.ProgramRunStatus;
import co.cask.cdap.proto.ProgramType;
import co.cask.cdap.proto.RunRecord;
import com.google.common.collect.Sets;
import org.apache.http.HttpResponse;
import org.junit.Assert;
import org.junit.BeforeClass;
import org.junit.Test;
import java.util.List;
import java.util.Set;
import java.util.concurrent.Callable;
import java.util.concurrent.TimeUnit;
/**
* Unit test for {@link ProgramLifecycleService}
*/
public class ProgramLifecycleServiceTest extends AppFabricTestBase {
private static ProgramLifecycleService programLifecycleService;
private static Store store;
private static ProgramRuntimeService runtimeService;
@BeforeClass
public static void setup() throws Exception {
programLifecycleService = getInjector().getInstance(ProgramLifecycleService.class);
store = getInjector().getInstance(DefaultStore.class);
runtimeService = getInjector().getInstance(ProgramRuntimeService.class);
}
@Test
public void testInvalidFlowRunRecord() throws Exception {
// Create App with Flow and the deploy
HttpResponse response = deploy(WordCountApp.class, Constants.Gateway.API_VERSION_3_TOKEN, TEST_NAMESPACE1);
Assert.assertEquals(200, response.getStatusLine().getStatusCode());
final Id.Program wordcountFlow1 =
Id.Program.from(TEST_NAMESPACE1, "WordCountApp", ProgramType.FLOW, "WordCountFlow");
// flow is stopped initially
Assert.assertEquals("STOPPED", getProgramStatus(wordcountFlow1));
// start a flow and check the status
startProgram(wordcountFlow1);
waitState(wordcountFlow1, ProgramRunStatus.RUNNING.toString());
// Wait until we have a run record
Tasks.waitFor(1, new Callable<Integer>() {
@Override
public Integer call() throws Exception {
return getProgramRuns(wordcountFlow1, ProgramRunStatus.RUNNING.toString()).size();
}
}, 5, TimeUnit.SECONDS);
// Get the RunRecord
List<RunRecord> runRecords = getProgramRuns(wordcountFlow1, ProgramRunStatus.RUNNING.toString());
Assert.assertEquals(1, runRecords.size());
RunRecord rr = runRecords.get(0);
// Check the RunRecords status
Assert.assertEquals(ProgramRunStatus.RUNNING, rr.getStatus());
// Lets set the runtime info to off
RuntimeInfo runtimeInfo = runtimeService.lookup(wordcountFlow1, RunIds.fromString(rr.getPid()));
ProgramController programController = runtimeInfo.getController();
programController.stop();
Thread.sleep(2000);
// Verify that the status of that run is KILLED
RunRecordMeta runRecordMeta = store.getRun(wordcountFlow1, rr.getPid());
Assert.assertEquals(ProgramRunStatus.KILLED, runRecordMeta.getStatus());
// Use the store manipulate state to be RUNNING
long now = System.currentTimeMillis();
long nowSecs = TimeUnit.MILLISECONDS.toSeconds(now);
store.setStart(wordcountFlow1, rr.getPid(), nowSecs);
// Now check again via Store to assume data store is wrong.
runRecordMeta = store.getRun(wordcountFlow1, rr.getPid());
Assert.assertEquals(ProgramRunStatus.RUNNING, runRecordMeta.getStatus());
// Verify there is NO FAILED run record for the application
runRecords = getProgramRuns(wordcountFlow1, ProgramRunStatus.FAILED.toString());
Assert.assertEquals(0, runRecords.size());
// Lets fix it
Set<String> processedInvalidRunRecordIds = Sets.newHashSet();
programLifecycleService.validateAndCorrectRunningRunRecords(ProgramType.FLOW, processedInvalidRunRecordIds);
// Verify there is one FAILED run record for the application
runRecords = getProgramRuns(wordcountFlow1, ProgramRunStatus.FAILED.toString());
Assert.assertEquals(1, runRecords.size());
rr = runRecords.get(0);
Assert.assertEquals(ProgramRunStatus.FAILED, rr.getStatus());
}
}