/*
* 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());
}
}
}