/* * 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.api.Config; import co.cask.cdap.client.app.AppReturnsArgs; import co.cask.cdap.client.app.ConfigTestApp; import co.cask.cdap.client.app.ConfigurableProgramsApp; import co.cask.cdap.client.app.ConfigurableProgramsApp2; import co.cask.cdap.client.app.FakeApp; import co.cask.cdap.client.app.FakeDatasetModule; import co.cask.cdap.client.common.ClientTestBase; import co.cask.cdap.common.BadRequestException; import co.cask.cdap.common.DatasetModuleNotFoundException; import co.cask.cdap.common.DatasetNotFoundException; import co.cask.cdap.common.NotFoundException; import co.cask.cdap.proto.ApplicationDetail; import co.cask.cdap.proto.ApplicationRecord; import co.cask.cdap.proto.Id; import co.cask.cdap.proto.ProgramRecord; import co.cask.cdap.proto.ProgramType; import co.cask.cdap.proto.artifact.AppRequest; import co.cask.cdap.proto.artifact.ArtifactSummary; import co.cask.cdap.test.XSlowTests; import com.google.common.collect.ImmutableSet; import com.google.common.collect.Sets; import com.google.common.io.Files; import org.junit.After; 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.IOException; import java.util.List; import java.util.Map; import java.util.Set; import java.util.concurrent.TimeUnit; /** * Test for {@link ApplicationClient}. */ @Category(XSlowTests.class) public class ApplicationClientTestRun extends ClientTestBase { private static final Logger LOG = LoggerFactory.getLogger(ApplicationClientTestRun.class); private ApplicationClient appClient; private DatasetClient datasetClient; private DatasetModuleClient datasetModuleClient; private ArtifactClient artifactClient; @Before public void setUp() throws Throwable { super.setUp(); appClient = new ApplicationClient(clientConfig); datasetClient = new DatasetClient(clientConfig); datasetModuleClient = new DatasetModuleClient(clientConfig); artifactClient = new ArtifactClient(clientConfig); } @After public void cleanup() throws Throwable { // Delete FakeApp's dataset and module so that DatasetClientTestRun works when running both inside a test suite // This is due to DatasetClientTestRun assuming that it is using a blank CDAP instance Id.Namespace namespace = Id.Namespace.DEFAULT; try { datasetClient.delete(Id.DatasetInstance.from(namespace, FakeApp.DS_NAME)); } catch (DatasetNotFoundException e) { // NO-OP } try { datasetModuleClient.delete(Id.DatasetModule.from(namespace, FakeDatasetModule.NAME)); } catch (DatasetModuleNotFoundException e) { // NO-OP } } @Test(expected = IOException.class) public void testInvalidAppConfig() throws Exception { Id.Application appid = Id.Application.from(Id.Namespace.DEFAULT, ConfigTestApp.NAME); appClient.deploy(appid.getNamespace(), createAppJarFile(ConfigTestApp.class, ConfigTestApp.NAME, "1.0.0-SNAPSHOT"), "adad"); } @Test public void testAll() throws Exception { Id.Application app = Id.Application.from(Id.Namespace.DEFAULT, FakeApp.NAME); Assert.assertEquals(0, appClient.list(Id.Namespace.DEFAULT).size()); // deploy app LOG.info("Deploying app"); appClient.deploy(Id.Namespace.DEFAULT, createAppJarFile(FakeApp.class, FakeApp.NAME, "1.0.0-SNAPSHOT")); appClient.waitForDeployed(app, 30, TimeUnit.SECONDS); Assert.assertEquals(1, appClient.list(Id.Namespace.DEFAULT).size()); try { // check program list LOG.info("Checking program list for app"); Map<ProgramType, List<ProgramRecord>> programs = appClient.listProgramsByType(app); verifyProgramNames(FakeApp.FLOWS, programs.get(ProgramType.FLOW)); verifyProgramNames(FakeApp.MAPREDUCES, programs.get(ProgramType.MAPREDUCE)); verifyProgramNames(FakeApp.WORKFLOWS, programs.get(ProgramType.WORKFLOW)); verifyProgramNames(FakeApp.SERVICES, programs.get(ProgramType.SERVICE)); verifyProgramNames(FakeApp.FLOWS, appClient.listPrograms(app, ProgramType.FLOW)); verifyProgramNames(FakeApp.MAPREDUCES, appClient.listPrograms(app, ProgramType.MAPREDUCE)); verifyProgramNames(FakeApp.WORKFLOWS, appClient.listPrograms(app, ProgramType.WORKFLOW)); verifyProgramNames(FakeApp.SERVICES, appClient.listPrograms(app, ProgramType.SERVICE)); verifyProgramNames(FakeApp.FLOWS, appClient.listAllPrograms(Id.Namespace.DEFAULT, ProgramType.FLOW)); verifyProgramNames(FakeApp.MAPREDUCES, appClient.listAllPrograms(Id.Namespace.DEFAULT, ProgramType.MAPREDUCE)); verifyProgramNames(FakeApp.WORKFLOWS, appClient.listAllPrograms(Id.Namespace.DEFAULT, ProgramType.WORKFLOW)); verifyProgramNames(FakeApp.SERVICES, appClient.listAllPrograms(Id.Namespace.DEFAULT, ProgramType.SERVICE)); verifyProgramRecords(FakeApp.ALL_PROGRAMS, appClient.listAllPrograms(Id.Namespace.DEFAULT)); ApplicationDetail appDetail = appClient.get(app); ArtifactSummary expected = new ArtifactSummary(FakeApp.NAME, "1.0.0-SNAPSHOT"); Assert.assertEquals(expected, appDetail.getArtifact()); } finally { // delete app LOG.info("Deleting app"); appClient.delete(app); appClient.waitForDeleted(app, 30, TimeUnit.SECONDS); Assert.assertEquals(0, appClient.list(Id.Namespace.DEFAULT).size()); } } @Test public void testAppConfig() throws Exception { ConfigTestApp.ConfigClass config = new ConfigTestApp.ConfigClass("testStream", "testDataset"); appClient.deploy(Id.Namespace.DEFAULT, createAppJarFile(ConfigTestApp.class), config); Assert.assertEquals(1, appClient.list(Id.Namespace.DEFAULT).size()); Id.Application app = Id.Application.from(Id.Namespace.DEFAULT, ConfigTestApp.NAME); try { appClient.exists(app); } finally { appClient.delete(app); appClient.waitForDeleted(app, 30, TimeUnit.SECONDS); Assert.assertEquals(0, appClient.list(Id.Namespace.DEFAULT).size()); } } @Test public void testAppUpdate() throws Exception { String artifactName = "cfg-programs"; Id.Artifact artifactIdV1 = Id.Artifact.from(Id.Namespace.DEFAULT, artifactName, "1.0.0"); Id.Artifact artifactIdV2 = Id.Artifact.from(Id.Namespace.DEFAULT, artifactName, "2.0.0"); Id.Application appId = Id.Application.from(Id.Namespace.DEFAULT, "ProgramsApp"); artifactClient.add(Id.Namespace.DEFAULT, artifactName, Files.newInputStreamSupplier(createAppJarFile(ConfigurableProgramsApp.class)), "1.0.0"); artifactClient.add(Id.Namespace.DEFAULT, artifactName, Files.newInputStreamSupplier(createAppJarFile(ConfigurableProgramsApp2.class)), "2.0.0"); try { // deploy the app with just the worker ConfigurableProgramsApp.Programs conf = new ConfigurableProgramsApp.Programs(null, "worker1", "stream1", "dataset1"); AppRequest<ConfigurableProgramsApp.Programs> request = new AppRequest<>( new ArtifactSummary(artifactIdV1.getName(), artifactIdV1.getVersion().getVersion()), conf); appClient.deploy(appId, request); // should only have the worker Assert.assertTrue(appClient.listPrograms(appId, ProgramType.FLOW).isEmpty()); Assert.assertEquals(1, appClient.listPrograms(appId, ProgramType.WORKER).size()); // update to use just the flow conf = new ConfigurableProgramsApp.Programs("flow1", null, "stream1", "dataset1"); request = new AppRequest<>( new ArtifactSummary(artifactIdV1.getName(), artifactIdV1.getVersion().getVersion()), conf); appClient.update(appId, request); // should only have the flow Assert.assertTrue(appClient.listPrograms(appId, ProgramType.WORKER).isEmpty()); Assert.assertEquals(1, appClient.listPrograms(appId, ProgramType.FLOW).size()); // check nonexistent app is not found try { appClient.update(Id.Application.from(Id.Namespace.DEFAULT, "ghost"), request); Assert.fail(); } catch (NotFoundException e) { // expected } // check different artifact name is invalid request = new AppRequest<>( new ArtifactSummary("ghost", artifactIdV1.getVersion().getVersion()), conf); try { appClient.update(appId, request); Assert.fail(); } catch (BadRequestException e) { // expected } // check nonexistent artifact is not found request = new AppRequest<>( new ArtifactSummary(artifactIdV1.getName(), "0.0.1"), conf); try { appClient.update(appId, request); Assert.fail(); } catch (NotFoundException e) { // expected } // update artifact version. This version uses a different app class with that can add a service ConfigurableProgramsApp2.Programs conf2 = new ConfigurableProgramsApp2.Programs(null, null, "stream1", "dataset1", "service2"); AppRequest<ConfigurableProgramsApp2.Programs> request2 = new AppRequest<>( new ArtifactSummary(artifactIdV2.getName(), artifactIdV2.getVersion().getVersion()), conf2); appClient.update(appId, request2); // should only have a single service Assert.assertTrue(appClient.listPrograms(appId, ProgramType.WORKER).isEmpty()); Assert.assertTrue(appClient.listPrograms(appId, ProgramType.FLOW).isEmpty()); Assert.assertEquals(1, appClient.listPrograms(appId, ProgramType.SERVICE).size()); } finally { appClient.delete(appId); appClient.waitForDeleted(appId, 30, TimeUnit.SECONDS); artifactClient.delete(artifactIdV1); artifactClient.delete(artifactIdV2); } } @Test public void testArtifactFilter() throws Exception { Id.Application appId1 = Id.Application.from(Id.Namespace.DEFAULT, FakeApp.NAME); Id.Application appId2 = Id.Application.from(Id.Namespace.DEFAULT, "fake2"); Id.Application appId3 = Id.Application.from(Id.Namespace.DEFAULT, "fake3"); try { // app1 should use fake-1.0.0-SNAPSHOT appClient.deploy(Id.Namespace.DEFAULT, createAppJarFile(FakeApp.class, "otherfake", "1.0.0-SNAPSHOT")); appClient.deploy(Id.Namespace.DEFAULT, createAppJarFile(FakeApp.class, "fake", "0.1.0-SNAPSHOT")); // app1 should end up with fake-1.0.0-SNAPSHOT appClient.deploy(Id.Namespace.DEFAULT, createAppJarFile(FakeApp.class, "fake", "1.0.0-SNAPSHOT")); // app2 should use fake-0.1.0-SNAPSHOT appClient.deploy(appId2, new AppRequest<Config>(new ArtifactSummary("fake", "0.1.0-SNAPSHOT"))); // app3 should use otherfake-1.0.0-SNAPSHOT appClient.deploy(appId3, new AppRequest<Config>(new ArtifactSummary("otherfake", "1.0.0-SNAPSHOT"))); appClient.waitForDeployed(appId1, 30, TimeUnit.SECONDS); appClient.waitForDeployed(appId2, 30, TimeUnit.SECONDS); appClient.waitForDeployed(appId3, 30, TimeUnit.SECONDS); // check calls that should return nothing // these don't match anything Assert.assertTrue(appClient.list(Id.Namespace.DEFAULT, "ghost", null).isEmpty()); Assert.assertTrue(appClient.list(Id.Namespace.DEFAULT, (String) null, "1.0.0").isEmpty()); Assert.assertTrue(appClient.list(Id.Namespace.DEFAULT, "ghost", "1.0.0").isEmpty()); // these match one but not the other Assert.assertTrue(appClient.list(Id.Namespace.DEFAULT, "otherfake", "0.1.0-SNAPSHOT").isEmpty()); Assert.assertTrue(appClient.list(Id.Namespace.DEFAULT, "fake", "1.0.0").isEmpty()); // check filter by name only Set<ApplicationRecord> apps = Sets.newHashSet(appClient.list(Id.Namespace.DEFAULT, "fake", null)); Set<ApplicationRecord> expected = ImmutableSet.of( new ApplicationRecord(new ArtifactSummary("fake", "1.0.0-SNAPSHOT"), appId1.getId(), ""), new ApplicationRecord(new ArtifactSummary("fake", "0.1.0-SNAPSHOT"), appId2.getId(), "") ); Assert.assertEquals(expected, apps); apps = Sets.newHashSet(appClient.list(Id.Namespace.DEFAULT, "otherfake", null)); expected = ImmutableSet.of( new ApplicationRecord(new ArtifactSummary("otherfake", "1.0.0-SNAPSHOT"), appId3.getId(), "")); Assert.assertEquals(expected, apps); // check filter by multiple names apps = Sets.newHashSet(appClient.list(Id.Namespace.DEFAULT, ImmutableSet.of("fake", "otherfake"), null)); expected = ImmutableSet.of( new ApplicationRecord(new ArtifactSummary("otherfake", "1.0.0-SNAPSHOT"), appId3.getId(), ""), new ApplicationRecord(new ArtifactSummary("fake", "1.0.0-SNAPSHOT"), appId1.getId(), ""), new ApplicationRecord(new ArtifactSummary("fake", "0.1.0-SNAPSHOT"), appId2.getId(), "")); Assert.assertEquals(expected, apps); // check filter by version only apps = Sets.newHashSet(appClient.list(Id.Namespace.DEFAULT, (String) null, "0.1.0-SNAPSHOT")); expected = ImmutableSet.of( new ApplicationRecord(new ArtifactSummary("fake", "0.1.0-SNAPSHOT"), appId2.getId(), "") ); Assert.assertEquals(expected, apps); apps = Sets.newHashSet(appClient.list(Id.Namespace.DEFAULT, (String) null, "1.0.0-SNAPSHOT")); expected = ImmutableSet.of( new ApplicationRecord(new ArtifactSummary("fake", "1.0.0-SNAPSHOT"), appId1.getId(), ""), new ApplicationRecord(new ArtifactSummary("otherfake", "1.0.0-SNAPSHOT"), appId3.getId(), "") ); Assert.assertEquals(expected, apps); // check filter by both apps = Sets.newHashSet(appClient.list(Id.Namespace.DEFAULT, "fake", "0.1.0-SNAPSHOT")); expected = ImmutableSet.of( new ApplicationRecord(new ArtifactSummary("fake", "0.1.0-SNAPSHOT"), appId2.getId(), "") ); Assert.assertEquals(expected, apps); } finally { appClient.deleteAll(Id.Namespace.DEFAULT); appClient.waitForDeleted(appId1, 30, TimeUnit.SECONDS); appClient.waitForDeleted(appId2, 30, TimeUnit.SECONDS); appClient.waitForDeleted(appId3, 30, TimeUnit.SECONDS); Assert.assertEquals(0, appClient.list(Id.Namespace.DEFAULT).size()); } } @Test public void testDeleteAll() throws Exception { Id.Namespace namespace = Id.Namespace.DEFAULT; Id.Application app = Id.Application.from(namespace, FakeApp.NAME); Id.Application app2 = Id.Application.from(namespace, AppReturnsArgs.NAME); Assert.assertEquals(0, appClient.list(namespace).size()); try { // deploy first app LOG.info("Deploying first app"); appClient.deploy(namespace, createAppJarFile(FakeApp.class)); appClient.waitForDeployed(app, 30, TimeUnit.SECONDS); Assert.assertEquals(1, appClient.list(namespace).size()); // deploy second app LOG.info("Deploying second app"); appClient.deploy(namespace, createAppJarFile(AppReturnsArgs.class)); appClient.waitForDeployed(app2, 30, TimeUnit.SECONDS); Assert.assertEquals(2, appClient.list(namespace).size()); } finally { appClient.deleteAll(namespace); appClient.waitForDeleted(app, 30, TimeUnit.SECONDS); appClient.waitForDeleted(app2, 30, TimeUnit.SECONDS); Assert.assertEquals(0, appClient.list(namespace).size()); } } }