/* * 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.client; import co.cask.cdap.api.artifact.ApplicationClass; import co.cask.cdap.api.artifact.ArtifactClasses; import co.cask.cdap.api.artifact.ArtifactScope; import co.cask.cdap.api.artifact.ArtifactVersion; import co.cask.cdap.api.data.schema.Schema; import co.cask.cdap.api.plugin.PluginClass; import co.cask.cdap.api.plugin.PluginPropertyField; import co.cask.cdap.app.program.ManifestFields; import co.cask.cdap.client.artifact.MyApp; import co.cask.cdap.client.artifact.plugin.Plugin1; import co.cask.cdap.client.common.ClientTestBase; import co.cask.cdap.client.util.RESTClient; import co.cask.cdap.common.ArtifactNotFoundException; import co.cask.cdap.common.BadRequestException; import co.cask.cdap.common.NotFoundException; import co.cask.cdap.internal.io.ReflectionSchemaGenerator; import co.cask.cdap.internal.test.AppJarHelper; import co.cask.cdap.internal.test.PluginJarHelper; import co.cask.cdap.proto.Id; import co.cask.cdap.proto.artifact.ApplicationClassInfo; import co.cask.cdap.proto.artifact.ApplicationClassSummary; import co.cask.cdap.proto.artifact.ArtifactInfo; import co.cask.cdap.proto.artifact.ArtifactRange; import co.cask.cdap.proto.artifact.ArtifactSummary; import co.cask.cdap.proto.artifact.PluginInfo; import co.cask.cdap.proto.artifact.PluginSummary; import co.cask.cdap.test.XSlowTests; import com.google.common.collect.ImmutableMap; import com.google.common.collect.ImmutableSet; import com.google.common.collect.Lists; import com.google.common.collect.Sets; import com.google.common.io.InputSupplier; import org.apache.twill.filesystem.LocalLocationFactory; import org.apache.twill.filesystem.Location; import org.junit.Assert; import org.junit.Before; import org.junit.ClassRule; import org.junit.Test; import org.junit.experimental.categories.Category; import org.junit.rules.TemporaryFolder; import java.io.ByteArrayInputStream; import java.io.IOException; import java.io.InputStream; import java.util.Collections; import java.util.HashSet; import java.util.Map; import java.util.Set; import java.util.jar.Manifest; /** * Test for {@link ArtifactClient} */ @Category(XSlowTests.class) public class ArtifactClientTestRun extends ClientTestBase { private static final InputSupplier<InputStream> DUMMY_SUPPLIER = new InputSupplier<InputStream>() { @Override public InputStream getInput() throws IOException { return new ByteArrayInputStream(new byte[]{}); } }; @ClassRule public static TemporaryFolder tmpFolder = new TemporaryFolder(); private ArtifactClient artifactClient; @Before public void setUp() throws Throwable { super.setUp(); artifactClient = new ArtifactClient(clientConfig, new RESTClient(clientConfig)); for (ArtifactSummary artifactSummary : artifactClient.list(Id.Namespace.DEFAULT)) { artifactClient.delete( Id.Artifact.from(Id.Namespace.DEFAULT, artifactSummary.getName(), artifactSummary.getVersion())); } } @Test public void testNotFound() throws Exception { Id.Artifact ghostId = Id.Artifact.from(Id.Namespace.DEFAULT, "ghost", "1.0.0"); try { artifactClient.list(Id.Namespace.from("ghost")); Assert.fail(); } catch (NotFoundException e) { // expected } try { artifactClient.getArtifactInfo(ghostId); Assert.fail(); } catch (ArtifactNotFoundException e) { // expected } try { artifactClient.listVersions(ghostId.getNamespace(), ghostId.getName()); Assert.fail(); } catch (ArtifactNotFoundException e) { // expected } // test adding an artifact that extends a non-existent artifact Set<ArtifactRange> parents = Sets.newHashSet( new ArtifactRange(ghostId.getNamespace(), ghostId.getName(), new ArtifactVersion("1"), new ArtifactVersion("2"))); try { artifactClient.add(Id.Namespace.DEFAULT, "abc", DUMMY_SUPPLIER, "1.0.0", parents); Assert.fail(); } catch (NotFoundException e) { // expected } try { artifactClient.getPluginTypes(ghostId); Assert.fail(); } catch (ArtifactNotFoundException e) { // expected } try { artifactClient.getPluginSummaries(ghostId, "type"); Assert.fail(); } catch (ArtifactNotFoundException e) { // expected } try { artifactClient.getPluginInfo(ghostId, "type", "name"); Assert.fail(); } catch (NotFoundException e) { // expected } } @Test public void testAddArtifactBadIds() throws Exception { // test bad version try { artifactClient.add(Id.Namespace.DEFAULT, "abc", DUMMY_SUPPLIER, "1/0.0"); Assert.fail(); } catch (BadRequestException e) { // expected } // test bad name try { artifactClient.add(Id.Namespace.DEFAULT, "ab:c", DUMMY_SUPPLIER, "1.0.0"); Assert.fail(); } catch (BadRequestException e) { // expected } } @Test public void testAddSelfExtendingThrowsBadRequest() throws Exception { try { artifactClient.add(Id.Namespace.DEFAULT, "abc", DUMMY_SUPPLIER, "1.0.0", Sets.newHashSet( new ArtifactRange(Id.Namespace.DEFAULT, "abc", new ArtifactVersion("1.0.0"), new ArtifactVersion("2.0.0")) )); Assert.fail(); } catch (BadRequestException e) { // expected } } @Test public void testArtifacts() throws Exception { // add 2 versions of an artifact with an application Id.Artifact myapp1Id = Id.Artifact.from(Id.Namespace.DEFAULT, "myapp", "1.0.0"); Id.Artifact myapp2Id = Id.Artifact.from(Id.Namespace.DEFAULT, "myapp", "2.0.0"); LocalLocationFactory locationFactory = new LocalLocationFactory(tmpFolder.newFolder()); Manifest manifest = new Manifest(); manifest.getMainAttributes().put(ManifestFields.BUNDLE_VERSION, "2.0.0"); final Location appJarLoc = AppJarHelper.createDeploymentJar(locationFactory, MyApp.class, manifest); InputSupplier<InputStream> inputSupplier = new InputSupplier<InputStream>() { @Override public InputStream getInput() throws IOException { return appJarLoc.getInputStream(); } }; artifactClient.add(myapp1Id.getNamespace(), myapp1Id.getName(), inputSupplier, myapp1Id.getVersion().getVersion()); // add some properties Map<String, String> myapp1Properties = ImmutableMap.of("k1", "v1"); artifactClient.writeProperties(myapp1Id, myapp1Properties); // let it derive version from jar manifest, which has bundle-version at 2.0.0 artifactClient.add(myapp2Id.getNamespace(), myapp2Id.getName(), inputSupplier, null, null); // add some properties Map<String, String> myapp2Properties = ImmutableMap.of("k1", "v1", "k2", "v2"); artifactClient.writeProperties(myapp2Id, myapp2Properties); // add an artifact that contains a plugin, but only extends myapp-2.0.0 Id.Artifact pluginId = Id.Artifact.from(Id.Namespace.DEFAULT, "myapp-plugins", "2.0.0"); manifest = new Manifest(); manifest.getMainAttributes().put(ManifestFields.EXPORT_PACKAGE, Plugin1.class.getPackage().getName()); final Location pluginJarLoc = PluginJarHelper.createPluginJar(locationFactory, manifest, Plugin1.class); inputSupplier = new InputSupplier<InputStream>() { @Override public InputStream getInput() throws IOException { return pluginJarLoc.getInputStream(); } }; Set<ArtifactRange> parents = Sets.newHashSet(new ArtifactRange( myapp2Id.getNamespace(), myapp2Id.getName(), myapp2Id.getVersion(), new ArtifactVersion("3.0.0"))); Set<PluginClass> additionalPlugins = Sets.newHashSet(new PluginClass( "jdbc", "mysql", "", "com.mysql.jdbc.Driver", null, Collections.<String, PluginPropertyField>emptyMap())); artifactClient.add(pluginId.getNamespace(), pluginId.getName(), inputSupplier, pluginId.getVersion().getVersion(), parents, additionalPlugins); ArtifactSummary myapp1Summary = new ArtifactSummary(myapp1Id.getName(), myapp1Id.getVersion().getVersion()); ArtifactSummary myapp2Summary = new ArtifactSummary(myapp2Id.getName(), myapp2Id.getVersion().getVersion()); ArtifactSummary pluginArtifactSummary = new ArtifactSummary(pluginId.getName(), pluginId.getVersion().getVersion()); Set<ArtifactSummary> artifacts = Sets.newHashSet(artifactClient.list(Id.Namespace.DEFAULT)); Assert.assertEquals(Sets.newHashSet(myapp1Summary, myapp2Summary, pluginArtifactSummary), artifacts); // list all artifacts named 'myapp' Assert.assertEquals(Sets.newHashSet(myapp1Summary, myapp2Summary), Sets.newHashSet(artifactClient.listVersions(Id.Namespace.DEFAULT, myapp1Id.getName()))); // list all artifacts named 'myapp-plugins' Assert.assertEquals(Sets.newHashSet(pluginArtifactSummary), Sets.newHashSet(artifactClient.listVersions(Id.Namespace.DEFAULT, pluginId.getName()))); // artifacts should be in user scope try { artifactClient.listVersions(Id.Namespace.DEFAULT, pluginId.getName(), ArtifactScope.SYSTEM); Assert.fail(); } catch (ArtifactNotFoundException e) { // expected } // get info about specific artifacts Schema myAppConfigSchema = new ReflectionSchemaGenerator(false).generate(MyApp.Conf.class); ArtifactClasses myAppClasses = ArtifactClasses.builder() .addApp(new ApplicationClass(MyApp.class.getName(), "", myAppConfigSchema)) .build(); // test get myapp-1.0.0 ArtifactInfo myapp1Info = new ArtifactInfo(myapp1Id.getName(), myapp1Id.getVersion().getVersion(), ArtifactScope.USER, myAppClasses, myapp1Properties); Assert.assertEquals(myapp1Info, artifactClient.getArtifactInfo(myapp1Id)); // test get myapp-2.0.0 ArtifactInfo myapp2Info = new ArtifactInfo(myapp2Id.getName(), myapp2Id.getVersion().getVersion(), ArtifactScope.USER, myAppClasses, myapp2Properties); Assert.assertEquals(myapp2Info, artifactClient.getArtifactInfo(myapp2Id)); // test overwriting properties myapp2Properties = ImmutableMap.of("k1", "v3", "k5", "v5"); artifactClient.writeProperties(myapp2Id, myapp2Properties); Assert.assertEquals(myapp2Properties, artifactClient.getArtifactInfo(myapp2Id).getProperties()); // test deleting property artifactClient.deleteProperty(myapp2Id, "k1"); Assert.assertEquals(ImmutableMap.of("k5", "v5"), artifactClient.getArtifactInfo(myapp2Id).getProperties()); // test writing property artifactClient.writeProperty(myapp2Id, "k5", "v4"); Assert.assertEquals(ImmutableMap.of("k5", "v4"), artifactClient.getArtifactInfo(myapp2Id).getProperties()); // test deleting properties artifactClient.deleteProperties(myapp2Id); Assert.assertTrue(artifactClient.getArtifactInfo(myapp2Id).getProperties().isEmpty()); // test get myapp-plugins-2.0.0 Map<String, PluginPropertyField> props = ImmutableMap.of( "x", new PluginPropertyField("x", "", "int", true)); ArtifactClasses pluginClasses = ArtifactClasses.builder() .addPlugin(new PluginClass("callable", "plugin1", "p1 description", Plugin1.class.getName(), "conf", props)) .addPlugins(additionalPlugins) .build(); ArtifactInfo pluginArtifactInfo = new ArtifactInfo(pluginId.getName(), pluginId.getVersion().getVersion(), ArtifactScope.USER, pluginClasses, ImmutableMap.<String, String>of()); Assert.assertEquals(pluginArtifactInfo, artifactClient.getArtifactInfo(pluginId)); // test get all app classes in namespace Set<ApplicationClassSummary> expectedSummaries = ImmutableSet.of( new ApplicationClassSummary(myapp1Summary, MyApp.class.getName()), new ApplicationClassSummary(myapp2Summary, MyApp.class.getName()) ); Set<ApplicationClassSummary> appClassSummaries = Sets.newHashSet( artifactClient.getApplicationClasses(Id.Namespace.DEFAULT)); Assert.assertEquals(expectedSummaries, appClassSummaries); // test get all app classes in namespace with name MyApp.class.getName() Set<ApplicationClassInfo> appClassInfos = Sets.newHashSet( artifactClient.getApplicationClasses(Id.Namespace.DEFAULT, MyApp.class.getName())); Set<ApplicationClassInfo> expectedInfos = ImmutableSet.of( new ApplicationClassInfo(myapp1Summary, MyApp.class.getName(), myAppConfigSchema), new ApplicationClassInfo(myapp2Summary, MyApp.class.getName(), myAppConfigSchema) ); Assert.assertEquals(expectedInfos, appClassInfos); // test get plugin types for myapp-1.0.0. should be empty, since plugins only extends versions [2.0.0 - 3.0.0) Assert.assertTrue(artifactClient.getPluginTypes(myapp1Id).isEmpty()); // test get plugin types for myapp-2.0.0 Assert.assertEquals(Lists.newArrayList("callable", "jdbc"), artifactClient.getPluginTypes(myapp2Id)); // test get plugins of type callable for myapp-2.0.0 PluginSummary pluginSummary = new PluginSummary("plugin1", "callable", "p1 description", Plugin1.class.getName(), pluginArtifactSummary); Assert.assertEquals(Sets.newHashSet(pluginSummary), Sets.newHashSet(artifactClient.getPluginSummaries(myapp2Id, "callable"))); // no plugins of type "runnable" Assert.assertTrue(artifactClient.getPluginSummaries(myapp2Id, "runnable").isEmpty()); // test get plugin details for plugin1 for myapp-2.0.0 PluginInfo pluginInfo = new PluginInfo("plugin1", "callable", "p1 description", Plugin1.class.getName(), pluginArtifactSummary, props, new HashSet<String>()); Assert.assertEquals(Sets.newHashSet(pluginInfo), Sets.newHashSet(artifactClient.getPluginInfo(myapp2Id, "callable", "plugin1"))); } }