/* * Copyright © 2014-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.client; import co.cask.cdap.client.app.FakeApp; import co.cask.cdap.client.app.FakeFlow; import co.cask.cdap.client.app.FakeWorkflow; import co.cask.cdap.client.app.PingService; import co.cask.cdap.client.common.ClientTestBase; import co.cask.cdap.common.NotFoundException; import co.cask.cdap.common.utils.Tasks; import co.cask.cdap.proto.BatchProgram; import co.cask.cdap.proto.BatchProgramResult; import co.cask.cdap.proto.BatchProgramStart; import co.cask.cdap.proto.BatchProgramStatus; import co.cask.cdap.proto.Id; import co.cask.cdap.proto.ProgramRecord; import co.cask.cdap.proto.ProgramType; import co.cask.cdap.proto.RunRecord; import co.cask.cdap.test.XSlowTests; import com.google.common.collect.ImmutableList; import com.google.common.collect.ImmutableMap; import org.junit.Assert; import org.junit.Before; import org.junit.Test; import org.junit.experimental.categories.Category; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import java.io.File; import java.util.ArrayList; import java.util.List; import java.util.concurrent.Callable; import java.util.concurrent.TimeUnit; /** * Test for {@link ProgramClient}. */ @Category(XSlowTests.class) public class ProgramClientTestRun extends ClientTestBase { private static final Logger LOG = LoggerFactory.getLogger(ProgramClientTestRun.class); private ApplicationClient appClient; private ProgramClient programClient; @Before public void setUp() throws Throwable { super.setUp(); appClient = new ApplicationClient(clientConfig); programClient = new ProgramClient(clientConfig); } @Test public void testBatchProgramCalls() throws Exception { Id.Namespace namespace = Id.Namespace.DEFAULT; Id.Application appId = Id.Application.from(namespace, FakeApp.NAME); BatchProgram flow = new BatchProgram(FakeApp.NAME, ProgramType.FLOW, FakeFlow.NAME); BatchProgram service = new BatchProgram(FakeApp.NAME, ProgramType.SERVICE, PingService.NAME); BatchProgram missing = new BatchProgram(FakeApp.NAME, ProgramType.FLOW, "not" + FakeFlow.NAME); appClient.deploy(namespace, createAppJarFile(FakeApp.class)); try { // make a batch call to start multiple programs, one of which does not exist List<BatchProgramStart> programStarts = ImmutableList.of( new BatchProgramStart(flow), new BatchProgramStart(service), new BatchProgramStart(missing) ); List<BatchProgramResult> results = programClient.start(namespace, programStarts); // check that we got a 200 for programs that exist, and a 404 for the one that doesn't for (BatchProgramResult result : results) { if (missing.getProgramId().equals(result.getProgramId())) { Assert.assertEquals(404, result.getStatusCode()); } else { Assert.assertEquals(200, result.getStatusCode()); } } // wait for all programs to be in RUNNING status programClient.waitForStatus( Id.Program.from(namespace, flow.getAppId(), flow.getProgramType(), flow.getProgramId()), "RUNNING", 2, TimeUnit.MINUTES); programClient.waitForStatus( Id.Program.from(namespace, service.getAppId(), service.getProgramType(), service.getProgramId()), "RUNNING", 2, TimeUnit.MINUTES); // make a batch call for status of programs, one of which does not exist List<BatchProgram> programs = ImmutableList.of(flow, service, missing); List<BatchProgramStatus> statusList = programClient.getStatus(namespace, programs); // check status is running for programs that exist, and that we get a 404 for the one that doesn't for (BatchProgramStatus status : statusList) { if (missing.getProgramId().equals(status.getProgramId())) { Assert.assertEquals(404, status.getStatusCode()); } else { Assert.assertEquals(200, status.getStatusCode()); Assert.assertEquals("RUNNING", status.getStatus()); } } // make a batch call to stop programs, one of which does not exist results = programClient.stop(namespace, programs); // check that we got a 200 for programs that exist, and a 404 for the one that doesn't for (BatchProgramResult result : results) { if (missing.getProgramId().equals(result.getProgramId())) { Assert.assertEquals(404, result.getStatusCode()); } else { Assert.assertEquals(200, result.getStatusCode()); } } // check programs are in stopped state programs = ImmutableList.of(flow, service); statusList = programClient.getStatus(namespace, programs); for (BatchProgramStatus status : statusList) { Assert.assertEquals(200, status.getStatusCode()); Assert.assertEquals("Program = " + status.getProgramId(), "STOPPED", status.getStatus()); } } finally { try { appClient.delete(appId); } catch (Exception e) { LOG.error("Error deleting app {} during test cleanup.", appId, e); } } } @Test public void testAll() throws Exception { Id.Namespace namespace = Id.Namespace.DEFAULT; Id.Application app = Id.Application.from(namespace, FakeApp.NAME); Id.Flow flow = Id.Flow.from(app, FakeFlow.NAME); Id.Flow.Flowlet flowlet = Id.Flow.Flowlet.from(flow, FakeFlow.FLOWLET_NAME); appClient.deploy(namespace, createAppJarFile(FakeApp.class)); try { // start, scale, and stop flow verifyProgramNames(FakeApp.FLOWS, appClient.listPrograms(app, ProgramType.FLOW)); LOG.info("Starting flow"); programClient.start(flow); assertProgramRunning(programClient, flow); LOG.info("Getting flow history"); programClient.getAllProgramRuns(flow, 0, Long.MAX_VALUE, Integer.MAX_VALUE); LOG.info("Scaling flowlet"); Assert.assertEquals(1, programClient.getFlowletInstances(flowlet)); programClient.setFlowletInstances(flowlet, 3); assertFlowletInstances(programClient, flowlet, 3); List<BatchProgram> statusRequest = new ArrayList<>(); for (ProgramRecord programRecord : appClient.listPrograms(app)) { statusRequest.add(BatchProgram.from(programRecord)); } List<BatchProgramStatus> statuses = programClient.getStatus(namespace, statusRequest); for (BatchProgramStatus status : statuses) { if (status.getProgramType() == ProgramType.FLOW && status.getProgramId().equals(FakeFlow.NAME)) { Assert.assertEquals("RUNNING", status.getStatus()); } else { Assert.assertEquals("STOPPED", status.getStatus()); } } LOG.info("Stopping flow"); programClient.stop(flow); assertProgramStopped(programClient, flow); testWorkflowCommand(Id.Program.from(app, ProgramType.WORKFLOW, FakeWorkflow.NAME)); LOG.info("Starting flow with debug"); programClient.start(flow, true); assertProgramRunning(programClient, flow); programClient.stop(flow); assertProgramStopped(programClient, flow); } finally { try { appClient.delete(app); } catch (Exception e) { LOG.error("Error deleting app {} during test cleanup.", app, e); } } } private void testWorkflowCommand(final Id.Program workflow) throws Exception { // File is used to synchronized between the test case and the FakeWorkflow File doneFile = TMP_FOLDER.newFile(); Assert.assertTrue(doneFile.delete()); LOG.info("Starting workflow"); programClient.start(workflow, false, ImmutableMap.of("done.file", doneFile.getAbsolutePath())); assertProgramRunning(programClient, workflow); Tasks.waitFor(1, new Callable<Integer>() { @Override public Integer call() throws Exception { return programClient.getProgramRuns(workflow, "running", Long.MIN_VALUE, Long.MAX_VALUE, 100).size(); } }, 5, TimeUnit.SECONDS); List<RunRecord> runRecords = programClient.getProgramRuns(workflow, "running", Long.MIN_VALUE, Long.MAX_VALUE, 100); Assert.assertEquals(1, runRecords.size()); final String pid = runRecords.get(0).getPid(); Tasks.waitFor(1, new Callable<Integer>() { @Override public Integer call() throws Exception { try { return programClient.getWorkflowCurrent(workflow.getApplication(), workflow.getId(), pid).size(); } catch (NotFoundException e) { // try again if the 'current' endpoint is not discoverable yet return 0; } } }, 20, TimeUnit.SECONDS); // Signal the FakeWorkflow that execution can be continued by creating temp file Assert.assertTrue(doneFile.createNewFile()); assertProgramStopped(programClient, workflow); LOG.info("Workflow stopped"); } }