YARN-7129. Application Catalog for YARN applications. Contributed by Eric Yang

This commit is contained in:
Billie Rinaldi 2019-03-04 18:20:21 -08:00
parent 686c0141ef
commit 2576401928
82 changed files with 15702 additions and 3 deletions

View File

@ -465,6 +465,19 @@
<type>test-jar</type>
</dependency>
<dependency>
<groupId>org.apache.hadoop</groupId>
<artifactId>hadoop-yarn-services-api</artifactId>
<version>${hadoop.version}</version>
</dependency>
<dependency>
<groupId>org.apache.hadoop</groupId>
<artifactId>hadoop-yarn-applications-catalog-webapp</artifactId>
<version>${hadoop.version}</version>
<type>war</type>
</dependency>
<dependency>
<groupId>org.apache.hadoop</groupId>
<artifactId>hadoop-mapreduce-client-jobclient</artifactId>

View File

@ -0,0 +1,50 @@
<!--
Licensed to the Apache Software Foundation (ASF) under one or more
contributor license agreements. See the NOTICE file distributed with
this work for additional information regarding copyright ownership.
The ASF licenses this file to You 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.
-->
# Apache Hadoop YARN Application Catalog
## Introduction
YARN Application Catalog is application catalog for
deploying docker enabled cloud application on Hadoop.
check it out:
```bash
git clone https://github.com/apache/hadoop.git
```
## Prerequisites
* Firefox or Chrome
* [npm](https://www.npmjs.org)
* [nodejs](http://nodejs.org)
* [JDK](http://www.oracle.com/technetwork/java/javaee/downloads/index.html)
* [IDE](http://www.jetbrains.com/)
* [bower](http://bower.io)
* [PhantomJs](http://phantomjs.org) or `brew install phantomjs`
* [Docker](http://docker.io)
## Installation
```bash
mvn package
```
When running this command a couple of things happen:
* YARN Application Catalog web application is built
* YARN Application Docker image is built
## Status of the project
See Apache [JIRA](http://issues.apache.org/jira/browse/HADOOP)

View File

@ -0,0 +1,37 @@
# Licensed to the Apache Software Foundation (ASF) under one
# or more contributor license agreements. See the NOTICE file
# distributed with this work for additional information
# regarding copyright ownership. The ASF licenses this file
# to you 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.
FROM centos:7
RUN yum -y install tomcat lsof krb5-workstation sssd-client curl
SHELL ["/bin/bash", "-o", "pipefail", "-c"]
RUN mkdir -p /opt/apache/solr && \
curl -SL http://archive.apache.org/dist/lucene/solr/6.6.0/solr-6.6.0.tgz | \
tar -xzC /opt/apache/solr --strip 1
COPY src/main/scripts/setup-image.sh /setup-image.sh
COPY src/main/resources/samples.xml /tmp/samples.xml
COPY src/main/resources/jaas.config /etc/tomcat/jaas.config.template
COPY src/main/scripts/entrypoint.sh /usr/bin/entrypoint.sh
COPY target/ROOT.war /var/lib/tomcat/webapps/ROOT.war
RUN chmod 755 /setup-image.sh
RUN chmod 755 /usr/bin/entrypoint.sh
RUN /setup-image.sh
EXPOSE 8080
EXPOSE 8983
WORKDIR /
ENTRYPOINT ["/usr/bin/entrypoint.sh" ]

View File

@ -0,0 +1,43 @@
<!---
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. See accompanying LICENSE file.
-->
# Apache Hadoop YARN AppCatalog Docker Image
## Introduction
AppCatalog Docker image is pre-packaged docker container for Hadoop Application Catalog.
check it out:
```
git clone https://github.com/apache/hadoop.git
```
## Compile
```
mvn package
```
## Run
```
docker run -d -p 8080:8080 -p 8983:8983 hadoop/appcatalog-docker:1.0-SNAPSHOT
```
When running this command a couple of things happens:
* Solr server will create appcatalog collection for hosting application catalog
* Sample applications are registered in Embedded Solr
* Tomcat will run appcatalog-webapp on port 8080
User can browse port 8080 to deploy application on a Hadoop cluster.

View File

@ -0,0 +1,113 @@
<?xml version="1.0" encoding="UTF-8"?>
<!--
Licensed to the Apache Software Foundation (ASF) under one or more
contributor license agreements. See the NOTICE file distributed with
this work for additional information regarding copyright ownership.
The ASF licenses this file to You 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.
-->
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/maven-v4_0_0.xsd">
<modelVersion>4.0.0</modelVersion>
<groupId>org.apache.hadoop</groupId>
<artifactId>hadoop-yarn-applications-catalog-docker</artifactId>
<packaging>pom</packaging>
<parent>
<artifactId>hadoop-yarn-applications-catalog</artifactId>
<groupId>org.apache.hadoop</groupId>
<version>3.3.0-SNAPSHOT</version>
</parent>
<name>YARN Application Catalog Docker Image</name>
<url>http://maven.apache.org</url>
<dependencies>
<dependency>
<groupId>org.apache.hadoop</groupId>
<artifactId>hadoop-yarn-applications-catalog-webapp</artifactId>
<version>${project.version}</version>
<type>war</type>
</dependency>
</dependencies>
<build>
<plugins>
<plugin>
<groupId>org.apache.rat</groupId>
<artifactId>apache-rat-plugin</artifactId>
<configuration>
<excludes>
<exclude>src/main/resources/jaas.config</exclude>
</excludes>
</configuration>
</plugin>
</plugins>
</build>
<profiles>
<profile>
<activation>
<file>
<exists>/var/run/docker.sock</exists>
</file>
</activation>
<id>application-catalog-docker-image</id>
<build>
<plugins>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-dependency-plugin</artifactId>
<version>3.0.1</version>
<executions>
<execution>
<id>copy-dependencies</id>
<phase>package</phase>
<goals>
<goal>copy</goal>
</goals>
<configuration>
<artifactItems>
<artifactItem>
<groupId>org.apache.hadoop</groupId>
<artifactId>hadoop-yarn-applications-catalog-webapp</artifactId>
<version>${project.version}</version>
<destFileName>ROOT.war</destFileName>
<type>war</type>
</artifactItem>
</artifactItems>
<outputDirectory>${project.build.directory}</outputDirectory>
<overWriteReleases>false</overWriteReleases>
<overWriteSnapshots>true</overWriteSnapshots>
<excludeTransitive>true</excludeTransitive>
</configuration>
</execution>
</executions>
</plugin>
<plugin>
<groupId>com.spotify</groupId>
<artifactId>dockerfile-maven-plugin</artifactId>
<version>1.3.4</version>
<executions>
<execution>
<id>default</id>
<goals>
<goal>build</goal>
</goals>
</execution>
</executions>
<configuration>
<repository>apache/hadoop-yarn-applications-catalog-docker</repository>
<tag>${project.version}</tag>
</configuration>
</plugin>
</plugins>
</build>
</profile>
</profiles>
</project>

View File

@ -0,0 +1,9 @@
com.sun.security.jgss.krb5.initiate {
com.sun.security.auth.module.Krb5LoginModule required
doNotPrompt=true
principal="${PRINCIPAL}"
useKeyTab=true
keyTab="${KEYTAB}"
storeKey=true
useTicketCache=false;
};

View File

@ -0,0 +1,135 @@
<?xml version="1.0" encoding="UTF-8"?>
<!--
Licensed to the Apache Software Foundation (ASF) under one or more
contributor license agreements. See the NOTICE file distributed with
this work for additional information regarding copyright ownership.
The ASF licenses this file to You 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.
-->
<add>
<doc>
<field name="id">appStore_1</field>
<field name="type_s">AppStoreEntry</field>
<field name="org_s">Apache</field>
<field name="name_s">Http Server Project</field>
<field name="desc_s">The Number One HTTP Server On The Internet</field>
<field name="like_i">50000</field>
<field name="download_i">100000</field>
<field name="yarnfile_s">{
"name": "httpd",
"version": "1.0",
"components" :
[
{
"name": "httpd",
"number_of_containers": 2,
"artifact": {
"id": "centos/httpd-24-centos7:latest",
"type": "DOCKER"
},
"resource": {
"cpus": 1,
"memory": "256"
},
"configuration": {
"env": {
"YARN_CONTAINER_RUNTIME_DOCKER_RUN_OVERRIDE_DISABLE":"true",
"YARN_CONTAINER_RUNTIME_DOCKER_PORTS_MAPPING":"8080:8080"
},
"properties": {
}
}
}
],
"quicklinks": {
"Httpd UI": "http://httpd.${SERVICE_NAME}.${USER}.${DOMAIN}:8080/"
}
}
</field>
</doc>
<doc>
<field name="id">appStore_2</field>
<field name="type_s">AppStoreEntry</field>
<field name="org_s">Jenkins-ci.org</field>
<field name="name_s">Jenkins</field>
<field name="desc_s">The leading open source automation server</field>
<field name="like_i">100000</field>
<field name="download_i">1000000</field>
<field name="yarnfile_s">{
"name": "httpd",
"version": "1.0",
"components" :
[
{
"name": "jenkins",
"number_of_containers": 1,
"artifact": {
"id": "jenkins/jenkins:latest",
"type": "DOCKER"
},
"resource": {
"cpus": 1,
"memory": "1024"
},
"run_privileged_container": true,
"configuration": {
"env": {
"YARN_CONTAINER_RUNTIME_DOCKER_RUN_OVERRIDE_DISABLE":"true",
"YARN_CONTAINER_RUNTIME_DOCKER_PORTS_MAPPING":"8080:8080"
},
"properties": {
}
}
}
],
"quicklinks": {
"Jenkins UI": "http://jenkins.${SERVICE_NAME}.${USER}.${DOMAIN}:8080/"
}
}
</field>
</doc>
<doc>
<field name="id">appStore_3</field>
<field name="type_s">AppStoreEntry</field>
<field name="org_s">Docker</field>
<field name="name_s">Registry</field>
<field name="desc_s">The Docker Registry 2.0 implementation for storing and distributing Docker images.</field>
<field name="like_i">360</field>
<field name="download_i">1</field>
<field name="yarnfile_s">{
"name": "docker-registry",
"version": "1.0",
"components" :
[
{
"name": "registry",
"number_of_containers": 1,
"artifact": {
"id": "registry:latest",
"type": "DOCKER"
},
"resource": {
"cpus": 1,
"memory": "1024"
},
"configuration": {
"env": {
"YARN_CONTAINER_RUNTIME_DOCKER_RUN_OVERRIDE_DISABLE":"true",
"YARN_CONTAINER_RUNTIME_DOCKER_PORTS_MAPPING":"5000:5000"
},
"properties": {
}
}
}
]
}
</field>
</doc>
</add>

View File

@ -0,0 +1,45 @@
#!/bin/bash
# Licensed to the Apache Software Foundation (ASF) under one or more
# contributor license agreements. See the NOTICE file distributed with
# this work for additional information regarding copyright ownership.
# The ASF licenses this file to You 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.
template_generator() {
REGEX='(\$\{[a-zA-Z_][a-zA-Z_0-9]*\})'
if [ -e "$2" ]; then
mv -f "$2" "$2.bak"
fi
while IFS='' read -r line || [[ -n "$line" ]]; do
while [[ "$line" =~ $REGEX ]] ; do
LHS=${BASH_REMATCH[1]}
RHS="$(eval echo "\"$LHS\"")"
line=${line//$LHS/$RHS}
done
echo "$line" >> "$2"
done < "$1"
}
export JAVA_HOME=/usr/lib/jvm/jre
export HADOOP_CONF_DIR=/etc/hadoop/conf
/opt/apache/solr/bin/solr start -p 8983 -force
/opt/apache/solr/bin/solr create_core -c appcatalog -force
/opt/apache/solr/bin/post -c appcatalog /tmp/samples.xml
if [ -d /etc/hadoop/conf ]; then
sed -i.bak 's/shared.loader=.*$/shared.loader=\/etc\/hadoop\/conf/g' /etc/tomcat/catalina.properties
fi
if [ -e "$KEYTAB" ]; then
export JAVA_OPTS="$JAVA_OPTS -Djava.security.auth.login.config=/etc/tomcat/jaas.config -Djava.security.krb5.conf=/etc/krb5.conf -Djavax.security.auth.useSubjectCredsOnly=false"
template_generator /etc/tomcat/jaas.config.template /etc/tomcat/jaas.config
fi
/usr/libexec/tomcat/server start

View File

@ -0,0 +1,20 @@
#!/bin/bash
# Licensed to the Apache Software Foundation (ASF) under one or more
# contributor license agreements. See the NOTICE file distributed with
# this work for additional information regarding copyright ownership.
# The ASF licenses this file to You 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.
mkdir -p /etc/hadoop
chmod -R 777 /opt/apache/solr/server/logs /var/log/tomcat /var/cache/tomcat /var/lib/tomcat/webapps /opt/apache/solr/server/solr
chmod 777 /etc/tomcat

View File

@ -0,0 +1,51 @@
<!---
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. See accompanying LICENSE file.
-->
# Apache Hadoop YARN Application Catalog
## Introduction
Hadoop YARN Application Catalog is application catalog for
deploying docker enabled cloud application on Hadoop.
check it out:
```bash
git clone https://github.com/apache/hadoop.git
```
## Prerequisites
* Firefox or Chrome
* [npm](https://www.npmjs.org)
* [nodejs](http://nodejs.org)
* [JDK](http://www.oracle.com/technetwork/java/javaee/downloads/index.html)
* [IDE](http://www.jetbrains.com/)
* [bower](http://bower.io)
* [PhantomJs](http://phantomjs.org) or `brew install phantomjs`
* [Application Server](https://glassfish.java.net/download.html) or `brew install glassfish`
## Installation
```bash
mvn package
```
When running this command a couple of things happen:
* Npm and yarnpkg install will be run
* JSLint will be run in src/main/javascript sources
* Javascript will be minified
* All the other standard maven phases.
## Status of the project
See Apache [JIRA](http://issues.apache.org/jira/browse/YARN)

View File

@ -0,0 +1,20 @@
<?xml version="1.0" encoding="UTF-8"?>
<!--
Licensed to the Apache Software Foundation (ASF) under one or more
contributor license agreements. See the NOTICE file distributed with
this work for additional information regarding copyright ownership.
The ASF licenses this file to You 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.
-->
<FindBugsFilter>
</FindBugsFilter>

View File

@ -0,0 +1,39 @@
{
"name": "AppCatalog",
"private": true,
"version": "0.0.0",
"description": "YARN App Catalog",
"repository": "git://git.apache.org/hadoop.git",
"license": "Apache 2.0",
"dependencies": {
"jquery": "3.3.1",
"angular-loader": "~1.6.4",
"angular-mocks": "~1.6.4",
"angular-route": "~1.6.4",
"angular": "~1.6.4",
"bootstrap": "~3.3.7"
},
"devDependencies": {
"http-server": "^0.6.1",
"requirejs": "^2.1.0",
"karma": "4.0.0",
"karma-requirejs": "^0.2.2",
"karma-script-launcher": "^0.1.0",
"karma-chrome-launcher": "^0.1.4",
"karma-firefox-launcher": "^0.1.3",
"karma-jasmine": "^0.1.5",
"karma-junit-reporter": "^0.2.2",
"shelljs": "^0.2.6",
"apidoc": "0.17.7"
},
"scripts": {
"prestart": "npm install & mvn clean package",
"start": "http-server target/app -a localhost -p 8000",
"pretest": "npm install",
"test": "karma start src/test/javascript/karma.conf.js",
"test-single-run": "karma start src/test/javascript/karma.conf.js --single-run",
"preupdate-webdriver": "npm install",
"update-webdriver": "webdriver-manager update",
"update-index-async": "node -e \"require('shelljs/global'); sed('-i', /\\/\\/@@NG_LOADER_START@@[\\s\\S]*\\/\\/@@NG_LOADER_END@@/, '//@@NG_LOADER_START@@\\n' + cat('src/main/webapp/vendor/angular-loader/angular-loader.min.js') + '\\n//@@NG_LOADER_END@@', 'src/main/webapp/index.html');\""
}
}

View File

@ -0,0 +1,498 @@
<?xml version="1.0" encoding="UTF-8"?>
<!--
Licensed to the Apache Software Foundation (ASF) under one or more
contributor license agreements. See the NOTICE file distributed with
this work for additional information regarding copyright ownership.
The ASF licenses this file to You 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.
-->
<project xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns="http://maven.apache.org/POM/4.0.0"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/maven-v4_0_0.xsd">
<modelVersion>4.0.0</modelVersion>
<groupId>org.apache.hadoop</groupId>
<artifactId>hadoop-yarn-applications-catalog-webapp</artifactId>
<packaging>war</packaging>
<parent>
<artifactId>hadoop-yarn-applications-catalog</artifactId>
<groupId>org.apache.hadoop</groupId>
<version>3.3.0-SNAPSHOT</version>
</parent>
<name>YARN Application Catalog Webapp</name>
<url>http://hadoop.apache.org</url>
<properties>
<artifact.name>app</artifact.name>
<endorsed.dir>${project.build.directory}/endorsed</endorsed.dir>
<vendor.loc>target/generated-sources/vendor</vendor.loc>
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
<dependency.locations.enabled>false</dependency.locations.enabled>
<powermock.version>1.5.6</powermock.version>
<solr.version>6.3.0</solr.version>
</properties>
<dependencies>
<dependency>
<groupId>junit</groupId>
<artifactId>junit</artifactId>
<scope>test</scope>
</dependency>
<dependency>
<groupId>io.swagger</groupId>
<artifactId>swagger-annotations</artifactId>
</dependency>
<dependency>
<groupId>org.mockito</groupId>
<artifactId>mockito-all</artifactId>
<version>1.9.5</version>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.powermock</groupId>
<artifactId>powermock-module-junit4</artifactId>
<version>${powermock.version}</version>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.powermock</groupId>
<artifactId>powermock-api-mockito</artifactId>
<version>${powermock.version}</version>
<exclusions>
<exclusion>
<groupId>org.mockito</groupId>
<artifactId>mockito-all</artifactId>
</exclusion>
</exclusions>
<scope>test</scope>
</dependency>
<dependency>
<groupId>javax</groupId>
<artifactId>javaee-api</artifactId>
<version>7.0</version>
<scope>provided</scope>
</dependency>
<dependency>
<groupId>org.glassfish.jersey.media</groupId>
<artifactId>jersey-media-json-jackson</artifactId>
<version>2.12</version>
<scope>test</scope>
<exclusions>
<exclusion>
<groupId>org.javassist</groupId>
<artifactId>javassist</artifactId>
</exclusion>
<exclusion>
<groupId>com.fasterxml.jackson.jaxrs</groupId>
<artifactId>jackson-jaxrs-base</artifactId>
</exclusion>
</exclusions>
</dependency>
<dependency>
<groupId>asm</groupId>
<artifactId>asm-commons</artifactId>
<version>3.3.1</version>
</dependency>
<dependency>
<groupId>com.sun.jersey</groupId>
<artifactId>jersey-json</artifactId>
<version>${jersey.version}</version>
</dependency>
<dependency>
<groupId>org.apache.solr</groupId>
<artifactId>solr-solrj</artifactId>
<version>${solr.version}</version>
<exclusions>
<exclusion>
<groupId>org.slf4j</groupId>
<artifactId>jcl-over-slf4j</artifactId>
</exclusion>
<exclusion>
<groupId>org.slf4j</groupId>
<artifactId>slf4j-api</artifactId>
</exclusion>
</exclusions>
</dependency>
<dependency>
<groupId>org.apache.solr</groupId>
<artifactId>solr-core</artifactId>
<version>${solr.version}</version>
<exclusions>
<exclusion>
<groupId>org.slf4j</groupId>
<artifactId>jcl-over-slf4j</artifactId>
</exclusion>
<exclusion>
<groupId>org.eclipse.jetty</groupId>
<artifactId>jetty-io</artifactId>
</exclusion>
</exclusions>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.apache.solr</groupId>
<artifactId>solr-test-framework</artifactId>
<version>${solr.version}</version>
<exclusions>
<exclusion>
<groupId>org.slf4j</groupId>
<artifactId>jcl-over-slf4j</artifactId>
</exclusion>
<exclusion>
<groupId>org.eclipse.jetty</groupId>
<artifactId>jetty-io</artifactId>
</exclusion>
</exclusions>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.slf4j</groupId>
<artifactId>jcl-over-slf4j</artifactId>
<version>1.7.25</version>
</dependency>
<dependency>
<groupId>org.slf4j</groupId>
<artifactId>slf4j-api</artifactId>
<version>1.7.25</version>
</dependency>
<dependency>
<groupId>org.apache.hadoop</groupId>
<artifactId>hadoop-common</artifactId>
</dependency>
<dependency>
<groupId>org.apache.hadoop</groupId>
<artifactId>hadoop-auth</artifactId>
</dependency>
<dependency>
<groupId>org.apache.hadoop</groupId>
<artifactId>hadoop-yarn-common</artifactId>
</dependency>
<dependency>
<groupId>org.apache.hadoop</groupId>
<artifactId>hadoop-yarn-services-core</artifactId>
</dependency>
<dependency>
<groupId>org.apache.hadoop</groupId>
<artifactId>hadoop-yarn-services-api</artifactId>
</dependency>
<dependency>
<groupId>com.fasterxml.jackson.core</groupId>
<artifactId>jackson-core</artifactId>
<version>${jackson2.version}</version>
</dependency>
<dependency>
<groupId>com.fasterxml.jackson.core</groupId>
<artifactId>jackson-annotations</artifactId>
<version>${jackson2.version}</version>
</dependency>
<dependency>
<groupId>com.fasterxml.jackson.core</groupId>
<artifactId>jackson-databind</artifactId>
</dependency>
<dependency>
<groupId>com.fasterxml.jackson.jaxrs</groupId>
<artifactId>jackson-jaxrs-json-provider</artifactId>
</dependency>
<dependency>
<groupId>com.fasterxml.jackson.jaxrs</groupId>
<artifactId>jackson-jaxrs-base</artifactId>
<version>${jackson2.version}</version>
</dependency>
</dependencies>
<build>
<finalName>${artifact.name}</finalName>
<resources>
<resource>
<directory>src/main/javascript</directory>
<filtering>true</filtering>
</resource>
<resource>
<directory>src/main/resources</directory>
<filtering>true</filtering>
</resource>
</resources>
<plugins>
<plugin>
<groupId>com.github.eirslett</groupId>
<artifactId>frontend-maven-plugin</artifactId>
<version>1.6</version>
<executions>
<execution>
<id>install node and yarn</id>
<goals>
<goal>install-node-and-yarn</goal>
</goals>
<phase>generate-resources</phase>
<configuration>
<nodeVersion>v8.11.3</nodeVersion>
<yarnVersion>v1.7.0</yarnVersion>
</configuration>
</execution>
<execution>
<id>yarn install</id>
<goals>
<goal>yarn</goal>
</goals>
</execution>
</executions>
</plugin>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-compiler-plugin</artifactId>
<version>3.1</version>
<configuration>
<source>1.8</source>
<target>1.8</target>
<compilerArguments>
<endorseddirs>${endorsed.dir}</endorseddirs>
</compilerArguments>
</configuration>
</plugin>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-surefire-plugin</artifactId>
<version>2.6</version>
<configuration>
<systemPropertyVariables>
<log4j.configuration>file:${project.build.testOutputDirectory}/log4j.properties</log4j.configuration>
</systemPropertyVariables>
</configuration>
</plugin>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-war-plugin</artifactId>
<version>2.4</version>
<configuration>
<failOnMissingWebXml>false</failOnMissingWebXml>
<webResources>
<resource>
<directory>target/generated-sources</directory>
<includes>
<include>vendor/jquery/**</include>
<include>vendor/angular/**</include>
<include>vendor/angular-route/**</include>
<include>vendor/bootstrap/**</include>
</includes>
</resource>
</webResources>
</configuration>
</plugin>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-dependency-plugin</artifactId>
<version>3.0.1</version>
<executions>
<execution>
<phase>validate</phase>
<goals>
<goal>copy</goal>
</goals>
<configuration>
<outputDirectory>${endorsed.dir}</outputDirectory>
<silent>true</silent>
<artifactItems>
<artifactItem>
<groupId>javax</groupId>
<artifactId>javaee-endorsed-api</artifactId>
<version>7.0</version>
<type>jar</type>
</artifactItem>
</artifactItems>
</configuration>
</execution>
</executions>
</plugin>
<plugin>
<groupId>net.alchim31.maven</groupId>
<artifactId>yuicompressor-maven-plugin</artifactId>
<version>1.1</version>
<executions>
<execution>
<id>compress-js</id>
<phase>process-resources</phase>
<goals>
<!--<goal>jslint</goal>-->
<goal>compress</goal>
</goals>
</execution>
</executions>
<configuration>
<failOnWarning>true</failOnWarning>
<!--<outputDirectory>src/main/webapp/js</outputDirectory>-->
<outputDirectory>target/${artifact.name}/js</outputDirectory>
<nosuffix>true</nosuffix>
<excludes>
<exclude>**/js/**/*js</exclude>
<exclude>vendor/**</exclude>
<exclude>**/*min.css</exclude>
<exclude>**/*min.js</exclude>
</excludes>
</configuration>
</plugin>
<plugin>
<groupId>com.github.klieber</groupId>
<artifactId>phantomjs-maven-plugin</artifactId>
<version>0.7</version>
<executions>
<execution>
<goals>
<goal>install</goal>
</goals>
</execution>
</executions>
<configuration>
<version>2.1.1</version>
</configuration>
</plugin>
<plugin>
<groupId>com.github.searls</groupId>
<artifactId>jasmine-maven-plugin</artifactId>
<version>2.2</version>
<executions>
<execution>
<goals>
<goal>test</goal>
<goal>bdd</goal>
</goals>
</execution>
</executions>
<configuration>
<webDriverClassName>org.openqa.selenium.phantomjs.PhantomJSDriver</webDriverClassName>
<webDriverCapabilities>
<capability>
<name>phantomjs.binary.path</name>
<value>${phantomjs.binary}</value>
</capability>
</webDriverCapabilities>
<preloadSources>
<source>${vendor.loc}/jquery/dist/jquery.js</source>
<source>${vendor.loc}/angular/angular.min.js</source>
<source>${vendor.loc}/angular-route/angular-route.min.js</source>
<source>${vendor.loc}/angular-mocks/angular-mocks.js</source>
</preloadSources>
<jsSrcDir>src/main/javascript</jsSrcDir>
<jsTestSrcDir>src/test/javascript</jsTestSrcDir>
<specIncludes>
<include>*Spec.js</include>
</specIncludes>
</configuration>
</plugin>
<plugin>
<artifactId>maven-clean-plugin</artifactId>
<version>2.5</version>
<configuration>
<filesets>
<fileset>
<directory>${vendor.loc}</directory>
</fileset>
</filesets>
</configuration>
</plugin>
<plugin>
<groupId>org.apache.rat</groupId>
<artifactId>apache-rat-plugin</artifactId>
<configuration>
<excludes>
<exclude>src/main/webapp/vendor/**</exclude>
<exclude>src/main/resources/appcatalog.properties</exclude>
<exclude>src/main/webapp//**/*.svg</exclude>
<exclude>src/test/javascript/karma.conf.js</exclude>
<exclude>src/test/resources/configsets/exampleCollection/conf/params.json</exclude>
<exclude>src/test/resources/log4j.properties</exclude>
<exclude>src/test/resources/jaas.config</exclude>
<exclude>.bowerrc</exclude>
<exclude>.yarnrc</exclude>
<exclude>bower.json</exclude>
<exclude>package.json</exclude>
<exclude>yarn.lock</exclude>
<exclude>node/**</exclude>
<exclude>node_modules/**</exclude>
</excludes>
</configuration>
</plugin>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-javadoc-plugin</artifactId>
<configuration>
<excludePackageNames>org.apache.hadoop.yarn.appcatalog</excludePackageNames>
</configuration>
</plugin>
</plugins>
</build>
<!--
<profiles>
<profile>
<id>rest-docs</id>
<activation>
<file>
<exists>${basedir}/node_modules/.bin/apidoc</exists>
</file>
</activation>
<build>
<plugins>
<plugin>
<groupId>org.codehaus.mojo</groupId>
<artifactId>exec-maven-plugin</artifactId>
<version>1.3.2</version>
<executions>
<execution>
<id>apidocs</id>
<phase>package</phase>
<goals>
<goal>exec</goal>
</goals>
<configuration>
<outputFile>${project.build.directory}/apidocs.log</outputFile>
<executable>${basedir}/node/node</executable>
<commandlineArgs>${basedir}/node_modules/.bin/apidoc -i src/main/java -o ${project.build.directory}/site/apidocs</commandlineArgs>
<silent>true</silent>
</configuration>
</execution>
</executions>
</plugin>
</plugins>
</build>
</profile>
</profiles>
-->
<reporting>
<plugins>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-project-info-reports-plugin</artifactId>
<version>2.9</version>
</plugin>
</plugins>
</reporting>
</project>

View File

@ -0,0 +1,60 @@
/*
* Licensed to the Apache Software Foundation (ASF) under one
* or more contributor license agreements. See the NOTICE file
* distributed with this work for additional information
* regarding copyright ownership. The ASF licenses this file
* to you 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 org.apache.hadoop.yarn.appcatalog.application;
import javax.ws.rs.ApplicationPath;
import javax.ws.rs.core.Application;
import org.apache.hadoop.yarn.appcatalog.controller.AppDetailsController;
import java.util.Set;
/**
* Jackson resource configuration class for Application Catalog.
*/
@ApplicationPath("service")
public class AppCatalog extends Application {
@Override
public Set<Class<?>> getClasses() {
final Set<Class<?>> resources = new java.util.HashSet<>();
// following code can be used to customize Jersey 2.0 JSON provider:
try {
final Class<?> jsonProvider =
Class.forName("org.glassfish.jersey.jackson.JacksonFeature");
// Class jsonProvider =
// Class.forName("org.glassfish.jersey.moxy.json.MoxyJsonFeature");
// Class jsonProvider =
// Class.forName("org.glassfish.jersey.jettison.JettisonFeature");
resources.add(jsonProvider);
} catch (final ClassNotFoundException ex) {
ex.printStackTrace();
}
addRestResourceClasses(resources);
return resources;
}
/**
* Add your own resources here.
*/
private void addRestResourceClasses(final Set<Class<?>> resources) {
resources.add(AppDetailsController.class);
}
}

View File

@ -0,0 +1,359 @@
/*
* Licensed to the Apache Software Foundation (ASF) under one
* or more contributor license agreements. See the NOTICE file
* distributed with this work for additional information
* regarding copyright ownership. The ASF licenses this file
* to you 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 org.apache.hadoop.yarn.appcatalog.application;
import java.io.IOException;
import java.io.InputStream;
import java.util.ArrayList;
import java.util.Collection;
import java.util.HashSet;
import java.util.Iterator;
import java.util.List;
import java.util.Properties;
import java.util.Random;
import org.apache.hadoop.yarn.appcatalog.model.AppEntry;
import org.apache.hadoop.yarn.appcatalog.model.AppStoreEntry;
import org.apache.hadoop.yarn.appcatalog.model.Application;
import org.apache.hadoop.yarn.appcatalog.utils.RandomWord;
import org.apache.hadoop.yarn.appcatalog.utils.WordLengthException;
import org.apache.hadoop.yarn.service.api.records.Service;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.apache.solr.client.solrj.SolrClient;
import org.apache.solr.client.solrj.SolrQuery;
import org.apache.solr.client.solrj.SolrServerException;
import org.apache.solr.client.solrj.impl.HttpSolrClient;
import org.apache.solr.client.solrj.response.QueryResponse;
import org.apache.solr.client.solrj.response.UpdateResponse;
import org.apache.solr.common.SolrDocument;
import org.apache.solr.common.SolrInputDocument;
import com.fasterxml.jackson.databind.DeserializationFeature;
import com.fasterxml.jackson.databind.ObjectMapper;
/**
* Driver class for accessing Solr.
*/
public class AppCatalogSolrClient {
private static final Log LOG = LogFactory.getLog(AppCatalogSolrClient.class);
private static String urlString;
public AppCatalogSolrClient() {
// Locate Solr URL
ClassLoader classLoader = Thread.currentThread().getContextClassLoader();
InputStream input =
classLoader.getResourceAsStream("appcatalog.properties");
Properties properties = new Properties();
try {
properties.load(input);
urlString = properties.getProperty("solr_url");
} catch (IOException e) {
LOG.error("Error reading appcatalog configuration: ", e);
}
}
public SolrClient getSolrClient() {
return new HttpSolrClient.Builder(urlString).build();
}
public List<AppStoreEntry> getRecommendedApps() {
List<AppStoreEntry> apps = new ArrayList<AppStoreEntry>();
SolrClient solr = getSolrClient();
SolrQuery query = new SolrQuery();
query.setQuery("*:*");
query.setFilterQueries("type_s:AppStoreEntry");
query.setRows(40);
QueryResponse response;
try {
response = solr.query(query);
Iterator<SolrDocument> list = response.getResults().listIterator();
while (list.hasNext()) {
SolrDocument d = list.next();
AppStoreEntry entry = new AppStoreEntry();
entry.setId(d.get("id").toString());
entry.setOrg(d.get("org_s").toString());
entry.setName(d.get("name_s").toString());
entry.setDesc(d.get("desc_s").toString());
if (d.get("icon_s")!=null) {
entry.setIcon(d.get("icon_s").toString());
}
entry.setLike(Integer.valueOf(d.get("like_i").toString()));
entry.setDownload(Integer.valueOf(d.get("download_i").toString()));
apps.add(entry);
}
} catch (SolrServerException | IOException e) {
LOG.error("Error getting a list of recommended applications: ", e);
}
return apps;
}
public List<AppStoreEntry> search(String keyword) {
List<AppStoreEntry> apps = new ArrayList<AppStoreEntry>();
SolrClient solr = getSolrClient();
SolrQuery query = new SolrQuery();
if (keyword.length()==0) {
query.setQuery("*:*");
query.setFilterQueries("type_s:AppStoreEntry");
} else {
query.setQuery(keyword);
query.setFilterQueries("type_s:AppStoreEntry");
}
query.setRows(40);
QueryResponse response;
try {
response = solr.query(query);
Iterator<SolrDocument> list = response.getResults().listIterator();
while (list.hasNext()) {
SolrDocument d = list.next();
AppStoreEntry entry = new AppStoreEntry();
entry.setId(d.get("id").toString());
entry.setOrg(d.get("org_s").toString());
entry.setName(d.get("name_s").toString());
entry.setDesc(d.get("desc_s").toString());
entry.setLike(Integer.valueOf(d.get("like_i").toString()));
entry.setDownload(Integer.valueOf(d.get("download_i").toString()));
apps.add(entry);
}
} catch (SolrServerException | IOException e) {
LOG.error("Error in searching for applications: ", e);
}
return apps;
}
public List<AppEntry> listAppEntries() {
List<AppEntry> list = new ArrayList<AppEntry>();
ObjectMapper mapper = new ObjectMapper();
mapper.configure(DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES, false);
SolrClient solr = getSolrClient();
SolrQuery query = new SolrQuery();
query.setQuery("*:*");
query.setFilterQueries("type_s:AppEntry");
query.setRows(40);
QueryResponse response;
try {
response = solr.query(query);
Iterator<SolrDocument> appList = response.getResults().listIterator();
while (appList.hasNext()) {
SolrDocument d = appList.next();
AppEntry entry = new AppEntry();
entry.setId(d.get("id").toString());
entry.setName(d.get("name_s").toString());
entry.setApp(d.get("app_s").toString());
entry.setYarnfile(mapper.readValue(d.get("yarnfile_s").toString(),
Service.class));
list.add(entry);
}
} catch (SolrServerException | IOException e) {
LOG.error("Error in listing deployed applications: ", e);
}
return list;
}
public AppStoreEntry findAppStoreEntry(String id) {
AppStoreEntry entry = new AppStoreEntry();
ObjectMapper mapper = new ObjectMapper();
mapper.configure(DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES, false);
SolrClient solr = getSolrClient();
SolrQuery query = new SolrQuery();
query.setQuery("id:" + id);
query.setFilterQueries("type_s:AppStoreEntry");
query.setRows(1);
QueryResponse response;
try {
response = solr.query(query);
Iterator<SolrDocument> appList = response.getResults().listIterator();
while (appList.hasNext()) {
SolrDocument d = appList.next();
entry.setId(d.get("id").toString());
entry.setOrg(d.get("org_s").toString());
entry.setName(d.get("name_s").toString());
entry.setDesc(d.get("desc_s").toString());
entry.setLike(Integer.valueOf(d.get("like_i").toString()));
entry.setDownload(Integer.valueOf(d.get("download_i").toString()));
Service yarnApp = mapper.readValue(d.get("yarnfile_s").toString(),
Service.class);
String name;
try {
Random r = new Random();
int low = 3;
int high = 10;
int seed = r.nextInt(high-low) + low;
int seed2 = r.nextInt(high-low) + low;
name = RandomWord.getNewWord(seed).toLowerCase() + "-" + RandomWord
.getNewWord(seed2).toLowerCase();
} catch (WordLengthException e) {
name = "c" + java.util.UUID.randomUUID().toString().substring(0, 11);
}
yarnApp.setName(name);
entry.setApp(yarnApp);
}
} catch (SolrServerException | IOException e) {
LOG.error("Error in finding deployed application: " + id, e);
}
return entry;
}
public AppEntry findAppEntry(String id) {
AppEntry entry = new AppEntry();
ObjectMapper mapper = new ObjectMapper();
mapper.configure(DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES, false);
SolrClient solr = getSolrClient();
SolrQuery query = new SolrQuery();
query.setQuery("id:" + id);
query.setFilterQueries("type_s:AppEntry");
query.setRows(1);
QueryResponse response;
try {
response = solr.query(query);
Iterator<SolrDocument> appList = response.getResults().listIterator();
while (appList.hasNext()) {
SolrDocument d = appList.next();
entry.setId(d.get("id").toString());
entry.setApp(d.get("app_s").toString());
entry.setName(d.get("name_s").toString());
entry.setYarnfile(mapper.readValue(d.get("yarnfile_s").toString(),
Service.class));
}
} catch (SolrServerException | IOException e) {
LOG.error("Error in finding deployed application: " + id, e);
}
return entry;
}
public void deployApp(String id, Service service) throws SolrServerException,
IOException {
long download = 0;
ObjectMapper mapper = new ObjectMapper();
mapper.configure(DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES, false);
Collection<SolrInputDocument> docs = new HashSet<SolrInputDocument>();
SolrClient solr = getSolrClient();
// Find application information from AppStore
SolrQuery query = new SolrQuery();
query.setQuery("id:" + id);
query.setFilterQueries("type_s:AppStoreEntry");
query.setRows(1);
QueryResponse response = solr.query(query);
Iterator<SolrDocument> appList = response.getResults().listIterator();
AppStoreEntry entry = new AppStoreEntry();
while (appList.hasNext()) {
SolrDocument d = appList.next();
entry.setOrg(d.get("org_s").toString());
entry.setName(d.get("name_s").toString());
entry.setDesc(d.get("desc_s").toString());
entry.setLike(Integer.valueOf(d.get("like_i").toString()));
entry.setDownload(Integer.valueOf(d.get("download_i").toString()));
download = entry.getDownload() + 1;
// Update download count
docs.add(incrementDownload(d, download));
}
// increment download count for application
if (service!=null) {
// Register deployed application instance with AppList
SolrInputDocument request = new SolrInputDocument();
String name = service.getName();
request.addField("type_s", "AppEntry");
request.addField("id", name);
request.addField("name_s", name);
request.addField("app_s", entry.getOrg()+"/"+entry.getName());
request.addField("yarnfile_s", mapper.writeValueAsString(service));
docs.add(request);
}
// Commit Solr changes.
UpdateResponse detailsResponse = solr.add(docs);
if (detailsResponse.getStatus() != 0) {
throw new IOException("Unable to register docker instance "
+ "with application entry.");
}
solr.commit();
}
private SolrInputDocument incrementDownload(SolrDocument doc,
long download) {
Collection<String> names = doc.getFieldNames();
SolrInputDocument s = new SolrInputDocument();
for (String name : names) {
if(!name.equals("_version_")) {
s.addField(name, doc.getFieldValues(name));
}
}
s.setField("download_i", download++);
return s;
}
public void deleteApp(String id) {
SolrClient solr = getSolrClient();
try {
solr.deleteById(id);
solr.commit();
} catch (SolrServerException | IOException e) {
LOG.error("Error in removing deployed application: "+id, e);
}
}
public void register(Application app) throws IOException {
Collection<SolrInputDocument> docs = new HashSet<SolrInputDocument>();
SolrClient solr = getSolrClient();
ObjectMapper mapper = new ObjectMapper();
mapper.configure(DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES, false);
try {
SolrInputDocument buffer = new SolrInputDocument();
buffer.setField("id", java.util.UUID.randomUUID().toString()
.substring(0, 11));
buffer.setField("org_s", app.getOrganization());
buffer.setField("name_s", app.getName());
buffer.setField("desc_s", app.getDescription());
if (app.getIcon() != null) {
buffer.setField("icon_s", app.getIcon());
}
buffer.setField("type_s", "AppStoreEntry");
buffer.setField("like_i", 0);
buffer.setField("download_i", 0);
// Keep only YARN data model for yarnfile field
String yarnFile = mapper.writeValueAsString(app);
LOG.info("app:"+yarnFile);
Service yarnApp = mapper.readValue(yarnFile, Service.class);
buffer.setField("yarnfile_s", mapper.writeValueAsString(yarnApp));
docs.add(buffer);
// Commit Solr changes.
UpdateResponse detailsResponse = solr.add(docs);
if (detailsResponse.getStatus() != 0) {
throw new IOException("Unable to register application " +
"in Application Store.");
}
solr.commit();
} catch (SolrServerException | IOException e) {
throw new IOException("Unable to register application " +
"in Application Store. "+ e.getMessage());
}
}
}

View File

@ -0,0 +1,174 @@
/*
* Licensed to the Apache Software Foundation (ASF) under one
* or more contributor license agreements. See the NOTICE file
* distributed with this work for additional information
* regarding copyright ownership. The ASF licenses this file
* to you 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 org.apache.hadoop.yarn.appcatalog.application;
import java.io.IOException;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.apache.hadoop.conf.Configuration;
import org.apache.hadoop.security.UserGroupInformation;
import org.apache.hadoop.yarn.appcatalog.model.AppEntry;
import org.apache.hadoop.yarn.service.api.records.Service;
import org.apache.hadoop.yarn.service.api.records.KerberosPrincipal;
import org.apache.hadoop.yarn.service.client.ApiServiceClient;
import com.fasterxml.jackson.core.JsonProcessingException;
import com.fasterxml.jackson.databind.DeserializationFeature;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.sun.jersey.api.client.ClientHandlerException;
import com.sun.jersey.api.client.ClientResponse;
import com.sun.jersey.api.client.UniformInterfaceException;
import com.sun.jersey.api.client.config.ClientConfig;
import com.sun.jersey.api.client.config.DefaultClientConfig;
/**
* Driver class for calling YARN Resource Manager REST API.
*/
public class YarnClient {
private static final Log LOG = LogFactory.getLog(YarnClient.class);
private static Configuration conf = new Configuration();
private static ClientConfig getClientConfig() {
ClientConfig config = new DefaultClientConfig();
config.getProperties().put(ClientConfig.PROPERTY_CHUNKED_ENCODING_SIZE, 0);
config.getProperties()
.put(ClientConfig.PROPERTY_BUFFER_RESPONSE_ENTITY_ON_EXCEPTION, true);
return config;
}
private ApiServiceClient asc;
public YarnClient() {
try {
asc = new ApiServiceClient(conf);
} catch (Exception e) {
LOG.error("Error initialize YARN Service Client: {}", e);
}
}
public void createApp(Service app) {
ObjectMapper mapper = new ObjectMapper();
mapper.configure(DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES, false);
ClientResponse response;
try {
boolean useKerberos = UserGroupInformation.isSecurityEnabled();
if (useKerberos) {
KerberosPrincipal kerberos = new KerberosPrincipal();
String[] temp = System.getenv("PRINCIPAL").split("@");
String[] temp2 = temp[0].split("/");
StringBuilder sb = new StringBuilder();
sb.append(temp2[0]);
sb.append("/");
sb.append("_HOST");
sb.append("@");
sb.append(temp[1]);
String keytab = System.getenv("KEYTAB");
if (!keytab.startsWith("file://")) {
keytab = "file://" + keytab;
}
kerberos.setPrincipalName(sb.toString());
kerberos.setKeytab(keytab);
app.setKerberosPrincipal(kerberos);
}
response = asc.getApiClient().post(ClientResponse.class,
mapper.writeValueAsString(app));
if (response.getStatus() >= 299) {
String message = response.getEntity(String.class);
throw new RuntimeException("Failed : HTTP error code : "
+ response.getStatus() + " error: " + message);
}
} catch (UniformInterfaceException | ClientHandlerException
| IOException e) {
LOG.error("Error in deploying application: ", e);
}
}
public void deleteApp(String appInstanceId) {
ClientResponse response;
try {
response = asc.getApiClient(asc.getServicePath(appInstanceId))
.delete(ClientResponse.class);
if (response.getStatus() >= 299) {
String message = response.getEntity(String.class);
throw new RuntimeException("Failed : HTTP error code : "
+ response.getStatus() + " error: " + message);
}
} catch (UniformInterfaceException | ClientHandlerException
| IOException e) {
LOG.error("Error in deleting application: ", e);
}
}
public void restartApp(Service app) throws JsonProcessingException {
ObjectMapper mapper = new ObjectMapper();
mapper.configure(DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES, false);
String appInstanceId = app.getName();
String yarnFile = mapper.writeValueAsString(app);
ClientResponse response;
try {
response = asc.getApiClient(asc.getServicePath(appInstanceId))
.put(ClientResponse.class, yarnFile);
if (response.getStatus() >= 299) {
String message = response.getEntity(String.class);
throw new RuntimeException("Failed : HTTP error code : "
+ response.getStatus() + " error: " + message);
}
} catch (UniformInterfaceException | ClientHandlerException
| IOException e) {
LOG.error("Error in restarting application: ", e);
}
}
public void stopApp(Service app) throws JsonProcessingException {
ObjectMapper mapper = new ObjectMapper();
mapper.configure(DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES, false);
String appInstanceId = app.getName();
String yarnFile = mapper.writeValueAsString(app);
ClientResponse response;
try {
response = asc.getApiClient(asc.getServicePath(appInstanceId))
.put(ClientResponse.class, yarnFile);
if (response.getStatus() >= 299) {
String message = response.getEntity(String.class);
throw new RuntimeException("Failed : HTTP error code : "
+ response.getStatus() + " error: " + message);
}
} catch (UniformInterfaceException | ClientHandlerException
| IOException e) {
LOG.error("Error in stopping application: ", e);
}
}
public void getStatus(AppEntry entry) {
ObjectMapper mapper = new ObjectMapper();
mapper.configure(DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES, false);
String appInstanceId = entry.getName();
Service app = null;
try {
String yarnFile = asc.getApiClient(asc.getServicePath(appInstanceId))
.get(String.class);
app = mapper.readValue(yarnFile, Service.class);
entry.setYarnfile(app);
} catch (UniformInterfaceException | IOException e) {
LOG.error("Error in fetching application status: ", e);
}
}
}

View File

@ -0,0 +1,22 @@
/*
* Licensed to the Apache Software Foundation (ASF) under one
* or more contributor license agreements. See the NOTICE file
* distributed with this work for additional information
* regarding copyright ownership. The ASF licenses this file
* to you 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
* <p>
* http://www.apache.org/licenses/LICENSE-2.0
* <p>
* 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.
*
*/
/**
* Application Catalog.
*/
package org.apache.hadoop.yarn.appcatalog.application;

View File

@ -0,0 +1,265 @@
/*
* Licensed to the Apache Software Foundation (ASF) under one
* or more contributor license agreements. See the NOTICE file
* distributed with this work for additional information
* regarding copyright ownership. The ASF licenses this file
* to you 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 org.apache.hadoop.yarn.appcatalog.controller;
import javax.ws.rs.GET;
import javax.ws.rs.POST;
import javax.ws.rs.Path;
import javax.ws.rs.PathParam;
import javax.ws.rs.Produces;
import javax.ws.rs.core.MediaType;
import javax.ws.rs.core.Response;
import javax.ws.rs.core.Response.Status;
import org.apache.hadoop.yarn.appcatalog.application.AppCatalogSolrClient;
import org.apache.hadoop.yarn.appcatalog.application.YarnClient;
import org.apache.hadoop.yarn.appcatalog.model.AppEntry;
import org.apache.hadoop.yarn.service.api.records.Service;
import org.apache.hadoop.yarn.service.api.records.ServiceState;
import com.fasterxml.jackson.core.JsonProcessingException;
/**
* Application catalog REST API for displaying application details.
*/
@Path("/app_details")
public class AppDetailsController {
public AppDetailsController() {
}
/**
* List detail information about the deployed application.
*
* @apiGroup AppDetailController
* @apiName getDetails
* @api {get} /app_details/config/{id} Check config of application instance.
* @apiParam {String} id Application ID to fetch configuration.
* @apiSuccess {Object} AppEntry Application configuration.
* @apiSuccessExample {json} Success-Response:
* HTTP/1.1 200 OK
* {
* "id":"howita_man",
* "name":"howita_man",
* "app":"Jenkins-ci.org/Jenkins",
* "yarnfile":{
* "name":"howita_man",
* "lifetime":3600,
* "containers":[
* ],
* "components":[
* {
* "name":"jenkins",
* "dependencies":[
* ],
* "artifact":{
* "id":"eyang-1.openstacklocal:5000/jenkins:latest",
* "type":"DOCKER"
* },
* "launch_command":"",
* "resource":{
* "uri":null,
* "profile":null,
* "cpus":1,
* "memory":"2048"
* },
* "number_of_containers":1,
* "run_privileged_container":false,
* "configuration":{
* "properties":{
* },
* "env":{
* },
* "files":[
* ]
* },
* "quicklinks":[
* ],
* "containers":[
* ]
* }
* ],
* "configuration":{
* "properties":{
* },
* "env":{
* },
* "files":[
* ]
* },
* "quicklinks":{
* "Jenkins UI":"http://jenkins.howita_man.yarn.${DOMAIN}:8080/"
* }
* }
* }
* @param id - Application ID
* @return application entry-
*/
@Path("config/{id}")
@GET
@Produces(MediaType.APPLICATION_JSON)
public AppEntry getDetails(@PathParam("id") String id) {
AppCatalogSolrClient sc = new AppCatalogSolrClient();
return sc.findAppEntry(id);
}
/**
* Check application status.
*
* @apiGroup AppDetailController
* @apiName getStatus
* @api {get} /app_details/status/{id} Check status of application instance.
* @apiParam {String} id Application ID to check.
* @apiSuccessExample {json} Success-Response:
* HTTP/1.1 200 OK
* {
* "id":"howita_man",
* "name":"howita_man",
* "app":"Jenkins-ci.org/Jenkins",
* "yarnfile":{
* "name":"howita_man",
* "lifetime":3099,
* "containers":[
* ],
* "id":"application_1503694240849_0043",
* "components":[
* {
* "name":"jenkins",
* "dependencies":[
* ],
* "artifact":{
* "id":"eyang-1.openstacklocal:5000/jenkins:latest",
* "type":"DOCKER"
* },
* "launch_command":"",
* "resource":{
* "uri":null,
* "profile":null,
* "cpus":1,
* "memory":"2048"
* },
* "number_of_containers":1,
* "run_privileged_container":false,
* "configuration":{
* "properties":{
* },
* "env":{
* },
* "files":[
* ]
* },
* "quicklinks":[
* ],
* "containers":[
* {
* "id":"container_1503694240849_0043_01_000002",
* "launch_time":1504630535403,
* "bare_host":"eyang-4.openstacklocal",
* "state":"READY",
* "component_name":"jenkins-0"
* }
* ]
* }
* ],
* "configuration":{
* "properties":{
* },
* "env":{
* },
* "files":[
* ]
* },
* "quicklinks":{
* "Jenkins UI":"http://jenkins.howita_man.yarn.${DOMAIN}:8080/"
* }
* }
* }
* @apiSuccess {Object} text Give status
* @param id - Application ID
* @return application entry
*/
@Path("status/{id}")
@GET
@Produces(MediaType.APPLICATION_JSON)
public AppEntry getStatus(@PathParam("id") String id) {
AppCatalogSolrClient sc = new AppCatalogSolrClient();
AppEntry appEntry = sc.findAppEntry(id);
YarnClient yc = new YarnClient();
yc.getStatus(appEntry);
return appEntry;
}
/**
* Stop an application.
*
* @apiGroup AppDetailController
* @apiName stopApp
* @api {post} /app_details/stop/{id} Stop one instance of application.
* @apiParam {String} id Application ID to stop.
* @apiSuccess {String} text Give deployment status
* @apiError BadRequest Requested application does not stop.
* @param id - Application ID
* @return Web response code
*/
@Path("stop/{id}")
@POST
@Produces(MediaType.APPLICATION_JSON)
public Response stopApp(@PathParam("id") String id) {
AppCatalogSolrClient sc = new AppCatalogSolrClient();
AppEntry app = sc.findAppEntry(id);
Service yarnApp = app.getYarnfile();
yarnApp.setState(ServiceState.STOPPED);
try {
YarnClient yc = new YarnClient();
yc.stopApp(yarnApp);
} catch (JsonProcessingException e) {
return Response.status(Status.BAD_REQUEST).build();
}
return Response.ok().build();
}
/**
* Restart an application.
*
* @apiGroup AppDetailController
* @apiName restartApp
* @api {post} /app_details/restart/{id} Restart one instance of application.
* @apiParam {String} id Application ID to restart.
* @apiSuccess {String} text Give deployment status
* @apiError BadRequest Requested application does not restart.
* @param id - Application ID
* @return Web response code
*/
@Path("restart/{id}")
@POST
@Produces(MediaType.APPLICATION_JSON)
public Response restartApp(@PathParam("id") String id) {
AppCatalogSolrClient sc = new AppCatalogSolrClient();
AppEntry app = sc.findAppEntry(id);
Service yarnApp = app.getYarnfile();
yarnApp.setState(ServiceState.STARTED);
try {
YarnClient yc = new YarnClient();
yc.restartApp(yarnApp);
} catch (JsonProcessingException e) {
return Response.status(Status.BAD_REQUEST).build();
}
return Response.ok().build();
}
}

View File

@ -0,0 +1,182 @@
/*
* Licensed to the Apache Software Foundation (ASF) under one
* or more contributor license agreements. See the NOTICE file
* distributed with this work for additional information
* regarding copyright ownership. The ASF licenses this file
* to you 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 org.apache.hadoop.yarn.appcatalog.controller;
import java.io.IOException;
import java.util.List;
import javax.ws.rs.Consumes;
import javax.ws.rs.DELETE;
import javax.ws.rs.GET;
import javax.ws.rs.POST;
import javax.ws.rs.Path;
import javax.ws.rs.PathParam;
import javax.ws.rs.Produces;
import javax.ws.rs.core.MediaType;
import javax.ws.rs.core.Response;
import javax.ws.rs.core.Response.Status;
import org.apache.hadoop.yarn.appcatalog.application.AppCatalogSolrClient;
import org.apache.hadoop.yarn.appcatalog.application.YarnClient;
import org.apache.hadoop.yarn.appcatalog.model.AppEntry;
import org.apache.hadoop.yarn.service.api.records.Service;
import org.apache.solr.client.solrj.SolrServerException;
/**
* Application deployment module.
*/
@Path("/app_list")
@Produces({ MediaType.APPLICATION_JSON })
public class AppListController {
public AppListController() {
}
/**
* Get Application List.
*
* @apiGroup AppListController
* @apiName get
* @api {get} /app_list Get list of deployed applications.
* @apiSuccess {Object[]} List<AppEntry> List of deployed Applications.
* @apiSuccessExample {json} Success-Response:
* HTTP/1.1 200 OK
* [
* {
* "id":"howita-man",
* "name":"howita-man",
* "app":"Jenkins-ci.org/Jenkins",
* "yarnfile":{
* "name":"howita_man",
* "lifetime":3600,
* "containers":[
* ],
* "components":[
* {
* "name":"jenkins",
* "dependencies":[
* ],
* "artifact":{
* "id":"eyang-1.openstacklocal:5000/jenkins:latest",
* "type":"DOCKER"
* },
* "launch_command":"",
* "resource":{
* "uri":null,
* "profile":null,
* "cpus":1,
* "memory":"2048"
* },
* "number_of_containers":1,
* "run_privileged_container":false,
* "configuration":{
* "properties":{
* },
* "env":{
* },
* "files":[
* ]
* },
* "quicklinks":[
* ],
* "containers":[
* ]
* }
* ],
* "configuration":{
* "properties":{
* },
* "env":{
* },
* "files":[
* ]
* },
* "quicklinks":{
* "Jenkins UI":"http://jenkins.${SERVICE_NAME}.${USER}.${DOMAIN}:8080/"
* }
* }
* },
* {
* ...
* }
* ]
* @return - Active application deployed by current user.
*/
@GET
@Produces(MediaType.APPLICATION_JSON)
public List<AppEntry> getList() {
AppCatalogSolrClient sc = new AppCatalogSolrClient();
return sc.listAppEntries();
}
/**
* Delete an application.
*
* @apiGroup AppListController
* @apiName delete
* @api {delete} /app_list Delete one instance of application.
* @apiParam {String} id Application name to delete.
* @apiSuccess {String} text Delete request accepted
* @param id - application ID
* @param name - application name
* @return Web response
*/
@DELETE
@Path("{id}/{name}")
@Produces(MediaType.APPLICATION_JSON)
public Response delete(@PathParam("id") String id,
@PathParam("name") String name) {
AppCatalogSolrClient sc = new AppCatalogSolrClient();
sc.deleteApp(id);
YarnClient yc = new YarnClient();
yc.deleteApp(name);
return Response.status(Status.ACCEPTED).build();
}
/**
* Deploy an application.
*
* @apiGroup AppListController
* @apiName deploy
* @api {post} /app_list/{id} Deploy one instance of application.
* @apiParam {String} id Application ID to deploy.
* @apiSuccess {String} text Give deployment status
* @apiError BadRequest Unable to deploy requested application.
* @param id - application ID
* @return Web response
*/
@POST
@Path("{id}")
@Consumes(MediaType.APPLICATION_JSON)
@Produces(MediaType.APPLICATION_JSON)
public Response deploy(@PathParam("id") String id, Service service) {
AppCatalogSolrClient sc = new AppCatalogSolrClient();
try {
sc.deployApp(id, service);
} catch (SolrServerException | IOException e) {
return Response.status(Status.BAD_REQUEST).entity(e.toString()).build();
}
YarnClient yc = new YarnClient();
yc.createApp(service);
String output = "{\"status\":\"Application deployed.\",\"id\":\"" +
service.getName() + "\"}";
return Response.status(Status.ACCEPTED).entity(output).build();
}
}

View File

@ -0,0 +1,198 @@
/*
* Licensed to the Apache Software Foundation (ASF) under one
* or more contributor license agreements. See the NOTICE file
* distributed with this work for additional information
* regarding copyright ownership. The ASF licenses this file
* to you 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 org.apache.hadoop.yarn.appcatalog.controller;
import java.io.IOException;
import java.util.List;
import javax.ws.rs.GET;
import javax.ws.rs.POST;
import javax.ws.rs.Path;
import javax.ws.rs.PathParam;
import javax.ws.rs.Produces;
import javax.ws.rs.QueryParam;
import javax.ws.rs.core.MediaType;
import javax.ws.rs.core.Response;
import javax.ws.rs.core.Response.Status;
import org.apache.hadoop.yarn.appcatalog.application.AppCatalogSolrClient;
import org.apache.hadoop.yarn.appcatalog.model.AppStoreEntry;
import org.apache.hadoop.yarn.appcatalog.model.Application;
/**
* Application catalog REST API for searching and recommending
* applications.
*
*/
@Path("/app_store")
public class AppStoreController {
public AppStoreController() {
}
/**
* Display the most frequently used applications on YARN AppCatalog home page.
*
* @apiGroup AppStoreController
* @apiName get
* @api {get} /app_store/recommended Display recommended applications.
* @apiSuccess {Object} AppEntry Application configuration.
* @apiSuccessExample {json} Success-Response:
* HTTP/1.1 200 OK
* [
* {
* "id":"96b7833a-e3",
* "org":"Hortonworks",
* "name":"LAMP",
* "desc":"Linux Apache MySQL PHP web application",
* "icon":"/css/img/feather.png",
* "like":0,
* "download":0,
* "app":null
* },
* {
* ...
* }
* ]
* @return - List of YARN applications
*/
@GET
@Path("recommended")
@Produces(MediaType.APPLICATION_JSON)
public List<AppStoreEntry> get() {
AppCatalogSolrClient sc = new AppCatalogSolrClient();
return sc.getRecommendedApps();
}
/**
* Search for yarn applications from solr.
*
* @apiGroup AppStoreController
* @apiName search
* @api {get} /app_store/search Find application from appstore.
* @apiParam {String} q Keyword to search.
* @apiSuccess {Object} AppStoreEntry List of matched applications.
* @apiSuccessExample {json} Success-Response:
* HTTP/1.1 200 OK
* [
* {
* "id":"96b7833a-e3",
* "org":"Hortonworks",
* "name":"LAMP",
* "desc":"Linux Apache MySQL PHP web application",
* "icon":"/css/img/feather.png",
* "like":0,
* "download":0,
* "app":null
* },
* {
* ...
* }
* ]
* @param keyword - search for keyword
* @return - List of YARN applications matching keyword search.
*/
@GET
@Path("search")
@Produces(MediaType.APPLICATION_JSON)
public List<AppStoreEntry> search(@QueryParam("q") String keyword) {
AppCatalogSolrClient sc = new AppCatalogSolrClient();
return sc.search(keyword);
}
/**
* Find yarn application from solr.
*
* @param id Application ID
* @return AppEntry
*/
@GET
@Path("get/{id}")
@Produces(MediaType.APPLICATION_JSON)
public AppStoreEntry get(@PathParam("id") String id) {
AppCatalogSolrClient sc = new AppCatalogSolrClient();
return sc.findAppStoreEntry(id);
}
/**
* Register an application.
*
* @apiGroup AppStoreController
* @apiName register
* @api {post} /app_store/register Register an application in appstore.
* @apiParam {Object} app Application definition.
* @apiParamExample {json} Request-Example:
* {
* "name": "Jenkins",
* "organization": "Jenkins-ci.org",
* "description": "The leading open source automation server",
* "icon": "/css/img/jenkins.png",
* "lifetime": "3600",
* "components": [
* {
* "name": "jenkins",
* "number_of_containers": 1,
* "artifact": {
* "id": "eyang-1.openstacklocal:5000/jenkins:latest",
* "type": "DOCKER"
* },
* "launch_command": "",
* "resource": {
* "cpus": 1,
* "memory": "2048"
* },
* "configuration": {
* "env": {
* },
* "files": [
* ]
* }
* }
* ],
* "quicklinks": {
* "Jenkins UI": "http://jenkins.${SERVICE_NAME}.${USER}.${DOMAIN}:8080/"
* }
* }
* @apiSuccess {String} Response Application register result.
* @apiError BadRequest Error in process application registration.
* @param app - Yarnfile in JSON form
* @return Web response
*/
@POST
@Path("register")
@Produces(MediaType.APPLICATION_JSON)
public Response register(Application app) {
try {
if (app.getName()==null) {
throw new IOException("Application name can not be empty.");
}
if (app.getOrganization()==null) {
throw new IOException("Application organization can not be empty.");
}
if (app.getDescription()==null) {
throw new IOException("Application description can not be empty.");
}
AppCatalogSolrClient sc = new AppCatalogSolrClient();
sc.register(app);
} catch (IOException e) {
return Response.status(Status.BAD_REQUEST).entity(e.getMessage()).build();
}
return Response.status(Status.ACCEPTED).build();
}
}

View File

@ -0,0 +1,22 @@
/*
* Licensed to the Apache Software Foundation (ASF) under one
* or more contributor license agreements. See the NOTICE file
* distributed with this work for additional information
* regarding copyright ownership. The ASF licenses this file
* to you 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
* <p>
* http://www.apache.org/licenses/LICENSE-2.0
* <p>
* 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.
*
*/
/**
* Application Catalog controllers.
*/
package org.apache.hadoop.yarn.appcatalog.controller;

View File

@ -0,0 +1,76 @@
/*
* Licensed to the Apache Software Foundation (ASF) under one
* or more contributor license agreements. See the NOTICE file
* distributed with this work for additional information
* regarding copyright ownership. The ASF licenses this file
* to you 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 org.apache.hadoop.yarn.appcatalog.model;
import javax.xml.bind.annotation.XmlAccessType;
import javax.xml.bind.annotation.XmlAccessorType;
import javax.xml.bind.annotation.XmlRootElement;
/**
* Data model for user defined application configuration.
*/
@XmlRootElement
@XmlAccessorType(XmlAccessType.PUBLIC_MEMBER)
public class AppDetails {
private String image;
private String version;
private String[] ports;
private String[] volumes;
private String[] env;
public String getImage() {
return image;
}
public void setImage(String image) {
this.image = image;
}
public String getVersion() {
return version;
}
public void setVersion(String version) {
this.version = version;
}
public String[] getPorts() {
return ports;
}
public void setPorts(String[] ports2) {
this.ports = ports2;
}
public String[] getVolumes() {
return volumes;
}
public void setVolumes(String[] volumes) {
this.volumes = volumes;
}
public String[] getEnv() {
return env;
}
public void setEnv(String[] env) {
this.env = env;
}
}

View File

@ -0,0 +1,72 @@
/*
* Licensed to the Apache Software Foundation (ASF) under one
* or more contributor license agreements. See the NOTICE file
* distributed with this work for additional information
* regarding copyright ownership. The ASF licenses this file
* to you 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 org.apache.hadoop.yarn.appcatalog.model;
import javax.xml.bind.annotation.XmlAccessType;
import javax.xml.bind.annotation.XmlAccessorType;
import javax.xml.bind.annotation.XmlRootElement;
import javax.xml.bind.annotation.XmlType;
import org.apache.hadoop.yarn.service.api.records.Service;
/**
* Data model for deployed application.
*
*/
@XmlType(namespace="http://hadoop.apache.org", name="AppEntry")
@XmlRootElement
@XmlAccessorType(XmlAccessType.PUBLIC_MEMBER)
public class AppEntry {
private String id;
private String name;
private String app;
private Service yarnfile;
public String getId() {
return id;
}
public void setId(String id) {
this.id = id;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public String getApp() {
return app;
}
public void setApp(String app) {
this.app = app;
}
public Service getYarnfile() {
return yarnfile;
}
public void setYarnfile(Service yarnfile) {
this.yarnfile = yarnfile;
}
}

View File

@ -0,0 +1,106 @@
/*
* Licensed to the Apache Software Foundation (ASF) under one
* or more contributor license agreements. See the NOTICE file
* distributed with this work for additional information
* regarding copyright ownership. The ASF licenses this file
* to you 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 org.apache.hadoop.yarn.appcatalog.model;
import javax.xml.bind.annotation.XmlAccessType;
import javax.xml.bind.annotation.XmlAccessorType;
import javax.xml.bind.annotation.XmlRootElement;
import org.apache.hadoop.yarn.service.api.records.Service;
/**
* Data model of application template stored in application catalog.
*/
@XmlRootElement
@XmlAccessorType(XmlAccessType.PUBLIC_MEMBER)
public class AppStoreEntry {
private String id;
private String org;
private String name;
private String desc;
private String icon = "/css/img/feather.png";
private long like;
private long download;
private Service app;
public String getOrg() {
return org;
}
public void setOrg(String org) {
this.org = org;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public String getDesc() {
return desc;
}
public void setDesc(String desc) {
this.desc = desc;
}
public long getLike() {
return like;
}
public void setLike(long like) {
this.like = like;
}
public long getDownload() {
return download;
}
public void setDownload(long download) {
this.download = download;
}
public String getId() {
return id;
}
public void setId(String id) {
this.id = id;
}
public Service getApp() {
return app;
}
public void setApp(Service app) {
this.app = app;
}
public String getIcon() {
return icon;
}
public void setIcon(String icon) {
this.icon = icon;
}
}

View File

@ -0,0 +1,67 @@
/*
* Licensed to the Apache Software Foundation (ASF) under one
* or more contributor license agreements. See the NOTICE file
* distributed with this work for additional information
* regarding copyright ownership. The ASF licenses this file
* to you 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 org.apache.hadoop.yarn.appcatalog.model;
import javax.xml.bind.annotation.XmlRootElement;
import com.fasterxml.jackson.annotation.JsonIgnoreProperties;
import com.fasterxml.jackson.annotation.JsonInclude;
import com.fasterxml.jackson.annotation.JsonProperty;
import com.fasterxml.jackson.annotation.JsonPropertyOrder;
import org.apache.hadoop.yarn.service.api.records.Service;
/**
* Data model of display recommended applications and descriptions.
*/
@XmlRootElement
@JsonInclude(JsonInclude.Include.NON_NULL)
@JsonIgnoreProperties(ignoreUnknown = true)
@JsonPropertyOrder({ "organization", "name", "description", "icon" })
public class Application extends Service {
private static final long serialVersionUID = -1776203219305414248L;
private String organization;
private String description;
private String icon;
@JsonProperty("organization")
public String getOrganization() {
return organization;
}
public void setOrganization(String organization) {
this.organization = organization;
}
@JsonProperty("description")
public String getDescription() {
return description;
}
public void setDescription(String description) {
this.description = description;
}
@JsonProperty("icon")
public String getIcon() {
return icon;
}
public void setIcon(String icon) {
this.icon = icon;
}
}

View File

@ -0,0 +1,22 @@
/*
* Licensed to the Apache Software Foundation (ASF) under one
* or more contributor license agreements. See the NOTICE file
* distributed with this work for additional information
* regarding copyright ownership. The ASF licenses this file
* to you 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
* <p>
* http://www.apache.org/licenses/LICENSE-2.0
* <p>
* 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.
*
*/
/**
* Application Catalog Data Models.
*/
package org.apache.hadoop.yarn.appcatalog.model;

View File

@ -0,0 +1,422 @@
/*
* Licensed to the Apache Software Foundation (ASF) under one
* or more contributor license agreements. See the NOTICE file
* distributed with this work for additional information
* regarding copyright ownership. The ASF licenses this file
* to you 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 org.apache.hadoop.yarn.appcatalog.utils;
import java.util.Arrays;
import java.util.Random;
/**
* Random word generator utility.
*/
public final class RandomWord {
private RandomWord() {
}
public static String getNewWord(int wordLength) throws WordLengthException {
if (wordLength < 3 || wordLength > 15) {
throw new WordLengthException(
"Word length error, words must be between 3 and 15 characters long.");
}
String theNewWord = "e";
while (theNewWord.equals("e")) {
try {
theNewWord = generateRandomWord(wordLength);
} catch (Exception e) {
}
}
return theNewWord;
}
private static String generateRandomWord(int wordLength) {
String randomWord;
// -----------------------------------------------------------------------
// Bigram source and general concept based on
// https://github.com/scrollback/scrollback & described in
// https://www.hackerearth.com/notes/random-pronouncable-text-generator/
String[] startBiGram = {"TH", "OF", "AN", "IN", "TO", "CO", "BE", "HE",
"RE", "HA", "WA", "FO", "WH", "MA", "WI", "ON", "HI", "PR", "ST", "NO",
"IS", "IT", "SE", "WE", "AS", "CA", "DE", "SO", "MO", "SH", "DI", "AL",
"AR", "LI", "WO", "FR", "PA", "ME", "AT", "SU", "BU", "SA", "FI", "NE",
"CH", "PO", "HO", "DO", "OR", "UN", "LO", "EX", "BY", "FA", "LA", "LE",
"PE", "MI", "SI", "YO", "TR", "BA", "GO", "BO", "GR", "TE", "EN", "OU",
"RA", "AC", "FE", "PL", "CL", "SP", "BR", "EV", "TA", "DA", "AB", "TI",
"RO", "MU", "EA", "NA", "SC", "AD", "GE", "YE", "AF", "AG", "UP", "AP",
"DR", "US", "PU", "CE", "IF", "RI", "VI", "IM", "AM", "KN", "OP", "CR",
"OT", "JU", "QU", "TW", "GA", "VA", "VE", "PI", "GI", "BI", "FL", "BL",
"EL", "JO", "FU", "HU", "CU", "RU", "OV", "MY", "OB", "KE", "EF", "PH",
"CI", "KI", "NI", "SL", "EM", "SM", "VO", "MR", "WR", "ES", "DU", "TU",
"AU", "NU", "GU", "OW", "SY", "JA", "OC", "EC", "ED", "ID", "JE", "AI",
"EI", "SK", "OL", "GL", "EQ", "LU", "AV", "SW", "AW", "EY", "TY"};
String[] lookupBiGram = {"TH", "AN", "IN", "IO", "EN", "TI", "FO", "HE",
"HA", "HI", "TE", "AT", "ER", "AL", "WA", "VE", "CO", "RE", "IT", "WI",
"ME", "NC", "ON", "PR", "AR", "ES", "EV", "ST", "EA", "IV", "EC", "NO",
"OU", "PE", "IL", "IS", "MA", "AV", "OM", "IC", "GH", "DE", "AI", "CT",
"IG", "ID", " OR", "OV", "UL", "YO", "BU", "RA", "FR", "RO", "WH", "OT",
"BL", "NT", "UN", "TR", "HO", "AC", "TU", "WE", "CA", "SH", "UR", "IE",
"PA", "TO", "EE", "LI", "RI", "UG", "AM", "ND", "US", "LL", "AS", "TA",
"LE", "MO", "WO", "MI", "AB", "EL", "IA", "NA", "SS", "AG", "TT", "NE",
"PL", " LA", "OS", "CE", "DI", "BE", "AP", "SI", "NI", "OW", "SO", "AK",
"CH", "EM", "IM", "SE", "NS", "PO", "EI", "EX", "KI", "UC", "AD", "GR",
"IR", "NG", "OP", "SP", "OL", "DA", "NL", "TL", "LO", "BO", "RS", "FE",
"FI", "SU", "GE", "MP", "UA", "OO", "RT", "SA", "CR", "FF", "IK", "MB",
" KE", "FA", "CI", "EQ", "AF", "ET", "AY", "MU", "UE", "HR", "TW", "GI",
"OI", "VI", "CU", "FU", "ED", "QU", "UT", "RC", "OF", "CL", "FT", "IZ",
"PP", "RG", "DU", "RM", "YE", "RL", "DO", "AU", "EP", "BA", "JU", "RD",
"RU", "OG", "BR", "EF", "KN", "LS", "GA", "PI", "YI", "BI", "IB", "UB",
"VA", "OC", "IF", "RN", "RR", "SC", "TC", "CK", "DG", "DR", "MM", "NN",
"OD", "RV", "UD", "XP", "JE", "UM", "EG", "DL", "PH", "SL", "GO", "CC",
"LU", "OA", "PU", "UI", "YS", "ZA", "HU", "MR", "OE", "SY", "EO", "TY",
"UP", "FL", "LM", "NF", "RP", "OH", "NU", "XA", "OB", "VO", "DM", "GN",
" LD", "PT", "SK", "WR", "JO", "LT", "YT", "UF", "BJ", "DD", "EY", "GG",
"GL", "GU", "HT", "LV", "MS", "NM", "NV", "OK", "PM", "RK", "SW", "TM",
"XC", "ZE", "AW", "SM"};
String[][][] nextCharLookup = {
{{"E", "A", "I", "O", "R"}, {"E", "O"}},
{{"D", "T", "Y", "C", "S", "G", "N", "I", "O", "E", "A", "K"},
{"D", "T", "Y", "S", "G", "O", "E", "A", "K"}},
{{"G", "T", "E", "D", "S", "C", "A", "I", "K", "V", "U", "N", "F"},
{"G", "T", "E", "D", "S", "A", "K"}},
{{"N", "U", "R"}, {"N", "U", "R"}},
{{"T", "C", "D", "S", "E", "I", "G", "O", "N", "A"},
{"T", "D", "S", "E", "G", "O", "A"}},
{{"O", "N", "C", "V", "M", "L", "E", "T", "S", "A", "R", "F"},
{"N", "C", "M", "L", "E", "T", "S", "A", "R", "F"}},
{{"R", "U", "O", "L"}, {"R", "U", "O", "L"}},
{{"R", "N", "Y", "S", "M", "I", "A", "L", "D", "T"},
{"R", "N", "Y", "S", "M", "A", "L", "D", "T"}},
{{"T", "D", "V", "N", "S", "R", "P", "L"},
{"T", "D", "N", "S", "R", "L"}},
{{"S", "N", "C", "M", "L", "P", "G", "T", "R", "E"},
{"S", "N", "C", "M", "L", "P", "G", "T", "R", "E"}},
{{"R", "D", "N", "S", "M", "L", "E", "C", "A"},
{"R", "D", "N", "S", "M", "L", "E", "A"}},
{{"I", "E", "T", "H", "U", "O", "C"}, {"E", "H", "O"}},
{{"E", "S", "I", "A", "N", "Y", "T", "V", "M", "R", "O", "L", "G",
"F", "C"}, {"E", "S", "A", "N", "Y", "T", "M"}},
{{"L", "S", "I", "T", "E", "U", "O", "M", "K", "F", "A"},
{"L", "S", "T", "E", "F"}},
{{"S", "Y", "R", "T", "N", "L"}, {"S", "Y", "R", "T", "N", "L"}},
{{"R", "N", "L", "S", "D"}, {"R", "N", "L", "S", "D"}},
{{"N", "M", "U", "R", "L", "V", "S", "O"},
{"N", "M", "U", "R", "L", "O"}},
{{"S", "A", "D", "N", "E", "C", "L", "T", "P", "M", "V", "G", "F",
"Q"}, {"S", "A", "D", "N", "E", "L", "T", "P", "M"}},
{{"H", "I", "Y", "E", "S", "T", "A", "U"},
{"H", "Y", "E", "S", "A"}},
{{"T", "L", "N", "S"}, {"T", "L", "N", "S"}},
{{"N", "R", "D", "T", "S", "M", "A"},
{"N", "R", "D", "T", "S", "M", "A"}},
{{"E", "I", "H", "T", "R", "O", "L"}, {"E", "H", "T"}},
{{"S", "E", "T", "G", "A", "D", "L", "C", "V", "O", "I", "F"},
{"S", "E", "T", "G", "A", "D", "O"}},
{{"O", "E", "I", "A"}, {"E", "A"}},
{{"E", "T", "D", "Y", "S", "I", "R", "L", "M", "K", "G", "A", "O",
"N", "C"}, {"E", "T", "D", "Y", "S", "M", "K", "A", "N"}},
{{"S", "T", "E", "I", "P", "U", "C"}, {"S", "T", "E"}},
{{"E", "I"}, {"E"}},
{{"A", "R", "I", "E", "O", "U", "S"}, {"A", "E", "O", "S"}},
{{"R", "S", "T", "D", "L", "C", "N", "V", "M", "K"},
{"R", "S", "T", "D", "L", "N", "M"}},
{{"E", "I", "A"}, {"E"}},
{{"T", "O", "I", "E", "A", "U", "R", "H"}, {"T", "E", "H"}},
{{"T", "W", "R", "U", "N", "M"}, {"T", "W", "R", "U", "N", "M"}},
{{"T", "L", "R", "N", "S", "G", "P", "B"},
{"T", "L", "R", "N", "S", "P"}},
{{"R", "N", "C", "A", "D", "T", "O"}, {"R", "N", "A", "D", "T"}},
{{"L", "E", "I", "Y", "D", "A"}, {"L", "E", "Y", "D"}},
{{"T", "H", "S", "I", "E", "C", "M"}, {"T", "H", "S", "E", "M"}},
{{"N", "T", "L", "K", "D", "S", "I", "G"},
{"N", "T", "L", "D", "S"}},
{{"E", "I", "A"}, {"E"}},
{{"E", "P", "M", "I", "A"}, {"E"}},
{{"A", "H", "E", "I", "T", "K", "U", "S"},
{"H", "E", "T", "K", "S"}},
{{"T"}, {"T"}},
{{"R", "N", "S", "D", "A", "V", "P", "T", "M", "L", "F"},
{"R", "N", "S", "D", "A", "P", "T", "M", "L"}},
{{"N", "D", "R", "L", "T"}, {"N", "D", "R", "L", "T"}},
{{"I", "E", "U", "S", "O"}, {"E", "S", "O"}},
{{"H", "N", "I"}, {"H", "N"}}, {{"E"}, {"E"}},
{{"E", "T", "M", "D", "S", "K", "I", "Y", "L", "G", "A", "R", "N",
"C"}, {"E", "T", "M", "D", "S", "K", "Y", "A", "N"}},
{{"E", "I"}, {"E"}},
{{"D", "T", "A", "L"}, {"D", "T", "L"}}, {{"U"}, {"U"}},
{{"T", "S", "R", "I"}, {"T", "S", "R"}},
{{"T", "N", "L", "C", "I", "M", "D", "S", "R", "P", "G", "B"},
{"T", "N", "L", "M", "D", "S", "R"}},
{{"O", "E", "A"}, {"E", "A"}},
{{"M", "U", "V", "P", "N", "W", "S", "O", "L", "D", "C", "B", "A",
"T", "G"}, {"M", "U", "P", "N", "W", "O", "L", "D", "T"}},
{{"I", "E", "O", "A"}, {"E", "O"}},
{{"H", "E", "T", "I"}, {"H", "E"}},
{{"E", "I", "Y", "O", "A"}, {"E", "Y"}},
{{"E", "I", "S", "R", "O", "A", "L", "Y", "U", "H"},
{"E", "S", "O", "A", "Y", "H"}},
{{"D", "T", "I", "C", "G"}, {"D", "T", "G"}},
{{"A", "I", "O", "E", "U", "Y"}, {"A", "E", "Y"}},
{{"U", "W", "S", "R", "L", "O", "M", "T", "P", "N", "D"},
{"U", "W", "R", "L", "O", "M", "T", "P", "N", "D"}},
{{"T", "E", "K", "H", "C", "R", "I"}, {"T", "E", "K", "H"}},
{{"R", "D", "A", "T"}, {"R", "T"}},
{{"R", "L", "E", "V", "S", "N", "A"},
{"R", "L", "E", "S", "N", "A"}},
{{"L", "N", "T", "R", "U", "S", "M", "P"},
{"L", "N", "T", "R", "S", "M"}},
{{"E", "O", "I", "A"}, {"E", "O"}},
{{"E", "N", "T", "S", "I", "A", "Y", "R", "P", "C"},
{"E", "N", "T", "S", "A", "Y"}},
{{"S", "N", "D", "T", "W", "V", "R", "L", "F"},
{"S", "N", "D", "T", "W", "R", "L"}},
{{"R", "T", "S", "N", "L", "I", "C"}, {"R", "T", "S", "N", "L"}},
{{"R", "O", "N", "W", "P", "M", "L"},
{"R", "O", "N", "W", "P", "M", "L"}},
{{"N", "D", "T", "M", "S", "R", "P", "L", "K"},
{"N", "D", "T", "M", "S", "R", "P", "L", "K"}},
{{"N", "T", "S", "C", "K", "G", "E", "F", "Z", "V", "O", "M", "A"},
{"N", "T", "S", "C", "G", "E", "F", "M", "A"}},
{{"N", "E", "C", "T", "S", "G", "A", "V", "O", "P", "M", "L", "D",
"B"}, {"N", "E", "C", "T", "S", "G", "A", "P", "M", "L", "D"}},
{{"H", "G"}, {"H"}}, {{"E", "P", "I", "O", "A"}, {"E"}},
{{"E", "I", "S", "A", "U", "O"}, {"E", "S", "O"}},
{{"E", "T", "I", "S", "L", "H"}, {"E", "T", "S", "H"}},
{{"Y", "E", "O", "I", "S", "A"}, {"Y", "E", "S"}},
{{"T", "S", "E", "I", "U", "O", "K", "H"},
{"T", "S", "E", "O", "H"}},
{{"T", "N", "L", "I", "R", "K", "B", "G", "C"},
{"T", "N", "L", "R"}},
{{"S", "D", "A", "T", "C", "R", "N", "M", "G", "V", "F"},
{"S", "D", "A", "T", "R", "N", "M"}},
{{"R", "S", "V", "T", "U", "D"}, {"R", "T", "U", "D"}},
{{"R", "U"}, {"R", "U"}},
{{"N", "L", "S", "T", "C", "G"}, {"N", "L", "S", "T", "C", "G"}},
{{"L", "O", "I"}, {}},
{{"L", "Y", "I", "E", "F", "O", "A", "T", "S", "P", "D"},
{"L", "Y", "E", "F", "T", "S", "D"}},
{{"L", "N", "T"}, {"L", "N", "T"}},
{{"L", "T", "R", "N", "M"}, {"L", "T", "R", "N", "M"}},
{{"I", "E", "U", "O", "A"}, {"E", "O"}},
{{"E", "A", "O"}, {"E", "O"}}, {{"E", "L", "I"}, {"E"}},
{{"D", "S", "W", "R", "E", "Y", "V", "T", "L", "C", "A"},
{"D", "S", "W", "R", "E", "Y", "T", "L", "A"}},
{{"A", "E", "I", "Y", "O"}, {"E", "Y"}},
{{"T", "N", "R", "S", "C", "Y", "W", "I", "B"},
{"T", "N", "R", "S", "Y", "W"}},
{{"T", "E", "S", "I"}, {"T", "E", "S"}},
{{"S", "N", "R", "D", "P", "L", "I"},
{"S", "N", "R", "D", "P", "L"}},
{{"S", "N", "T", "D", "F", "E", "C", "A", "V", "R"},
{"S", "N", "T", "D", "F", "E", "C", "A", "R"}},
{{"R", "E", "C", "T", "L", "F", "S", "I", "G", "D", "A"},
{"R", "E", "T", "L", "S", "D", "A"}},
{{"P", "E", "A"}, {"E"}},
{{"O", "N", "D", "T", "S", "G", "C", "B", "V", "M", "A"},
{"N", "D", "T", "S", "G", "C", "M", "A"}},
{{"N", "T", "S", "C", "Z", "O", "G", "F"},
{"N", "T", "S", "C", "G", "F"}},
{{"N", "E", "S", "I", "A"}, {"N", "E", "S"}},
{{"N", "M", "U", "L", "C", "R"}, {"N", "M", "U", "L", "R"}},
{{"E", "I"}, {"E"}},
{{"E", "A", "I", "O", "U", "R"}, {"E", "O"}},
{{"E", "S", "P", "O", "B", "A", "I"}, {"E", "S"}},
{{"E", "P", "I", "A", "S", "M"}, {"E", "S"}},
{{"D", "N", "L", "S", "R", "E", "C", "T", "V", "A"},
{"D", "N", "L", "S", "R", "E", "T", "A"}},
{{"T", "I", "E"}, {"T", "E"}},
{{"S", "R", "N", "L", "W", "T", "I"}, {"R", "N", "L", "W", "T"}},
{{"R", "N", "G", "T"}, {"R", "N", "G", "T"}},
{{"P", "T", "I", "C", "A"}, {"T"}}, {{"N"}, {"N"}},
{{"H", "T", "K", "E"}, {"H", "T", "K", "E"}},
{{"E", "I", "Y", "V", "M", "D"}, {"E", "Y"}},
{{"E", "A", "O"}, {"E", "A"}},
{{"E", "S", "T", "L", "I"}, {"E", "S", "T"}},
{{"E", "S", "L", "T", "R", "I"}, {"E", "S"}},
{{"E", "P", "L"}, {"E"}}, {{"E", "O", "I", "A"}, {"E"}},
{{"D", "L", "I", "O", "E", "U"}, {"D", "L", "E"}},
{{"Y", "T", "R", "N"}, {"Y", "T", "R", "N"}},
{{"Y"}, {"Y"}}, {{"Y", "E"}, {"Y", "E"}},
{{"W", "N", "O", "S", "C", "V", "U", "T", "R", "P", "G"},
{"W", "N", "O", "U", "T", "R", "P"}},
{{"U", "T", "R", "O", "D", "A"}, {"U", "T", "R", "O", "D"}},
{{"T", "E", "O", "I"}, {"T", "E", "O"}},
{{"R", "E", "W", "L", "C", "A"}, {"R", "E", "W", "L", "A"}},
{{"R", "N", "C", "E", "L", "G"}, {"R", "N", "C", "E", "L", "G"}},
{{"R", "C", "P", "B", "M", "L", "A"}, {"R", "P", "M", "L"}},
{{"N", "T", "S", "R", "D"}, {"N", "T", "S", "R", "D"}},
{{"L", "O", "A", "T", "R", "E"}, {"T", "E"}},
{{"L", "T", "R"}, {"L", "T", "R"}},
{{"K", "D", "L", "T", "R", "N", "M"},
{"K", "D", "L", "T", "R", "N", "M"}},
{{"I", "H", "A", "E", "Y", "U", "S"}, {"H", "A", "E", "Y", "S"}},
{{"I", "M", "Y", "N", "L"}, {"M", "Y", "N", "L"}},
{{"E", "I", "O", "A"}, {"E", "A"}}, {{"E", "I"}, {"E"}},
{{"E"}, {"E"}}, {{"E"}, {"E"}},
{{"D", "N", "T", "S", "R", "E"}, {"D", "N", "T", "S", "R", "E"}},
{{"C", "R", "M", "I"}, {"R", "M"}},
{{"A", "T", "E", "S", "P", "N"}, {"A", "T", "E", "S", "P", "N"}},
{{"U"}, {}}, {{"T", "F"}, {"T", "F"}},
{{"T", "I", "H", "E", "Y", "W", "S", "A"},
{"H", "E", "Y", "S", "A"}},
{{"S", "E"}, {"S"}},
{{"S", "N", "L", "C"}, {"S", "N", "L"}},
{{"S", "N"}, {"S", "N"}}, {{"O", "E", "I"}, {"E"}},
{{"O", "E"}, {"O", "E"}},
{{"N", "V", "O", "C"}, {"N", "C"}}, {{"N"}, {"N"}},
{{"N", "D", "S", "C", "T", "O", "L", "E"},
{"N", "D", "S", "C", "T", "L", "E"}},
{{"L", "R", "T", "S"}, {"L", "R", "T", "S"}},
{{"L", "R", "N"}, {"L", "R", "N"}},
{{"I", "U", "E"}, {"E"}}, {{"I", "E", "A"}, {"E"}},
{{"I", "H", "E"}, {"H", "E"}}, {{"H", "E"}, {"H", "E"}},
{{"F", "T"}, {"F", "T"}}, {{"E", "A", "U", "O"}, {"E"}},
{{"E"}, {"E"}}, {{"E", "A"}, {"E"}},
{{"E", "O", "R", "L"}, {"E"}}, {{"E", "A"}, {"E"}},
{{"C", "S", "R", "A"}, {"S", "R"}},
{{"A", "S", "I", "E"}, {"S", "E"}},
{{"A", "S", "D"}, {"A", "S", "D"}},
{{"Y", "D"}, {"Y", "D"}},
{{"W", "N", "M", "E"}, {"W", "N", "M"}},
{{"T", "S"}, {"T", "S"}},
{{"T", "O", "E", "A"}, {"T", "E"}},
{{"S", "C", "R", "N", "L"}, {"S", "R", "N", "L"}},
{{"S"}, {"S"}}, {{"S", "E", "I"}, {"S", "E"}},
{{"S", "N", "C"}, {"S", "N"}}, {{"R", "I"}, {}},
{{"O", "I", "E", "A"}, {"E", "A"}},
{{"O", "F", "U", "T", "E"}, {"F", "T", "E"}},
{{"O", "E"}, {"O", "E"}}, {{"O"}, {"O"}},
{{"N", "I", "T", "R"}, {"N", "T", "R"}},
{{"N", "T", "R", "E", "C"}, {"N", "T", "R", "E", "C"}},
{{"N"}, {"N"}}, {{"L", "T", "N"}, {"L", "T", "N"}},
{{"L", "I", "E"}, {"E"}}, {{"L"}, {}},
{{"L", "T", "R", "N"}, {"L", "T", "R", "N"}},
{{"K", "I", "E", "C", "A"}, {"K", "E"}},
{{"I", "F", "E", "T"}, {"F", "E", "T"}},
{{"I", "E", "M", "A"}, {"E", "A"}},
{{"I", "E", "Y", "O"}, {"E", "Y"}},
{{"H", "R", "O", "I", "A"}, {"H"}}, {{"H"}, {"H"}},
{{"E"}, {"E"}}, {{"E"}, {"E"}},
{{"E", "O", "I", "A"}, {"E", "A"}},
{{"E", "U", "I"}, {"E"}}, {{"E", "O", "I"}, {"E", "O"}},
{{"E", "Y", "U"}, {"E", "Y"}}, {{"E", "I"}, {"E"}},
{{"E", "I"}, {"E"}}, {{"E"}, {"E"}}, {{"C"}, {}},
{{"B", "E"}, {"E"}}, {{"A", "R", "I", "E"}, {"E"}},
{{"Y", "E"}, {"Y", "E"}},
{{"Y", "O", "I", "E"}, {"Y", "O", "E"}},
{{"Y", "A"}, {"Y"}}, {{"V", "T", "O"}, {"T", "O"}},
{{"U", "O", "E"}, {"E"}},
{{"T", "S", "M", "E", "D"}, {"T", "S", "M", "E"}},
{{"T", "R", "D"}, {"T", "R", "D"}},
{{"T", "R", "L", "B"}, {"T", "R", "L"}},
{{"T", "R", "L"}, {"T", "R", "L"}}, {{"T"}, {"T"}},
{{"T"}, {"T"}},
{{"S", "R", "N", "M"}, {"S", "R", "N", "M"}},
{{"S"}, {"S"}}, {{"S"}, {"S"}}, {{"S"}, {"S"}},
{{"R", "P"}, {"R", "P"}}, {{"P"}, {}}, {{"P", "O"}, {}},
{{"O", "E"}, {"E"}}, {{"O"}, {}}, {{"O"}, {}},
{{"O"}, {}}, {{"N"}, {}}, {{"M"}, {"M"}},
{{"M"}, {"M"}}, {{"L"}, {}}, {{"L"}, {"L"}},
{{"I"}, {}}, {{"I"}, {}}, {{"I", "E"}, {"E"}},
{{"I"}, {}}, {{"I", "E"}, {"E"}}, {{"I"}, {}},
{{"H"}, {}}, {{"H", "E"}, {"H", "E"}}, {{"H"}, {"H"}},
{{"F"}, {"F"}}, {{"E"}, {}}, {{"E"}, {"E"}},
{{"E"}, {}}, {{"E"}, {"E"}}, {{"E", "A"}, {"E"}},
{{"E"}, {"E"}}, {{"E"}, {"E"}}, {{"E"}, {"E"}},
{{"E"}, {"E"}}, {{"E"}, {"E"}}, {{"E"}, {"E"}},
{{"E"}, {"E"}}, {{"E"}, {"E"}}, {{"E"}, {"E"}},
{{"E"}, {"E"}}, {{"E"}, {"E"}}, {{"E"}, {"E"}},
{{"D"}, {"D"}}, {{"A"}, {}}, {{"A"}, {}}};
// ------------------------------------------------------------------------
randomWord = startBiGram[indexGenerator(startBiGram.length)];
int flag = 0;
int count = 0;
String previousWord;
while (randomWord.length() != wordLength) {
previousWord = randomWord;
randomWord = addCharacter(startBiGram, wordLength, randomWord,
lookupBiGram, nextCharLookup, flag);
if (previousWord.equals(randomWord)) {
count++;
} else {
flag = 0;
}
if (count == 5) {
flag = 1;
count++;
} else if (count == 20) {
randomWord = startBiGram[indexGenerator(startBiGram.length)];
count = 0;
}
}
return randomWord;
}
private static String addCharacter(String[] startBiGram, int desiredLength,
String currentWord, String[] lookupBiGram, String[][][] nextCharLookup,
int flag) {
int mainIndex = getLookupIndex(currentWord, lookupBiGram);
int type = 0;
if (currentWord.length() == (desiredLength - 1)) {
type = 1;
}
while (mainIndex < 0 || mainIndex > 263
|| nextCharLookup[mainIndex][type].length <= 0) {
if (currentWord.length() == 2) {
return startBiGram[indexGenerator(startBiGram.length)];
}
if (flag == 1) {
currentWord = backtrack(currentWord, 2);
flag = 0;
} else {
currentWord = backtrack(currentWord, 1);
}
mainIndex = getLookupIndex(currentWord, lookupBiGram);
if (type == 1) {
type = 0;
}
}
String updatedWord = currentWord
+ getNextCharacter(type, mainIndex, nextCharLookup);
return updatedWord;
}
private static int indexGenerator(int arrayLength) {
int theIndex;
Random generator = new Random();
theIndex = generator.nextInt(arrayLength);
return theIndex;
}
private static String getNextCharacter(int type, int mainIndex,
String[][][] theCharacterVault) {
String nextChar;
int i = indexGenerator(theCharacterVault[mainIndex][type].length);
nextChar = theCharacterVault[mainIndex][type][i];
return nextChar;
}
private static String backtrack(String theWord, int numberChars) {
theWord = theWord.substring(0, theWord.length() - numberChars);
return theWord;
}
private static int getLookupIndex(String theWord, String[] lookupArray) {
String lookupCharacters = theWord.substring(theWord.length() - 2);
int lookupIndex = Arrays.asList(lookupArray).indexOf(lookupCharacters);
return lookupIndex;
}
}

View File

@ -0,0 +1,29 @@
/*
* Licensed to the Apache Software Foundation (ASF) under one
* or more contributor license agreements. See the NOTICE file
* distributed with this work for additional information
* regarding copyright ownership. The ASF licenses this file
* to you 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 org.apache.hadoop.yarn.appcatalog.utils;
/**
* Word length exception class.
*/
public class WordLengthException extends Exception {
private static final long serialVersionUID = -217354336577036950L;
public WordLengthException(String message){
super(message);
}
}

View File

@ -0,0 +1,22 @@
/*
* Licensed to the Apache Software Foundation (ASF) under one
* or more contributor license agreements. See the NOTICE file
* distributed with this work for additional information
* regarding copyright ownership. The ASF licenses this file
* to you 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
* <p>
* http://www.apache.org/licenses/LICENSE-2.0
* <p>
* 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.
*
*/
/**
* Application Catalog Utilities classes.
*/
package org.apache.hadoop.yarn.appcatalog.utils;

View File

@ -0,0 +1,59 @@
/*
* Licensed to the Apache Software Foundation (ASF) under one
* or more contributor license agreements. See the NOTICE file
* distributed with this work for additional information
* regarding copyright ownership. The ASF licenses this file
* to you 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.
*/
var app = angular.module('app', [
'ngRoute',
'filters',
'controllers'
]);
app.directive('jsonText', function() {
return {
restrict: 'A',
require: 'ngModel',
link: function(scope, element, attr, ngModel) {
function into(input) {
console.log(JSON.parse(input));
return JSON.parse(input);
}
function out(data) {
return JSON.stringify(data);
}
ngModel.$parsers.push(into);
ngModel.$formatters.push(out);
}
};
});
app.config(['$routeProvider',
function ($routeProvider) {
$routeProvider.when('/', {
templateUrl: 'partials/home.html',
controller: 'AppStoreController'
}).when('/app/:id', {
templateUrl: 'partials/details.html',
controller: 'AppDetailsController'
}).when('/new', {
templateUrl: 'partials/new.html',
controller: 'NewAppController'
}).when('/deploy/:id', {
templateUrl: 'partials/deploy.html',
controller: 'DeployAppController'
}).otherwise({
redirectTo: '/'
});
}]);

View File

@ -0,0 +1,320 @@
/*
* Licensed to the Apache Software Foundation (ASF) under one
* or more contributor license agreements. See the NOTICE file
* distributed with this work for additional information
* regarding copyright ownership. The ASF licenses this file
* to you 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.
*/
var controllers = angular.module("controllers", []);
controllers.controller("AppListController", [ '$scope', '$rootScope', '$http',
function($scope, $rootScope, $http) {
$scope.appList = [];
function successCallback(response) {
$scope.appList = response.data;
$rootScope.$emit("hideLoadScreen", {});
}
function errorCallback(response) {
$rootScope.$emit("hideLoadScreen", {});
console.log("Error in downloading application list");
}
$rootScope.$on("RefreshAppList", function() {
$scope.refreshList();
});
$scope.refreshList = function() {
$http({
method : 'GET',
url : '/v1/app_list'
}).then(successCallback, errorCallback);
}
$scope.deleteApp = function(id, name) {
$rootScope.$emit("showLoadScreen", {});
$http({
method: 'DELETE',
url: '/v1/app_list/' + id + '/' + name
}).then(function(response) {
$rootScope.$emit("RefreshAppList", {});
window.location = '/#';
}, function(response) {
console.log(response);
});
}
$http({
method : 'GET',
url : '/v1/app_list'
}).then(successCallback, errorCallback);
} ]);
controllers.controller("AppStoreController", [ '$scope', '$rootScope', '$http',
function($scope, $rootScope, $http) {
$scope.canDeployApp = function() {
return false;
};
$scope.appStore = [];
$scope.searchText = null;
function successCallback(response) {
$scope.appStore = response.data;
}
function errorCallback(response) {
console.log("Error in downloading AppStore information.");
}
$scope.deployApp = function(id) {
window.location = '/#!/deploy/' + id;
}
$http({
method : 'GET',
url : '/v1/app_store/recommended'
}).then(successCallback, errorCallback);
$scope.change = function(text) {
var q = $scope.searchText;
$http({
method : 'GET',
url : '/v1/app_store/search?q=' + q
}).then(successCallback, errorCallback);
}
} ]);
controllers.controller("AppDetailsController", [ '$scope', '$interval', '$rootScope', '$http',
'$routeParams', function($scope, $interval, $rootScope, $http, $routeParams) {
$scope.details = {"yarnfile":{"state":"UNKNOWN"}};
$scope.appName = $routeParams.id;
var timer = $interval(function() {
$scope.refreshAppDetails();
}, 2000);
$scope.refreshAppDetails = function() {
$http({
method : 'GET',
url : '/v1/app_details/status/' + $scope.appName
}).then(successCallback, errorCallback);
}
$scope.stopApp = function(id) {
$http({
method : 'POST',
url : '/v1/app_details/stop/' + id
}).then(function(data, status, header, config) {
$scope.refreshAppDetails();
}, errorCallback);
}
$scope.restartApp = function(id) {
$http({
method : 'POST',
url : '/v1/app_details/restart/' + id
}).then(function(data, status, header, config) {
$scope.refreshAppDetails();
}, errorCallback);
}
$scope.canDeployApp = function() {
return true;
};
$scope.checkServiceLink = function() {
if ($scope.details.yarnfile.state != "STABLE") {
return true;
} else {
return false;
}
}
function successCallback(response) {
if (response.data.yarnfile.components.length!=0) {
$scope.details = response.data;
} else {
// When application is in accepted or failed state, it does not
// have components detail, hence we update states only.
$scope.details.yarnfile.state = response.data.yarnfile.state;
}
}
function errorCallback(response) {
console.log("Error in getting application detail");
}
$rootScope.$on("RefreshAppDetails", function() {
$scope.refreshAppDetails();
});
$scope.$on("$locationChangeStart", function() {
$interval.cancel(timer);
});
$scope.$on('$destroy', function() {
$interval.cancel(timer);
});
$http({
method : 'GET',
url : '/v1/app_details/config/' + $scope.appName
}).then(successCallback, errorCallback);
} ]);
controllers.controller("NewAppController", [ '$scope', '$rootScope', '$http', function($scope, $rootScope, $http) {
$scope.details = {
"name" : "",
"version" : "",
"organization" : "",
"description" : "",
"quicklinks": {
"UI": "http://${SERVICE_NAME}.${USER}.${DOMAIN}:8080/"
},
"icon" : "",
"components" : [
{
"name" : "",
"number_of_containers" : 1,
"artifact" : {
"id": "centos:latest"
},
"launch_command": "",
"resource" : {
"cpus" : 1,
"memory" : 2048
},
"run_privileged_container" : false,
"dependencies" : [],
"placement_policy" : {
"constraints" : []
},
"configuration" : {
"env" : {
"YARN_CONTAINER_RUNTIME_DOCKER_RUN_OVERRIDE_DISABLE":"true"
},
"properties" : {
"docker.network":"host"
}
}
}
]
};
$scope.template = {
"name" : "",
"number_of_containers" : 1,
"artifact" : {
"id": "centos:latest"
},
"launch_command": "",
"resource" : {
"cpus" : 1,
"memory" : 2048
},
"run_privileged_container" : false,
"dependencies" : [],
"placement_policy" : {
"constraints" : []
},
"configuration" : {
"env" : {
"YARN_CONTAINER_RUNTIME_DOCKER_RUN_OVERRIDE_DISABLE":"true"
},
"properties" : {
"docker.network":"host"
}
}
};
$scope.message = null;
$scope.error = null;
$scope.save = function() {
$http({
method : 'POST',
url : '/v1/app_store/register',
data : JSON.stringify($scope.details)
}).then(successCallback, errorCallback)
}
$scope.add = function() {
$scope.details.components.push($scope.template);
}
$scope.remove = function(index) {
$scope.details.components.splice(index, 1);
}
function successCallback(response) {
$scope.message = "Application published successfully.";
setTimeout(function() {
$scope.$apply(function() {
window.location = '/#';
});
}, 5000);
}
function errorCallback(response) {
$scope.error = "Error in registering application configuration.";
}
} ]);
controllers.controller("DeployAppController", [ '$scope', '$rootScope', '$http',
'$routeParams', function($scope, $rootScope, $http, $routeParams) {
$scope.id = $routeParams.id;
function successCallback(response) {
$scope.details = response.data;
$rootScope.$emit("hideLoadScreen", {});
}
function errorCallback(response) {
$rootScope.$emit("hideLoadScreen", {});
console.log("Error in downloading application template.");
}
$scope.launchApp = function(app) {
$rootScope.$emit("showLoadScreen", {});
$http({
method : 'POST',
url : '/v1/app_list/' + $scope.id,
data : JSON.stringify($scope.details.app)
}).then(function(data, status, headers, config) {
$rootScope.$emit("RefreshAppList", {});
window.location = '/#!/app/' + data.data.id;
}, function(data, status, headers, config) {
console.log('error', data, status);
});
}
$http({
method : 'GET',
url : '/v1/app_store/get/' + $scope.id
}).then(successCallback, errorCallback);
}]);
controllers.controller("LoadScreenController", [ '$scope', '$rootScope', '$http', function($scope, $rootScope, $http) {
$scope.loadScreen = "hide";
$rootScope.$on("showLoadScreen", function() {
$scope.loadScreen = "show";
});
$rootScope.$on("hideLoadScreen", function() {
$scope.loadScreen = "hide";
});
}]);

View File

@ -0,0 +1,32 @@
/*
* Licensed to the Apache Software Foundation (ASF) under one
* or more contributor license agreements. See the NOTICE file
* distributed with this work for additional information
* regarding copyright ownership. The ASF licenses this file
* to you 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.
*/
var app = angular.module("filters", []);
app.filter("counterValue",function(){
return function(value){
var count=parseInt(value), suffix="";
if (count>=1000000){
count=Math.round(count/1000000);
suffix="M"
} else if (count>=1000){
count=Math.round(count/1000);
suffix="K"
}
return""+count+suffix
}
});

View File

@ -0,0 +1,17 @@
/*
* Licensed to the Apache Software Foundation (ASF) under one
* or more contributor license agreements. See the NOTICE file
* distributed with this work for additional information
* regarding copyright ownership. The ASF licenses this file
* to you 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.
*/

View File

@ -0,0 +1,17 @@
/*
* Licensed to the Apache Software Foundation (ASF) under one
* or more contributor license agreements. See the NOTICE file
* distributed with this work for additional information
* regarding copyright ownership. The ASF licenses this file
* to you 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.
*/

View File

@ -0,0 +1,23 @@
# 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.
log4j.rootLogger = INFO, CATALINA
# Define all the appenders
log4j.appender.CATALINA = org.apache.log4j.DailyRollingFileAppender
log4j.appender.CATALINA.File = ${catalina.base}/logs/appcatalog.log
log4j.appender.CATALINA.Append = true
log4j.appender.CATALINA.Encoding = UTF-8
# Roll-over the log once per day
log4j.appender.CATALINA.DatePattern = '.'yyyy-MM-dd'.log'
log4j.appender.CATALINA.layout = org.apache.log4j.PatternLayout
log4j.appender.CATALINA.layout.ConversionPattern = %d [%t] %-5p %c- %m%n

View File

@ -0,0 +1,22 @@
<?xml version="1.0" encoding="UTF-8"?>
<!--
Licensed to the Apache Software Foundation (ASF) under one or more
contributor license agreements. See the NOTICE file distributed with
this work for additional information regarding copyright ownership.
The ASF licenses this file to You 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.
-->
<beans xmlns="http://xmlns.jcp.org/xml/ns/javaee"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://xmlns.jcp.org/xml/ns/javaee http://xmlns.jcp.org/xml/ns/javaee/beans_1_1.xsd"
bean-discovery-mode="annotated">
</beans>

View File

@ -0,0 +1,75 @@
<?xml version="1.0" encoding="ISO-8859-1"?>
<!--
Licensed to the Apache Software Foundation (ASF) under one or more
contributor license agreements. See the NOTICE file distributed with
this work for additional information regarding copyright ownership.
The ASF licenses this file to You 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.
-->
<web-app xmlns="http://java.sun.com/xml/ns/javaee"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://java.sun.com/xml/ns/javaee http://java.sun.com/xml/ns/javaee/web-app_3_0.xsd"
version="3.0">
<context-param>
<param-name>local-deployment</param-name>
<param-value>true</param-value>
</context-param>
<description>
YARN Application Catalog
</description>
<display-name>appcatalog</display-name>
<servlet>
<servlet-name>REST_API</servlet-name>
<servlet-class>com.sun.jersey.spi.container.servlet.ServletContainer</servlet-class>
<init-param>
<param-name>com.sun.jersey.config.property.packages</param-name>
<param-value>org.apache.hadoop.yarn.appcatalog.controller;com.wordnik.swagger.jaxrs.listing;com.wordnik.swagger.jaxrs.json</param-value>
</init-param>
<init-param>
<param-name>com.sun.jersey.api.json.POJOMappingFeature</param-name>
<param-value>true</param-value>
</init-param>
<load-on-startup>1</load-on-startup>
</servlet>
<servlet>
<servlet-name>DefaultJaxrsConfig</servlet-name>
<servlet-class>com.wordnik.swagger.jaxrs.config.DefaultJaxrsConfig</servlet-class>
<init-param>
<param-name>api.version</param-name>
<param-value>3</param-value>
</init-param>
<init-param>
<param-name>swagger.api.basepath</param-name>
<param-value>http://localhost:8080/v1</param-value>
</init-param>
<load-on-startup>2</load-on-startup>
</servlet>
<servlet-mapping>
<servlet-name>REST_API</servlet-name>
<url-pattern>/v1/*</url-pattern>
</servlet-mapping>
<filter>
<filter-name>CorsFilter</filter-name>
<filter-class>org.apache.catalina.filters.CorsFilter</filter-class>
</filter>
<filter-mapping>
<filter-name>CorsFilter</filter-name>
<url-pattern>/*</url-pattern>
</filter-mapping>
</web-app>

View File

@ -0,0 +1,8 @@
<svg width="200px" height="200px" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 100 100" preserveAspectRatio="xMidYMid" class="lds-dual-ring" style="background: none;">
<circle cx="50" cy="50" ng-attr-r="{{config.radius}}" ng-attr-stroke-width="{{config.width}}" ng-attr-stroke="{{config.c1}}" ng-attr-stroke-dasharray="{{config.dasharray}}" fill="none" stroke-linecap="round" r="40" stroke-width="4" stroke="#84de5c" stroke-dasharray="62.83185307179586 62.83185307179586" transform="rotate(318 50 50)">
<animateTransform attributeName="transform" type="rotate" calcMode="linear" values="0 50 50;360 50 50" keyTimes="0;1" dur="1s" begin="0s" repeatCount="indefinite"></animateTransform>
</circle>
<circle cx="50" cy="50" ng-attr-r="{{config.radius2}}" ng-attr-stroke-width="{{config.width}}" ng-attr-stroke="{{config.c2}}" ng-attr-stroke-dasharray="{{config.dasharray2}}" ng-attr-stroke-dashoffset="{{config.dashoffset2}}" fill="none" stroke-linecap="round" r="35" stroke-width="4" stroke="#21d129" stroke-dasharray="54.97787143782138 54.97787143782138" stroke-dashoffset="54.97787143782138" transform="rotate(-318 50 50)">
<animateTransform attributeName="transform" type="rotate" calcMode="linear" values="0 50 50;-360 50 50" keyTimes="0;1" dur="1s" begin="0s" repeatCount="indefinite"></animateTransform>
</circle>
</svg>

After

Width:  |  Height:  |  Size: 1.3 KiB

View File

@ -0,0 +1,252 @@
/**
* Licensed to the Apache Software Foundation (ASF) under one
* or more contributor license agreements. See the NOTICE file
* distributed with this work for additional information
* regarding copyright ownership. The ASF licenses this file
* to you 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.
*/
.tile {
margin: 5px 5px 0px 5px;
padding: 5px;
border: 1px solid #cdcdcd;
height: 210px;
overflow: hidden;
background-color: #ffffff;
border-top-left-radius: 4px;
border-top-right-radius: 4px;
min-width: 250px;
}
.panel {
margin: 5px 0px 0px 0px;
padding: 5px;
border: 1px solid #cdcdcd;
}
.desc {
position: relative;
top: 100px;
margin: 5px;
overflow: hidden;
text-overflow: ellipsis;
height: 60px;
padding-right: 25px;
}
.appBanner {
display: block;
height: 40px;
}
.appList {
display: block;
}
html, body, .container {
height: 100%;
}
.container {
display:table;
width: 100%;
margin: 0;
padding: 0; /*set left/right padding according to needs*/
box-sizing: border-box;
}
.row {
height: 100%;
display: table-row;
}
.wizard {
height: 100%;
border: none;
}
.wizard-body {
height: 100%;
}
.navigation-header div.btn-group div {
display: inline;
}
.navigation-header div.btn-group div a:hover {
text-decoration: none;
}
.navigation-bar-container ul.nav.side-nav-menu li.mainmenu-li {
color: #999;
}
.navigation-bar-container ul.nav.side-nav-menu li.mainmenu-li:hover {
background-color: #404351;
}
.navigation-menu-item {
padding-left: 30px;
}
ul.nav.side-nav-menu li {
height: 55px;
}
.navigation-bar-container ul.nav.side-nav-menu li.mainmenu-li>a {
color: #b8bec4;
}
.navigation-bar {
height: 100%;
}
.navigation-bar-container {
height: 100%;
}
.content {
margin: 5px;
}
.mdl-shadow-4dp {
box-shadow: 0 4px 5px 0 rgba(0,0,0,.14), 0 1px 10px 0 rgba(0,0,0,.12), 0 2px 4px -1px rgba(0,0,0,.2);
}
/*
* hexagon
*/
.hexa, .hexa div {
position: absolute;
top: -5px;
left: 0px;
overflow: hidden;
width: 128px;
height: 128px;
}
.hexa {
width: 128px;
height: 96px;
}
.hexa div {
width: 100%;
height: 100%;
}
.hexa {
-webkit-transform: rotate(120deg);
-moz-transform: rotate(120deg);
-ms-transform: rotate(120deg);
-o-transform: rotate(120deg);
transform: rotate(120deg);
}
.hex1 {
-webkit-transform: rotate(-60deg);
-moz-transform: rotate(-60deg);
-ms-transform: rotate(-60deg);
-o-transform: rotate(-60deg);
transform: rotate(-60deg);
}
.hex2 {
box-shadow: 0 4px 5px 0 rgba(0,0,0,.14), 0 1px 10px 0 rgba(0,0,0,.12), 0 2px 4px -1px rgba(0,0,0,.2);
-webkit-transform: rotate(-60deg);
-moz-transform: rotate(-60deg);
-ms-transform: rotate(-60deg);
-o-transform: rotate(-60deg);
transform: rotate(-60deg);
}
/*
* card title
*/
.card-title {
position: absolute;
top: 5px;
left: 128px;
}
.card-desc {
margin: 10px;
padding-right: 25px;
}
.card-footer {
position: relative;
margin: 0px 5px 5px 5px;
padding: 5px;
background-color: #eeeeee;
line-height: 25px;
height: 45px;
color: #666;
font-family: Roboto,sans-serif!important;
font-size: 14;
min-width: 250px;
border-bottom-left-radius: 4px;
border-bottom-right-radius: 4px;
}
.toparea {
position: fixed;
top: 0px;
left: 230px;
width: calc(100% - 230px);
height: 300px;
color: #2c2e3b;
background: -webkit-linear-gradient(left,#2e3b51 ,#181d2b);
background: -moz-linear-gradient(right,#2e3b51,#181d2b);
background: -o-linear-gradient(right,#2e3b51,#181d2b);
background: linear-gradient(to right,#2e3b51 ,#181d2b);
}
.pattern-wrapper {
width: 100%;
height: 300px;
background-image: url(img/pattern-f61c2e99f82389a67432f54155c5f483.png);
background-repeat: no-repeat;
background-position: 0px 30px;
position: absolute;
top: 0;
left: 0;
}
.recommended {
color: #fff;
}
.loader-wrapper {
position: fixed;
top: 0;
width: 100%;
height: 100%;
background: #222222;
z-index: 1000;
opacity: 0.3;
}
.loader {
display: table;
margin: 0 auto;
position: relative;
top: 50%;
transform: translateY(-50%);
}
.loader img {
width: 300px;
height: 225px;
}
.infobox {
font-size:14px;
margin:20px;
padding:10px;
}

View File

@ -0,0 +1,30 @@
/**
* Licensed to the Apache Software Foundation (ASF) under one
* or more contributor license agreements. See the NOTICE file
* distributed with this work for additional information
* regarding copyright ownership. The ASF licenses this file
* to you 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.
*/
.theme-dropdown .dropdown-menu {
position: static;
display: block;
margin-bottom: 20px;
}
.theme-showcase > p > .btn {
margin: 5px 0;
}
.theme-showcase .navbar .container {
width: auto;
}

View File

@ -0,0 +1,82 @@
<!---
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. See accompanying LICENSE file.
-->
<!doctype html>
<html lang="en" data-ng-app="app">
<head>
<meta charset="utf-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="viewport" content="width=device-width, initial-scale=1">
<title>YARN Application Catalog</title>
<link rel="stylesheet" href="vendor/bootstrap/dist/css/bootstrap.min.css">
<link rel="stylesheet" href="vendor/bootstrap/dist/css/bootstrap-theme.min.css">
<link href="css/bootstrap-hadoop.min.css" rel="stylesheet">
<link rel="stylesheet" href="css/theme.css">
<link rel="stylesheet" href="css/specific.css">
</head>
<body role="document">
<div ng-controller="LoadScreenController" class="loader-wrapper {{loadScreen}}">
<div class="loader"><img src="/css/img/loading.svg"></div>
</div>
<div class="container">
<div class="row">
<div class="navigation-bar-container col-sm-3 col-md-3 col-lg-3" ng-controller="AppListController">
<ul class="side-nav-header nav nav-pills nav-stacked">
<li class="navigation-header">
<div class="btn-group">
<div>
<a href="#"><span class="ambari-header">Applications</span></a>
</div>
<div class="dropdown-toggle" data-toggle="dropdown" aria-haspopup="true" aria-expanded="false">
<span class="toggle-icon glyphicon glyphicon-menu-down"></span>
</div>
<ul class="dropdown-menu">
<li><a href="#!/">Appstore</a></li>
<li><a href="#!/new">Register App</a></li>
</ul>
</div>
</li>
</ul>
<ul class="side-nav-menu nav nav-pills nav-stacked">
<li class="mainmenu-li" ng-repeat="yarnApp in appList">
<a href="#!/app/{{yarnApp.id}}"><span class="glyphicon glyphicon-ok-sign"></span> {{yarnApp.name}}</a>
<a ng-click="deleteApp(yarnApp.id,yarnApp.name)" class="icon-width"><span class="glyphicon glyphicon-remove pull-right"></span></a>
<span class="navigation-menu-item">{{yarnApp.app}}</span>
</li>
</ul>
<ul class="side-nav-footer nav nav-pills nav-stacked">
<li class="navigation-footer">
<a href="#" data-toggle="collapse-side-nav">
<span class="navigation-icon fa fa-angle-double-left"></span>
</a>
</li>
</ul>
</div>
<div class="col-xs-9 col-md-9 col-lg-9">
<div data-ng-view></div>
</div>
</div>
</div>
<script src="vendor/jquery/dist/jquery.min.js"></script>
<script src="vendor/bootstrap/dist/js/bootstrap.min.js"></script>
<script src="vendor/angular/angular.min.js"></script>
<script src="vendor/angular-route/angular-route.min.js"></script>
<script src="js/bootstrap-hadoop.min.js"></script>
<script src="js/app.js"></script>
<script src="js/controllers.js"></script>
<script src="js/filters.js"></script>
<script src="js/routes.js"></script>
<script src="js/services.js"></script>
</body>
</html>

View File

@ -0,0 +1,284 @@
/**
* Licensed to the Apache Software Foundation (ASF) under one
* or more contributor license agreements. See the NOTICE file
* distributed with this work for additional information
* regarding copyright ownership. The ASF licenses this file
* to you 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.
*/
'use strict';
$(document).ready(function () {
var $accordionToggler = $(this).find('[data-toggle="collapseAccordion"]');
$accordionToggler.off('click').on('click', function (event) {
var $this = $(this);
$this.siblings('.panel-body').slideToggle(500);
$this.children().children('.panel-toggle').toggleClass('fa-angle-down fa-angle-up');
event.stopPropagation();
return false;
});
});
'use strict';
(function ($) {
/**
* jQuery plugin for navigation bars
* Usage:
* <pre>
* $('.navigation-bar').navigationBar();
* </pre>
*
* @param {object} options see <code>$.fn.navigationBar.defaults</code>
* @returns {$}
*/
$.fn.navigationBar = function (options) {
var settings = $.extend({}, $.fn.navigationBar.defaults, options);
return this.each(function () {
var _this = this;
var containerSelector = '.navigation-bar-container';
var $navigationContainer = $(this).find(containerSelector);
var $sideNavToggler = $(this).find('[data-toggle=' + settings.navBarToggleDataAttr + ']');
var $subMenuToggler = $(this).find('[data-toggle=' + settings.subMenuNavToggleDataAttr + ']');
var firstLvlMenuItemsSelector = '.side-nav-menu>li';
var secondLvlMenuItemsSelector = '.side-nav-menu>li>ul>li';
var $moreActions = $(this).find('.more-actions');
var $dropdownMenu = $moreActions.children('.dropdown-menu');
$subMenuToggler.each(function (index, toggler) {
return $(toggler).parent().addClass('has-sub-menu');
});
if (settings.fitHeight) {
$(this).addClass('navigation-bar-fit-height');
// make scrolling effect on side nav ONLY, i.e. not effected on ambari main contents
$(this).find('.side-nav-menu').on('DOMMouseScroll mousewheel', function (ev) {
var $this = $(this),
scrollTop = this.scrollTop,
scrollHeight = this.scrollHeight,
height = $this.innerHeight(),
delta = ev.originalEvent.wheelDelta,
up = delta > 0;
var prevent = function prevent() {
ev.stopPropagation();
ev.preventDefault();
ev.returnValue = false;
return false;
};
if (!up && -delta > scrollHeight - height - scrollTop) {
// Scrolling down, but this will take us past the bottom.
$this.scrollTop(scrollHeight);
return prevent();
} else if (up && delta > scrollTop) {
// Scrolling up, but this will take us past the top.
$this.scrollTop(0);
return prevent();
}
});
}
//set main content left margin based on the width of side-nav
var containerWidth = $navigationContainer.width();
if (settings.moveLeftContent) {
$(settings.content).css('margin-left', containerWidth);
}
if (settings.moveLeftFooter) {
$(settings.footer).css('margin-left', containerWidth);
}
function popStateHandler() {
var path = window.location.pathname + window.location.hash;
$navigationContainer.find('li a').each(function (index, link) {
var $link = $(link);
var href = $link.attr('data-href') || $link.attr('href');
if (path.indexOf(href) !== -1 && ['', '#'].indexOf(href) === -1) {
$link.parent().addClass('active');
} else {
$link.parent().removeClass('active');
}
});
}
if (settings.handlePopState) {
popStateHandler();
$(window).bind('popstate', popStateHandler);
}
function clickHandler(el) {
var $li = $(el).parent();
var activeClass = settings.activeClass;
var activeMenuItems = firstLvlMenuItemsSelector + '.' + activeClass;
var activeSubMenuItems = secondLvlMenuItemsSelector + '.' + activeClass;
$navigationContainer.find(activeMenuItems).removeClass(activeClass);
$navigationContainer.find(activeSubMenuItems).removeClass(activeClass);
$li.addClass(activeClass);
}
/**
* Click on menu item
*/
$(firstLvlMenuItemsSelector + '>a').on('click', function () {
clickHandler(this);
});
/**
* Click on sub menu item
*/
$(secondLvlMenuItemsSelector + '>a').on('click', function () {
clickHandler(this);
$(this).parent().parent().parent().addClass(settings.activeClass);
});
/**
* Slider for sub menu
*/
$subMenuToggler.off('click').on('click', function (event) {
// ignore click if navigation-bar is collapsed
if ($navigationContainer.hasClass('collapsed')) {
return false;
}
var $this = $(this);
$this.siblings('.sub-menu').slideToggle(600, function () {
var $topMenuItem = $this.parent();
var $subMenu = $topMenuItem.find('ul');
return $subMenu.is(':visible') ? $topMenuItem.removeClass('collapsed') : $topMenuItem.addClass('collapsed');
});
$this.children('.toggle-icon').toggleClass(settings.menuLeftClass + ' ' + settings.menuDownClass);
event.stopPropagation();
return false;
});
/**
* Hovering effects for "more actions icon": "..."
*/
$(this).find('.mainmenu-li>a').hover(function () {
var $moreIcon = $(this).siblings('.more-actions');
if ($moreIcon.length && !$navigationContainer.hasClass('collapsed')) {
$moreIcon.css('display', 'inline-block');
}
}, function () {
var $moreIcon = $(this).siblings('.more-actions');
if ($moreIcon.length && !$navigationContainer.hasClass('collapsed')) {
$moreIcon.hide();
}
});
$moreActions.hover(function () {
$(this).css('display', 'inline-block');
});
if (settings.fitHeight) {
$moreActions.on('click', function () {
// set actions submenu position
var $moreIcon = $(this);
var $header = $('.side-nav-header');
$dropdownMenu.css({
top: $moreIcon.offset().top - $header.offset().top + 20 + 'px',
left: $moreIcon.offset().left + 'px'
});
});
}
$dropdownMenu.on('click', function () {
// some action was triggered, should hide this icon
var moreIcon = $(this).parent();
setTimeout(function () {
moreIcon.hide();
}, 1000);
});
$navigationContainer.children('.side-nav-menu').scroll(function () {
$moreActions.removeClass('open');
});
/**
* Expand/collapse navigation bar
*/
$sideNavToggler.click(function () {
$navigationContainer.toggleClass('collapsed').promise().done(function () {
var subMenuSelector = 'ul.sub-menu';
var $subMenus = $navigationContainer.find(subMenuSelector);
var $subMenuItems = $navigationContainer.find('.side-nav-menu>li');
if ($navigationContainer.hasClass('collapsed')) {
// set sub menu invisible when collapsed
$subMenus.hide();
$moreActions.hide();
// set the hover effect when collapsed, should show sub-menu on hovering
$subMenuItems.hover(function () {
$(this).find(subMenuSelector).show();
// set sub-menu position
var $parent = $(this);
var $header = $('.side-nav-header');
if (settings.fitHeight) {
$(this).find(subMenuSelector).css({
position: 'fixed',
top: $parent.offset().top - $header.offset().top + 'px',
left: 50 + 'px'
});
}
}, function () {
$(this).find(subMenuSelector).hide();
});
} else {
// keep showing all sub menu
$subMenus.show().each(function (index, item) {
return $(item).parent().removeClass('collapsed');
});
$subMenuItems.unbind('mouseenter mouseleave');
$navigationContainer.find('.toggle-icon').removeClass(settings.menuLeftClass).addClass(settings.menuDownClass);
// set sub-menu position
if (settings.fitHeight) {
$(_this).find(subMenuSelector).css({
position: 'relative',
top: 0,
left: 0
});
}
}
$navigationContainer.on('transitionend', function () {
//set main content left margin based on the width of side-nav
var containerWidth = $navigationContainer.width();
if (settings.moveLeftContent) {
$(settings.content).css('margin-left', containerWidth);
}
if (settings.moveLeftFooter) {
$(settings.footer).css('margin-left', containerWidth);
}
});
$sideNavToggler.find('span').toggleClass(settings.collapseNavBarClass + ' ' + settings.expandNavBarClass);
});
return false;
});
});
};
$.fn.navigationBar.defaults = {
handlePopState: true,
fitHeight: false,
content: '#main',
footer: 'footer',
moveLeftContent: true,
moveLeftFooter: true,
menuLeftClass: 'glyphicon-menu-right',
menuDownClass: 'glyphicon-menu-down',
collapseNavBarClass: 'fa-angle-double-left',
expandNavBarClass: 'fa-angle-double-right',
activeClass: 'active',
navBarToggleDataAttr: 'collapse-side-nav',
subMenuNavToggleDataAttr: 'collapse-sub-menu'
};
})(jQuery);

View File

@ -0,0 +1,19 @@
/**
* Licensed to the Apache Software Foundation (ASF) under one
* or more contributor license agreements. See the NOTICE file
* distributed with this work for additional information
* regarding copyright ownership. The ASF licenses this file
* to you 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.
*/
"use strict";$(document).ready(function(){$(this).find('[data-toggle="collapseAccordion"]').off("click").on("click",function(n){var l=$(this);return l.siblings(".panel-body").slideToggle(500),l.children().children(".panel-toggle").toggleClass("fa-angle-down fa-angle-up"),n.stopPropagation(),!1})});
"use strict";!function(e){e.fn.navigationBar=function(n){var t=e.extend({},e.fn.navigationBar.defaults,n);return this.each(function(){function n(){var n=window.location.pathname+window.location.hash;i.find("li a").each(function(t,a){var s=e(a),i=s.attr("data-href")||s.attr("href");-1!==n.indexOf(i)&&-1===["","#"].indexOf(i)?s.parent().addClass("active"):s.parent().removeClass("active")})}function a(n){var a=e(n).parent(),s=t.activeClass,o=r+"."+s,l=f+"."+s;i.find(o).removeClass(s),i.find(l).removeClass(s),a.addClass(s)}var s=this,i=e(this).find(".navigation-bar-container"),o=e(this).find("[data-toggle="+t.navBarToggleDataAttr+"]"),l=e(this).find("[data-toggle="+t.subMenuNavToggleDataAttr+"]"),r=".side-nav-menu>li",f=".side-nav-menu>li>ul>li",c=e(this).find(".more-actions"),u=c.children(".dropdown-menu");l.each(function(n,t){return e(t).parent().addClass("has-sub-menu")}),t.fitHeight&&(e(this).addClass("navigation-bar-fit-height"),e(this).find(".side-nav-menu").on("DOMMouseScroll mousewheel",function(n){var t=e(this),a=this.scrollTop,s=this.scrollHeight,i=t.innerHeight(),o=n.originalEvent.wheelDelta,l=o>0,r=function(){return n.stopPropagation(),n.preventDefault(),n.returnValue=!1,!1};return!l&&-o>s-i-a?(t.scrollTop(s),r()):l&&o>a?(t.scrollTop(0),r()):void 0}));var d=i.width();t.moveLeftContent&&e(t.content).css("margin-left",d),t.moveLeftFooter&&e(t.footer).css("margin-left",d),t.handlePopState&&(n(),e(window).bind("popstate",n)),e(r+">a").on("click",function(){a(this)}),e(f+">a").on("click",function(){a(this),e(this).parent().parent().parent().addClass(t.activeClass)}),l.off("click").on("click",function(n){if(i.hasClass("collapsed"))return!1;var a=e(this);return a.siblings(".sub-menu").slideToggle(600,function(){var e=a.parent();return e.find("ul").is(":visible")?e.removeClass("collapsed"):e.addClass("collapsed")}),a.children(".toggle-icon").toggleClass(t.menuLeftClass+" "+t.menuDownClass),n.stopPropagation(),!1}),e(this).find(".mainmenu-li>a").hover(function(){var n=e(this).siblings(".more-actions");n.length&&!i.hasClass("collapsed")&&n.css("display","inline-block")},function(){var n=e(this).siblings(".more-actions");n.length&&!i.hasClass("collapsed")&&n.hide()}),c.hover(function(){e(this).css("display","inline-block")}),t.fitHeight&&c.on("click",function(){var n=e(this),t=e(".side-nav-header");u.css({top:n.offset().top-t.offset().top+20+"px",left:n.offset().left+"px"})}),u.on("click",function(){var n=e(this).parent();setTimeout(function(){n.hide()},1e3)}),i.children(".side-nav-menu").scroll(function(){c.removeClass("open")}),o.click(function(){return i.toggleClass("collapsed").promise().done(function(){var n=i.find("ul.sub-menu"),a=i.find(".side-nav-menu>li");i.hasClass("collapsed")?(n.hide(),c.hide(),a.hover(function(){e(this).find("ul.sub-menu").show();var n=e(this),a=e(".side-nav-header");t.fitHeight&&e(this).find("ul.sub-menu").css({position:"fixed",top:n.offset().top-a.offset().top+"px",left:"50px"})},function(){e(this).find("ul.sub-menu").hide()})):(n.show().each(function(n,t){return e(t).parent().removeClass("collapsed")}),a.unbind("mouseenter mouseleave"),i.find(".toggle-icon").removeClass(t.menuLeftClass).addClass(t.menuDownClass),t.fitHeight&&e(s).find("ul.sub-menu").css({position:"relative",top:0,left:0})),i.on("transitionend",function(){var n=i.width();t.moveLeftContent&&e(t.content).css("margin-left",n),t.moveLeftFooter&&e(t.footer).css("margin-left",n)}),o.find("span").toggleClass(t.collapseNavBarClass+" "+t.expandNavBarClass)}),!1})})},e.fn.navigationBar.defaults={handlePopState:!0,fitHeight:!1,content:"#main",footer:"footer",moveLeftContent:!0,moveLeftFooter:!0,menuLeftClass:"glyphicon-menu-right",menuDownClass:"glyphicon-menu-down",collapseNavBarClass:"fa-angle-double-left",expandNavBarClass:"fa-angle-double-right",activeClass:"active",navBarToggleDataAttr:"collapse-side-nav",subMenuNavToggleDataAttr:"collapse-sub-menu"}}(jQuery);

View File

@ -0,0 +1,80 @@
<!---
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. See accompanying LICENSE file.
-->
<div class="container content">
<div class="row">
<div class="col-xs-12 col-md-12 col-lg-12">
<div class="form-group">
<h1>Deploy Application</h1>
</div>
</div>
</div>
<div class="row">
<div class="col-xs-2 col-md-2 col-lg-2">
<img ng-src="{{details.icon}}" alt="" width="128" height="128" ng-if="details.icon != null" />
</div>
<div class="col-xs-10 col-md-10 col-lg-10">
<div class="form-group">
<h2>{{details.org}}/{{details.name}}</h2>
{{details.desc}}
</div>
</div>
</div>
<div class="row">
<div class="col-xs-12 col-md-12 col-lg-12">
<div class="form-group">
<label>Application Name</label>
<input type=text name="name" class="form-control" ng-model="details.app.name" autofocus />
</div>
<div class="form-group">
<label>Queue</label>
<input type=text name="queue" class="form-control" ng-model="details.app.queue" />
</div>
</div>
</div>
<div class="row">
<div class="col-xs-12 col-md-12 col-lg-12" ng-repeat="docker in details.app.components track by $index">
<div class="panel">
<div class="form-group">
<label>Component {{docker.name}}</label>
</div>
<div class="form-group">
<label>Number of containers</label>
<input type=text name="artifact_id" class="form-control" ng-model="docker.number_of_containers" />
</div>
<div class="form-group">
<label>Environments</label>
<textarea json-text name="env" class="form-control" ng-model="docker.configuration.env"/></textarea>
</div>
<div class="form-group">
<label>Properties</label>
<textarea json-text name="properties" class="form-control" ng-model="docker.configuration.properties"/></textarea>
</div>
</div>
</div>
</div>
<br>
<div class="row">
<div class="col-xs-12 col-md-12 col-lg-12">
<p>
<a class="btn btn-secondary" ng-click="launchApp(details.app)">LAUNCH</a>
<a class="btn btn-secondary" href="/">CANCEL</a>
<span class="infobox bg-info" ng-if="message">{{message}}</span>
<span class="infobox bg-danger" ng-if="error">{{error}}</span>
</div>
</div>
</div>

View File

@ -0,0 +1,64 @@
<!---
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. See accompanying LICENSE file.
-->
<div class="container content">
<div class="row">
<div class="col-xs-12 col-md-12 col-lg-12">
<a ng-click="stopApp(appName)" class="btn btn-secondary"><span class="glyphicon glyphicon-stop"></span> Stop</a>
<a ng-click="restartApp(appName)" class="btn btn-secondary"><span class="glyphicon glyphicon-refresh"></span> Start</a>
<div style="display:inline-block;" ng-repeat="(key, value) in details.yarnfile.quicklinks">
<a href="{{value}}" class="btn btn-secondary" ng-hide="checkServiceLink()"><span class="glyphicon glyphicon-new-window"></span> {{key}}</a>
</div>
{{details.yarnfile.state}}
</div>
</div>
<div class="row">
<div class="col-xs-12 col-md-12 col-lg-12" ng-repeat="docker in details.yarnfile.components">
<div class="panel">
<h3>{{docker.name}}</h3>
<p>Artifact: {{docker.artifact.id}}</p>
<p>Number of containers: {{docker.number_of_containers}}</p>
<p>CPU: {{docker.resource.cpus}}</p>
<p>Memory: {{docker.resource.memory}}</p>
<p>Environments
<table class="table">
<tbody class="table-striped">
<tr ng-repeat="(key, value) in docker.configuration.env">
<td>{{key}}</td><td>{{value}}</td>
</tr>
</tbody>
</table>
</p>
<table class="table">
<thead>
<tr>
<th>Hostname</th>
<th>Container ID</th>
<th>Launch Time</th>
<th>State</th>
</tr>
</thead>
<tbody class="table-striped">
<tr ng-repeat="container in docker.containers">
<td>{{container.bare_host}}</td>
<td><a href="http://{{container.bare_host}}:8042/terminal/terminal.template?container={{container.id}}" target="_blank">{{container.id}}</a></td>
<td>{{container.launch_time | date:'yyyy-MM-dd HH:mm:ss Z'}}</td>
<td>{{container.state}}</td>
</tr>
</tbody>
</table>
</div>
</div>
</div>
</div>

View File

@ -0,0 +1,52 @@
<!---
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. See accompanying LICENSE file.
-->
<div class="toparea"><div class="pattern-wrapper"></div></div>
<div class="container content toparea pattern-wrapper">
<form>
<div class="input-group input-group-lg">
<div class="input-group-addon">
<span class="glyphicon glyphicon-search" id="search"></span>
</div>
<input type="text" class="form-control" placeholder="Search for applications from HDFS" aria-describedby="search" ng-model="searchText" ng-change="change(text)" />
</div>
</form>
<p></p>
<p class="recommended">Recommended</p>
<div ng-repeat="a in appStore" ng-if="$index % 3 == 0" class="row">
<div ng-repeat="i in [$index, $index + 1, $index + 2]"
ng-if="appStore[i] != null" class="col-xs-4 col-md-4 col-lg-4">
<div class="tile mdl-shadow-4dp">
<img ng-src="{{appStore[i].icon}}" alt="" width="128" height="128" ng-if="appStore[i] != null" />
<div class="card-title">
<h3>{{appStore[i].org}}</h3>
<h2>{{appStore[i].name}}</h2>
</div>
<div class="card-desc">
<p>{{appStore[i].desc}}</p>
</div>
</div>
<div class="card-footer mdl-shadow-4dp">
<div class="pull-left">
<!-- <span class="glyphicon glyphicon-thumbs-up"></span> {{appStore[i].like | counterValue}} -->
<span class="glyphicon glyphicon-cloud-download"></span> {{appStore[i].download | counterValue}}
</div>
<div class="pull-right">
<button class="btn btn-secondary" ng-click="deployApp(appStore[i].id)">DEPLOY</button>
</div>
</div>
</div>
</div>
</div>

View File

@ -0,0 +1,111 @@
<!---
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. See accompanying LICENSE file.
-->
<div class="container content">
<div class="row">
<div class="col-xs-12 col-md-12 col-lg-12">
<div class="form-group">
<h1>Register Application</h1>
</div>
<div class="form-group">
<label>Organization Name</label>
<input type=text name="org" class="form-control" ng-model="details.organization" />
</div>
<div class="form-group">
<label>Application Name</label>
<input type=text name="name" class="form-control" ng-model="details.name" />
</div>
<div class="form-group">
<label>Version</label>
<input type=text name="version" class="form-control" ng-model="details.version" />
</div>
<div class="form-group">
<label>Description</label>
<textarea name="desc" class="form-control" ng-model="details.description"></textarea>
</div>
<div class="form-group">
<label>Quick Link</label>
<textarea json-text name="quicklink" class="form-control" ng-model="details.quicklinks"/></textarea>
</div>
<div class="form-group">
<label>Icon</label>
<input type=text name="icon" class="form-control" ng-model="details.icon" />
</div>
</div>
</div>
<div class="row">
<div class="col-xs-12 col-md-12 col-lg-12" ng-repeat="docker in details.components track by $index">
<div class="panel">
<div class="pull-right">
<a class="btn btn-secondary" ng-click="remove($index)">-</a>
</div>
<div class="form-group">
<label>Component Name</label>
<input type=text name="name" class="form-control" ng-model="docker.name" />
</div>
<div class="form-group">
<label>Artifact</label>
<input type=text name="artifact_id" class="form-control" ng-model="docker.artifact.id" />
</div>
<div class="form-group">
<label>Number of containers</label>
<input type=text name="artifact_id" class="form-control" ng-model="docker.number_of_containers" />
</div>
<div class="form-group">
<label>Launch Command</label>
<input type=text name="launch_command" class="form-control" ng-model="docker.launch_command" />
</div>
<div class="form-group">
<label>CPU</label>
<input type=text name="cpus" class="form-control" ng-model="docker.resource.cpus" />
</div>
<div class="form-group">
<label>Memory</label>
<input type=text name="memory" class="form-control" ng-model="docker.resource.memory" />
</div>
<div class="form-group">
<input type="checkbox" ng-attr-id="{{'checkbox-priv-' + $index}}" ng-model="docker.run_privileged_container">
<label for="checkbox-priv-{{$index}}"> Privileged Container</label>
</div>
<div class="form-group">
<label>Dependencies</label>
<input json-text type=text name="dependencies" class="form-control" ng-model="docker.dependencies" />
</div>
<div class="form-group">
<label>Placement Policy</label>
<input type=text name="placement" class="form-control" ng-model="docker.placement_policy.constraints" />
</div>
<div class="form-group">
<label>Environments</label>
<textarea json-text name="env" class="form-control" ng-model="docker.configuration.env"/></textarea>
</div>
<div class="form-group">
<label>Properties</label>
<textarea json-text name="properties" class="form-control" ng-model="docker.configuration.properties"/></textarea>
</div>
</div>
</div>
</div>
<br>
<div class="row">
<div class="col-xs-12 col-md-12 col-lg-12">
<p>
<a class="btn btn-secondary" ng-click="add()">+</a>
<a class="btn btn-secondary" ng-click="save()">Publish</a>
<span class="infobox bg-info" ng-if="message">{{message}}</span>
<span class="infobox bg-danger" ng-if="error">{{error}}</span>
</div>
</div>
</div>

View File

@ -0,0 +1,649 @@
<!---
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. See accompanying LICENSE file.
-->
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="utf-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="viewport" content="width=device-width, initial-scale=1">
<meta name="description" content="">
<meta name="author" content="">
<title>Theme Template for Bootstrap</title>
<!-- Bootstrap core CSS -->
<link href="vendor/bootstrap/dist/css/bootstrap.min.css" rel="stylesheet">
<!-- Bootstrap theme -->
<link href="vendor/bootstrap/dist/css/bootstrap-theme.min.css" rel="stylesheet">
<!-- Custom styles for this template -->
<link href="css/theme.css" rel="stylesheet">
<link href="css/bootstrap-hadoop.min.css" rel="stylesheet">
</head>
<body role="document">
<!-- Fixed navbar -->
<div class="navbar navbar-inverse navbar-fixed-top" role="navigation">
<div class="container">
<div class="navbar-header">
<button type="button" class="navbar-toggle collapsed" data-toggle="collapse" data-target=".navbar-collapse">
<span class="sr-only">Toggle navigation</span>
<span class="icon-bar"></span>
<span class="icon-bar"></span>
<span class="icon-bar"></span>
</button>
<a class="navbar-brand" href="#">Bootstrap theme</a>
</div>
<div class="navbar-collapse collapse">
<ul class="nav navbar-nav">
<li class="active"><a href="index.html">Home</a></li>
<li><a href="#about">About</a></li>
<li><a href="#contact">Contact</a></li>
<li class="dropdown">
<a href="#" class="dropdown-toggle" data-toggle="dropdown">Dropdown <span class="caret"></span></a>
<ul class="dropdown-menu" role="menu">
<li><a href="#">Action</a></li>
<li><a href="#">Another action</a></li>
<li><a href="#">Something else here</a></li>
<li class="divider"></li>
<li class="dropdown-header">Nav header</li>
<li><a href="#">Separated link</a></li>
<li><a href="#">One more separated link</a></li>
</ul>
</li>
</ul>
</div>
<!--/.nav-collapse -->
</div>
</div>
<div class="container theme-showcase" role="main">
<!-- Main jumbotron for a primary marketing message or call to action -->
<div class="jumbotron">
<h1>Hello, world!</h1>
<p>This is a template for a simple marketing or informational website. It includes a large callout called a
jumbotron and three supporting pieces of content. Use it as a starting point to create something more
unique.</p>
<p><a href="#" class="btn btn-primary btn-lg" role="button">Learn more &raquo;</a></p>
</div>
<div class="page-header">
<h1>Buttons</h1>
</div>
<p>
<button type="button" class="btn btn-lg btn-default">Default</button>
<button type="button" class="btn btn-lg btn-primary">Primary</button>
<button type="button" class="btn btn-lg btn-success">Success</button>
<button type="button" class="btn btn-lg btn-info">Info</button>
<button type="button" class="btn btn-lg btn-warning">Warning</button>
<button type="button" class="btn btn-lg btn-danger">Danger</button>
<button type="button" class="btn btn-lg btn-link">Link</button>
</p>
<p>
<button type="button" class="btn btn-default">Default</button>
<button type="button" class="btn btn-primary">Primary</button>
<button type="button" class="btn btn-success">Success</button>
<button type="button" class="btn btn-info">Info</button>
<button type="button" class="btn btn-warning">Warning</button>
<button type="button" class="btn btn-danger">Danger</button>
<button type="button" class="btn btn-link">Link</button>
</p>
<p>
<button type="button" class="btn btn-sm btn-default">Default</button>
<button type="button" class="btn btn-sm btn-primary">Primary</button>
<button type="button" class="btn btn-sm btn-success">Success</button>
<button type="button" class="btn btn-sm btn-info">Info</button>
<button type="button" class="btn btn-sm btn-warning">Warning</button>
<button type="button" class="btn btn-sm btn-danger">Danger</button>
<button type="button" class="btn btn-sm btn-link">Link</button>
</p>
<p>
<button type="button" class="btn btn-xs btn-default">Default</button>
<button type="button" class="btn btn-xs btn-primary">Primary</button>
<button type="button" class="btn btn-xs btn-success">Success</button>
<button type="button" class="btn btn-xs btn-info">Info</button>
<button type="button" class="btn btn-xs btn-warning">Warning</button>
<button type="button" class="btn btn-xs btn-danger">Danger</button>
<button type="button" class="btn btn-xs btn-link">Link</button>
</p>
<div class="page-header">
<h1>Tables</h1>
</div>
<div class="row">
<div class="col-md-6">
<table class="table">
<thead>
<tr>
<th>#</th>
<th>First Name</th>
<th>Last Name</th>
<th>Username</th>
</tr>
</thead>
<tbody>
<tr>
<td>1</td>
<td>Mark</td>
<td>Otto</td>
<td>@mdo</td>
</tr>
<tr>
<td>2</td>
<td>Jacob</td>
<td>Thornton</td>
<td>@fat</td>
</tr>
<tr>
<td>3</td>
<td>Larry</td>
<td>the Bird</td>
<td>@twitter</td>
</tr>
</tbody>
</table>
</div>
<div class="col-md-6">
<table class="table table-striped">
<thead>
<tr>
<th>#</th>
<th>First Name</th>
<th>Last Name</th>
<th>Username</th>
</tr>
</thead>
<tbody>
<tr>
<td>1</td>
<td>Mark</td>
<td>Otto</td>
<td>@mdo</td>
</tr>
<tr>
<td>2</td>
<td>Jacob</td>
<td>Thornton</td>
<td>@fat</td>
</tr>
<tr>
<td>3</td>
<td>Larry</td>
<td>the Bird</td>
<td>@twitter</td>
</tr>
</tbody>
</table>
</div>
</div>
<div class="row">
<div class="col-md-6">
<table class="table table-bordered">
<thead>
<tr>
<th>#</th>
<th>First Name</th>
<th>Last Name</th>
<th>Username</th>
</tr>
</thead>
<tbody>
<tr>
<td rowspan="2">1</td>
<td>Mark</td>
<td>Otto</td>
<td>@mdo</td>
</tr>
<tr>
<td>Mark</td>
<td>Otto</td>
<td>@TwBootstrap</td>
</tr>
<tr>
<td>2</td>
<td>Jacob</td>
<td>Thornton</td>
<td>@fat</td>
</tr>
<tr>
<td>3</td>
<td colspan="2">Larry the Bird</td>
<td>@twitter</td>
</tr>
</tbody>
</table>
</div>
<div class="col-md-6">
<table class="table table-condensed">
<thead>
<tr>
<th>#</th>
<th>First Name</th>
<th>Last Name</th>
<th>Username</th>
</tr>
</thead>
<tbody>
<tr>
<td>1</td>
<td>Mark</td>
<td>Otto</td>
<td>@mdo</td>
</tr>
<tr>
<td>2</td>
<td>Jacob</td>
<td>Thornton</td>
<td>@fat</td>
</tr>
<tr>
<td>3</td>
<td colspan="2">Larry the Bird</td>
<td>@twitter</td>
</tr>
</tbody>
</table>
</div>
</div>
<div class="page-header">
<h1>Thumbnails</h1>
</div>
<img data-src="holder.js/200x200" class="img-thumbnail"
alt="A generic square placeholder image with a white border around it, making it resemble a photograph taken with an old instant camera">
<div class="page-header">
<h1>Labels</h1>
</div>
<h1>
<span class="label label-default">Default</span>
<span class="label label-primary">Primary</span>
<span class="label label-success">Success</span>
<span class="label label-info">Info</span>
<span class="label label-warning">Warning</span>
<span class="label label-danger">Danger</span>
</h1>
<h2>
<span class="label label-default">Default</span>
<span class="label label-primary">Primary</span>
<span class="label label-success">Success</span>
<span class="label label-info">Info</span>
<span class="label label-warning">Warning</span>
<span class="label label-danger">Danger</span>
</h2>
<h3>
<span class="label label-default">Default</span>
<span class="label label-primary">Primary</span>
<span class="label label-success">Success</span>
<span class="label label-info">Info</span>
<span class="label label-warning">Warning</span>
<span class="label label-danger">Danger</span>
</h3>
<h4>
<span class="label label-default">Default</span>
<span class="label label-primary">Primary</span>
<span class="label label-success">Success</span>
<span class="label label-info">Info</span>
<span class="label label-warning">Warning</span>
<span class="label label-danger">Danger</span>
</h4>
<h5>
<span class="label label-default">Default</span>
<span class="label label-primary">Primary</span>
<span class="label label-success">Success</span>
<span class="label label-info">Info</span>
<span class="label label-warning">Warning</span>
<span class="label label-danger">Danger</span>
</h5>
<h6>
<span class="label label-default">Default</span>
<span class="label label-primary">Primary</span>
<span class="label label-success">Success</span>
<span class="label label-info">Info</span>
<span class="label label-warning">Warning</span>
<span class="label label-danger">Danger</span>
</h6>
<p>
<span class="label label-default">Default</span>
<span class="label label-primary">Primary</span>
<span class="label label-success">Success</span>
<span class="label label-info">Info</span>
<span class="label label-warning">Warning</span>
<span class="label label-danger">Danger</span>
</p>
<div class="page-header">
<h1>Badges</h1>
</div>
<p>
<a href="#">Inbox <span class="badge">42</span></a>
</p>
<ul class="nav nav-pills">
<li class="active"><a href="#">Home <span class="badge">42</span></a></li>
<li><a href="#">Profile</a></li>
<li><a href="#">Messages <span class="badge">3</span></a></li>
</ul>
<div class="page-header">
<h1>Dropdown menus</h1>
</div>
<div class="dropdown theme-dropdown clearfix">
<a id="dropdownMenu1" href="#" role="button" class="sr-only dropdown-toggle" data-toggle="dropdown">Dropdown <span
class="caret"></span></a>
<ul class="dropdown-menu" role="menu" aria-labelledby="dropdownMenu1">
<li class="active" role="presentation"><a role="menuitem" tabindex="-1" href="#">Action</a></li>
<li role="presentation"><a role="menuitem" tabindex="-1" href="#">Another action</a></li>
<li role="presentation"><a role="menuitem" tabindex="-1" href="#">Something else here</a></li>
<li role="presentation" class="divider"></li>
<li role="presentation"><a role="menuitem" tabindex="-1" href="#">Separated link</a></li>
</ul>
</div>
<div class="page-header">
<h1>Navs</h1>
</div>
<ul class="nav nav-tabs" role="tablist">
<li class="active"><a href="#">Home</a></li>
<li><a href="#">Profile</a></li>
<li><a href="#">Messages</a></li>
</ul>
<ul class="nav nav-pills">
<li class="active"><a href="#">Home</a></li>
<li><a href="#">Profile</a></li>
<li><a href="#">Messages</a></li>
</ul>
<div class="page-header">
<h1>Navbars</h1>
</div>
<div class="navbar navbar-default">
<div class="container">
<div class="navbar-header">
<button type="button" class="navbar-toggle collapsed" data-toggle="collapse" data-target=".navbar-collapse">
<span class="sr-only">Toggle navigation</span>
<span class="icon-bar"></span>
<span class="icon-bar"></span>
<span class="icon-bar"></span>
</button>
<a class="navbar-brand" href="#">Project name</a>
</div>
<div class="navbar-collapse collapse">
<ul class="nav navbar-nav">
<li class="active"><a href="#">Home</a></li>
<li><a href="#about">About</a></li>
<li><a href="#contact">Contact</a></li>
<li class="dropdown">
<a href="#" class="dropdown-toggle" data-toggle="dropdown">Dropdown <span class="caret"></span></a>
<ul class="dropdown-menu" role="menu">
<li><a href="#">Action</a></li>
<li><a href="#">Another action</a></li>
<li><a href="#">Something else here</a></li>
<li class="divider"></li>
<li class="dropdown-header">Nav header</li>
<li><a href="#">Separated link</a></li>
<li><a href="#">One more separated link</a></li>
</ul>
</li>
</ul>
</div>
<!--/.nav-collapse -->
</div>
</div>
<div class="navbar navbar-inverse">
<div class="container">
<div class="navbar-header">
<button type="button" class="navbar-toggle collapsed" data-toggle="collapse" data-target=".navbar-collapse">
<span class="sr-only">Toggle navigation</span>
<span class="icon-bar"></span>
<span class="icon-bar"></span>
<span class="icon-bar"></span>
</button>
<a class="navbar-brand" href="#">Project name</a>
</div>
<div class="navbar-collapse collapse">
<ul class="nav navbar-nav">
<li class="active"><a href="#">Home</a></li>
<li><a href="#about">About</a></li>
<li><a href="#contact">Contact</a></li>
<li class="dropdown">
<a href="#" class="dropdown-toggle" data-toggle="dropdown">Dropdown <span class="caret"></span></a>
<ul class="dropdown-menu" role="menu">
<li><a href="#">Action</a></li>
<li><a href="#">Another action</a></li>
<li><a href="#">Something else here</a></li>
<li class="divider"></li>
<li class="dropdown-header">Nav header</li>
<li><a href="#">Separated link</a></li>
<li><a href="#">One more separated link</a></li>
</ul>
</li>
</ul>
</div>
<!--/.nav-collapse -->
</div>
</div>
<div class="page-header">
<h1>Alerts</h1>
</div>
<div class="alert alert-success" role="alert">
<strong>Well done!</strong> You successfully read this important alert message.
</div>
<div class="alert alert-info" role="alert">
<strong>Heads up!</strong> This alert needs your attention, but it's not super important.
</div>
<div class="alert alert-warning" role="alert">
<strong>Warning!</strong> Best check yo self, you're not looking too good.
</div>
<div class="alert alert-danger" role="alert">
<strong>Oh snap!</strong> Change a few things up and try submitting again.
</div>
<div class="page-header">
<h1>Progress bars</h1>
</div>
<div class="progress">
<div class="progress-bar" role="progressbar" aria-valuenow="60" aria-valuemin="0" aria-valuemax="100"
style="width: 60%;"><span class="sr-only">60% Complete</span></div>
</div>
<div class="progress">
<div class="progress-bar progress-bar-success" role="progressbar" aria-valuenow="40" aria-valuemin="0"
aria-valuemax="100" style="width: 40%"><span class="sr-only">40% Complete (success)</span></div>
</div>
<div class="progress">
<div class="progress-bar progress-bar-info" role="progressbar" aria-valuenow="20" aria-valuemin="0"
aria-valuemax="100" style="width: 20%"><span class="sr-only">20% Complete</span></div>
</div>
<div class="progress">
<div class="progress-bar progress-bar-warning" role="progressbar" aria-valuenow="60" aria-valuemin="0"
aria-valuemax="100" style="width: 60%"><span class="sr-only">60% Complete (warning)</span></div>
</div>
<div class="progress">
<div class="progress-bar progress-bar-danger" role="progressbar" aria-valuenow="80" aria-valuemin="0"
aria-valuemax="100" style="width: 80%"><span class="sr-only">80% Complete (danger)</span></div>
</div>
<div class="progress">
<div class="progress-bar progress-bar-striped" role="progressbar" aria-valuenow="60" aria-valuemin="0"
aria-valuemax="100" style="width: 60%"><span class="sr-only">100% Complete</span></div>
</div>
<div class="progress">
<div class="progress-bar progress-bar-success" style="width: 35%"><span
class="sr-only">35% Complete (success)</span></div>
<div class="progress-bar progress-bar-warning" style="width: 20%"><span
class="sr-only">20% Complete (warning)</span></div>
<div class="progress-bar progress-bar-danger" style="width: 10%"><span class='sr-only'>10% Complete (danger)</span>
</div>
</div>
<div class="page-header">
<h1>List groups</h1>
</div>
<div class="row">
<div class="col-sm-4">
<ul class="list-group">
<li class="list-group-item">Cras justo odio</li>
<li class="list-group-item">Dapibus ac facilisis in</li>
<li class="list-group-item">Morbi leo risus</li>
<li class="list-group-item">Porta ac consectetur ac</li>
<li class="list-group-item">Vestibulum at eros</li>
</ul>
</div>
<!-- /.col-sm-4 -->
<div class="col-sm-4">
<div class="list-group">
<a href="#" class="list-group-item active">
Cras justo odio
</a>
<a href="#" class="list-group-item">Dapibus ac facilisis in</a>
<a href="#" class="list-group-item">Morbi leo risus</a>
<a href="#" class="list-group-item">Porta ac consectetur ac</a>
<a href="#" class="list-group-item">Vestibulum at eros</a>
</div>
</div>
<!-- /.col-sm-4 -->
<div class="col-sm-4">
<div class="list-group">
<a href="#" class="list-group-item active">
<h4 class="list-group-item-heading">List group item heading</h4>
<p class="list-group-item-text">Donec id elit non mi porta gravida at eget metus. Maecenas sed diam eget
risus varius blandit.</p>
</a>
<a href="#" class="list-group-item">
<h4 class="list-group-item-heading">List group item heading</h4>
<p class="list-group-item-text">Donec id elit non mi porta gravida at eget metus. Maecenas sed diam eget
risus varius blandit.</p>
</a>
<a href="#" class="list-group-item">
<h4 class="list-group-item-heading">List group item heading</h4>
<p class="list-group-item-text">Donec id elit non mi porta gravida at eget metus. Maecenas sed diam eget
risus varius blandit.</p>
</a>
</div>
</div>
<!-- /.col-sm-4 -->
</div>
<div class="page-header">
<h1>Panels</h1>
</div>
<div class="row">
<div class="col-sm-4">
<div class="panel panel-default">
<div class="panel-heading">
<h3 class="panel-title">Panel title</h3>
</div>
<div class="panel-body">
Panel content
</div>
</div>
<div class="panel panel-primary">
<div class="panel-heading">
<h3 class="panel-title">Panel title</h3>
</div>
<div class="panel-body">
Panel content
</div>
</div>
</div>
<!-- /.col-sm-4 -->
<div class="col-sm-4">
<div class="panel panel-success">
<div class="panel-heading">
<h3 class="panel-title">Panel title</h3>
</div>
<div class="panel-body">
Panel content
</div>
</div>
<div class="panel panel-info">
<div class="panel-heading">
<h3 class="panel-title">Panel title</h3>
</div>
<div class="panel-body">
Panel content
</div>
</div>
</div>
<!-- /.col-sm-4 -->
<div class="col-sm-4">
<div class="panel panel-warning">
<div class="panel-heading">
<h3 class="panel-title">Panel title</h3>
</div>
<div class="panel-body">
Panel content
</div>
</div>
<div class="panel panel-danger">
<div class="panel-heading">
<h3 class="panel-title">Panel title</h3>
</div>
<div class="panel-body">
Panel content
</div>
</div>
</div>
<!-- /.col-sm-4 -->
</div>
<div class="page-header">
<h1>Wells</h1>
</div>
<div class="well">
<p>Lorem ipsum dolor sit amet, consectetur adipiscing elit. Maecenas sed diam eget risus varius blandit sit amet non
magna. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Praesent commodo cursus magna, vel scelerisque
nisl consectetur et. Cras mattis consectetur purus sit amet fermentum. Duis mollis, est non commodo luctus, nisi
erat porttitor ligula, eget lacinia odio sem nec elit. Aenean lacinia bibendum nulla sed consectetur.</p>
</div>
</div>
<!-- /container -->
<!-- Bootstrap core JavaScript
================================================== -->
<!-- Placed at the end of the document so the pages load faster -->
<script src="vendor/jquery/dist/jquery.min.js"></script>
<script src="vendor/bootstrap/dist/js/bootstrap.min.js"></script>
<script src="js/bootstrap-hadoop.min.js"></script>
</body>
</html>

View File

@ -0,0 +1,104 @@
/*
* Licensed to the Apache Software Foundation (ASF) under one or more
* contributor license agreements. See the NOTICE file distributed with
* this work for additional information regarding copyright ownership.
* The ASF licenses this file to You 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 org.apache.hadoop.yarn.appcatalog.application;
import org.apache.commons.io.FileUtils;
import org.apache.solr.client.solrj.SolrClient;
import org.apache.solr.client.solrj.SolrServerException;
import org.apache.solr.client.solrj.embedded.EmbeddedSolrServer;
import org.apache.solr.client.solrj.request.CoreAdminRequest;
import org.apache.solr.core.NodeConfig;
import org.apache.solr.core.SolrResourceLoader;
import java.io.File;
import java.io.IOException;
import java.nio.file.Path;
import java.nio.file.Paths;
/**
* Embedded solr server factory class for unit tests.
*/
public final class EmbeddedSolrServerFactory {
private EmbeddedSolrServerFactory() {
}
/**
* Cleans the given solrHome directory and creates a new EmbeddedSolrServer.
*
* @param solrHome
* the Solr home directory to use
* @param configSetHome
* the directory containing config sets
* @param coreName
* the name of the core, must have a matching directory in configHome
*
* @return an EmbeddedSolrServer with a core created for the given coreName
* @throws IOException
*/
public static SolrClient create(final String solrHome,
final String configSetHome, final String coreName)
throws IOException, SolrServerException {
return create(solrHome, configSetHome, coreName, true);
}
/**
* @param solrHome
* the Solr home directory to use
* @param configSetHome
* the directory containing config sets
* @param coreName
* the name of the core, must have a matching directory in configHome
* @param cleanSolrHome
* if true the directory for solrHome will be deleted and re-created
* if it already exists
*
* @return an EmbeddedSolrServer with a core created for the given coreName
* @throws IOException
*/
public static SolrClient create(final String solrHome,
final String configSetHome, final String coreName,
final boolean cleanSolrHome) throws IOException, SolrServerException {
final File solrHomeDir = new File(solrHome);
if (solrHomeDir.exists()) {
FileUtils.deleteDirectory(solrHomeDir);
solrHomeDir.mkdirs();
} else {
solrHomeDir.mkdirs();
}
final SolrResourceLoader loader = new SolrResourceLoader(
solrHomeDir.toPath());
final Path configSetPath = Paths.get(configSetHome).toAbsolutePath();
final NodeConfig config = new NodeConfig.NodeConfigBuilder(
"embeddedSolrServerNode", loader)
.setConfigSetBaseDirectory(configSetPath.toString()).build();
final EmbeddedSolrServer embeddedSolrServer = new EmbeddedSolrServer(config,
coreName);
final CoreAdminRequest.Create createRequest = new CoreAdminRequest.Create();
createRequest.setCoreName(coreName);
createRequest.setConfigSet(coreName);
embeddedSolrServer.request(createRequest);
return embeddedSolrServer;
}
}

View File

@ -0,0 +1,112 @@
/*
* Licensed to the Apache Software Foundation (ASF) under one
* or more contributor license agreements. See the NOTICE file
* distributed with this work for additional information
* regarding copyright ownership. The ASF licenses this file
* to you 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 org.apache.hadoop.yarn.appcatalog.application;
import org.apache.hadoop.yarn.appcatalog.model.AppStoreEntry;
import org.apache.hadoop.yarn.appcatalog.model.Application;
import org.apache.solr.client.solrj.SolrClient;
import org.junit.After;
import org.junit.Before;
import org.junit.Test;
import org.powermock.api.mockito.PowerMockito;
import static org.powermock.api.mockito.PowerMockito.when;
import static org.powermock.api.support.membermodification.MemberMatcher.method;
import static org.junit.Assert.*;
import java.util.List;
/**
* Unit test for AppCatalogSolrClient.
*/
public class TestAppCatalogSolrClient {
static final String CONFIGSET_DIR = "src/test/resources/configsets";
private static SolrClient solrClient;
private static AppCatalogSolrClient spy;
@Before
public void setup() throws Exception {
String targetLocation = EmbeddedSolrServerFactory.class
.getProtectionDomain().getCodeSource().getLocation().getFile() + "/..";
String solrHome = targetLocation + "/solr";
solrClient = EmbeddedSolrServerFactory.create(solrHome, CONFIGSET_DIR,
"exampleCollection");
spy = PowerMockito.spy(new AppCatalogSolrClient());
when(spy, method(AppCatalogSolrClient.class, "getSolrClient"))
.withNoArguments().thenReturn(solrClient);
}
@After
public void teardown() throws Exception {
try {
solrClient.close();
} catch (Exception e) {
}
}
@Test
public void testRegister() throws Exception {
Application example = new Application();
example.setOrganization("jenkins-ci.org");
example.setName("jenkins");
example.setDescription("World leading open source automation system.");
example.setIcon("/css/img/feather.png");
spy.register(example);
List<AppStoreEntry> apps = spy.getRecommendedApps();
assertEquals(1, apps.size());
}
@Test
public void testSearch() throws Exception {
Application example = new Application();
example.setOrganization("jenkins-ci.org");
example.setName("jenkins");
example.setDescription("World leading open source automation system.");
example.setIcon("/css/img/feather.png");
spy.register(example);
List<AppStoreEntry> results = spy.search("name_s:jenkins");
int expected = 1;
int actual = results.size();
assertEquals(expected, actual);
}
@Test
public void testNotFoundSearch() throws Exception {
Application example = new Application();
example.setOrganization("jenkins-ci.org");
example.setName("jenkins");
example.setDescription("World leading open source automation system.");
example.setIcon("/css/img/feather.png");
spy.register(example);
List<AppStoreEntry> results = spy.search("name_s:abc");
int expected = 0;
int actual = results.size();
assertEquals(expected, actual);
}
@Test
public void testGetRecommendedApps() throws Exception {
List<AppStoreEntry> expected = spy.getRecommendedApps();
List<AppStoreEntry> actual = spy.getRecommendedApps();
assertEquals(expected, actual);
}
}

View File

@ -0,0 +1,138 @@
/*
* Licensed to the Apache Software Foundation (ASF) under one
* or more contributor license agreements. See the NOTICE file
* distributed with this work for additional information
* regarding copyright ownership. The ASF licenses this file
* to you 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 org.apache.hadoop.yarn.appcatalog.controller;
import org.apache.hadoop.yarn.service.api.records.Service;
import org.apache.hadoop.yarn.appcatalog.model.AppEntry;
import org.apache.hadoop.yarn.service.api.records.Component;
import org.apache.hadoop.yarn.service.api.records.Container;
import org.junit.Before;
import org.junit.Test;
import org.mockito.Mockito;
import javax.ws.rs.Path;
import javax.ws.rs.core.Response;
import static org.hamcrest.MatcherAssert.assertThat;
import static org.hamcrest.core.Is.is;
import static org.junit.Assert.*;
import static org.mockito.Mockito.*;
import java.util.ArrayList;
import java.util.List;
/**
* Unit test for AppDetailsController.
*/
public class AppDetailsControllerTest {
private AppDetailsController controller;
@Before
public void setUp() throws Exception {
this.controller = new AppDetailsController();
}
@Test
public void testGetDetails() throws Exception {
String id = "application 1";
AppDetailsController ac = Mockito.mock(AppDetailsController.class);
AppEntry actual = new AppEntry();
actual.setName(id);
when(ac.getDetails(id)).thenReturn(actual);
final AppEntry result = ac.getDetails(id);
assertEquals(result, actual);
}
@Test
public void testGetStatus() throws Exception {
String id = "application 1";
AppDetailsController ac = Mockito.mock(AppDetailsController.class);
Service yarnfile = new Service();
Component comp = new Component();
Container c = new Container();
c.setId("container-1");
List<Container> containers = new ArrayList<Container>();
containers.add(c);
comp.setContainers(containers);
yarnfile.addComponent(comp);
AppEntry actual = new AppEntry();
actual.setName(id);
actual.setYarnfile(yarnfile);
when(ac.getStatus(id)).thenReturn(actual);
final AppEntry result = ac.getStatus(id);
assertEquals(result, actual);
}
@Test
public void testStopApp() throws Exception {
String id = "application 1";
AppDetailsController ac = Mockito.mock(AppDetailsController.class);
Service yarnfile = new Service();
Component comp = new Component();
Container c = new Container();
c.setId("container-1");
List<Container> containers = new ArrayList<Container>();
containers.add(c);
comp.setContainers(containers);
yarnfile.addComponent(comp);
Response expected = Response.ok().build();
when(ac.stopApp(id)).thenReturn(Response.ok().build());
final Response actual = ac.stopApp(id);
assertEquals(expected.getStatus(), actual.getStatus());
}
@Test
public void testRestartApp() throws Exception {
String id = "application 1";
AppDetailsController ac = Mockito.mock(AppDetailsController.class);
Service yarnfile = new Service();
Component comp = new Component();
Container c = new Container();
c.setId("container-1");
List<Container> containers = new ArrayList<Container>();
containers.add(c);
comp.setContainers(containers);
yarnfile.addComponent(comp);
Response expected = Response.ok().build();
when(ac.restartApp(id)).thenReturn(Response.ok().build());
final Response actual = ac.restartApp(id);
assertEquals(expected.getStatus(), actual.getStatus());
}
@Test
public void testPathAnnotation() throws Exception {
assertNotNull(this.controller.getClass()
.getAnnotations());
assertThat("The controller has the annotation Path",
this.controller.getClass()
.isAnnotationPresent(Path.class));
final Path path = this.controller.getClass()
.getAnnotation(Path.class);
assertThat("The path is /app_details", path.value(),
is("/app_details"));
}
}

View File

@ -0,0 +1,95 @@
/*
* Licensed to the Apache Software Foundation (ASF) under one
* or more contributor license agreements. See the NOTICE file
* distributed with this work for additional information
* regarding copyright ownership. The ASF licenses this file
* to you 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 org.apache.hadoop.yarn.appcatalog.controller;
import org.apache.hadoop.yarn.appcatalog.model.AppEntry;
import org.apache.hadoop.yarn.service.api.records.Service;
import org.junit.Before;
import org.junit.Test;
import org.mockito.Mockito;
import javax.ws.rs.Path;
import javax.ws.rs.core.Response;
import static org.hamcrest.MatcherAssert.assertThat;
import static org.hamcrest.core.Is.is;
import static org.junit.Assert.*;
import static org.mockito.Mockito.*;
import java.util.ArrayList;
import java.util.List;
/**
* Unit test for AppListController.
*/
public class AppListControllerTest {
private AppListController controller;
@Before
public void setUp() throws Exception {
this.controller = new AppListController();
}
@Test
public void testGetList() throws Exception {
AppListController ac = Mockito.mock(AppListController.class);
List<AppEntry> actual = new ArrayList<AppEntry>();
when(ac.getList()).thenReturn(actual);
final List<AppEntry> result = ac.getList();
assertEquals(result, actual);
}
@Test
public void testDelete() throws Exception {
String id = "application 1";
AppListController ac = Mockito.mock(AppListController.class);
Response expected = Response.ok().build();
when(ac.delete(id, id)).thenReturn(Response.ok().build());
final Response actual = ac.delete(id, id);
assertEquals(expected.getStatus(), actual.getStatus());
}
@Test
public void testDeploy() throws Exception {
String id = "application 1";
AppListController ac = Mockito.mock(AppListController.class);
Service service = new Service();
Response expected = Response.ok().build();
when(ac.deploy(id, service)).thenReturn(Response.ok().build());
final Response actual = ac.deploy(id, service);
assertEquals(expected.getStatus(), actual.getStatus());
}
@Test
public void testPathAnnotation() throws Exception {
assertNotNull(this.controller.getClass()
.getAnnotations());
assertThat("The controller has the annotation Path",
this.controller.getClass().isAnnotationPresent(Path.class));
final Path path = this.controller.getClass()
.getAnnotation(Path.class);
assertThat("The path is /app_list", path.value(), is("/app_list"));
}
}

View File

@ -0,0 +1,97 @@
/*
* Licensed to the Apache Software Foundation (ASF) under one
* or more contributor license agreements. See the NOTICE file
* distributed with this work for additional information
* regarding copyright ownership. The ASF licenses this file
* to you 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 org.apache.hadoop.yarn.appcatalog.controller;
import org.apache.hadoop.yarn.appcatalog.model.AppStoreEntry;
import org.apache.hadoop.yarn.appcatalog.model.Application;
import org.junit.Before;
import org.junit.Test;
import org.mockito.Mockito;
import javax.ws.rs.Path;
import javax.ws.rs.core.Response;
import static org.hamcrest.MatcherAssert.assertThat;
import static org.hamcrest.core.Is.is;
import static org.junit.Assert.*;
import static org.mockito.Mockito.*;
import java.util.ArrayList;
import java.util.List;
/**
* Unit tests for AppStoreController.
*/
public class AppStoreControllerTest {
private AppStoreController controller;
@Before
public void setUp() throws Exception {
this.controller = new AppStoreController();
}
@Test
public void testGetRecommended() throws Exception {
AppStoreController ac = Mockito.mock(AppStoreController.class);
List<AppStoreEntry> actual = new ArrayList<AppStoreEntry>();
when(ac.get()).thenReturn(actual);
final List<AppStoreEntry> result = ac.get();
assertEquals(result, actual);
}
@Test
public void testSearch() throws Exception {
String keyword = "jenkins";
AppStoreController ac = Mockito.mock(AppStoreController.class);
List<AppStoreEntry> expected = new ArrayList<AppStoreEntry>();
when(ac.search(keyword)).thenReturn(expected);
final List<AppStoreEntry> actual = ac.search(keyword);
assertEquals(expected, actual);
}
@Test
public void testRegister() throws Exception {
AppStoreController ac = Mockito.mock(AppStoreController.class);
Application app = new Application();
app.setName("jenkins");
app.setOrganization("jenkins.org");
app.setDescription("This is a description");
app.setIcon("/css/img/feather.png");
Response expected = Response.ok().build();
when(ac.register(app)).thenReturn(Response.ok().build());
final Response actual = ac.register(app);
assertEquals(expected.getStatus(), actual.getStatus());
}
@Test
public void testPathAnnotation() throws Exception {
assertNotNull(this.controller.getClass()
.getAnnotations());
assertThat("The controller has the annotation Path",
this.controller.getClass()
.isAnnotationPresent(Path.class));
final Path path = this.controller.getClass()
.getAnnotation(Path.class);
assertThat("The path is /app_store", path.value(), is("/app_store"));
}
}

View File

@ -0,0 +1,249 @@
/*
* Licensed to the Apache Software Foundation (ASF) under one
* or more contributor license agreements. See the NOTICE file
* distributed with this work for additional information
* regarding copyright ownership. The ASF licenses this file
* to you 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.
*/
describe('Controller tests', function () {
// Unit test for listing, and start/stop applications.
describe('AppListController', function() {
var scope, ctrl, http, httpBackend;
beforeEach(module('app'));
beforeEach(inject(function ($controller, $rootScope, $http, $httpBackend) {
scope = $rootScope.$new();
rootScope = $rootScope;
http = $http;
httpBackend = $httpBackend;
ctrl = $controller('AppListController', {$scope: scope});
}));
afterEach(function() {
httpBackend.verifyNoOutstandingExpectation();
httpBackend.verifyNoOutstandingRequest();
});
it('should contain appList', function () {
httpBackend.expectGET('/v1/app_list').respond(200, [{id:"jenkins",name:"jenkins",app:"",yarnfile:{}}]);
httpBackend.expectGET('partials/home.html').respond(200, "");
httpBackend.flush();
expect(scope.appList.length).toBe(1);
});
it('should test to delete app', function () {
httpBackend.expectGET('/v1/app_list').respond(200, [{id:"jenkins",name:"jenkins",app:"",yarnfile:{}}]);
httpBackend.expectDELETE('/v1/app_list/jenkins/jenkins').respond(200, {data:"Application Deleted."});
httpBackend.expectGET('partials/home.html').respond(200, "");
spyOn(rootScope, '$emit');
scope.$apply(function() {
scope.deleteApp("jenkins","jenkins");
});
httpBackend.flush();
expect(rootScope.$emit).toHaveBeenCalledWith('RefreshAppList', {});
});
it('should test to refresh appList', function() {
spyOn(rootScope, '$emit');
httpBackend.expectGET('/v1/app_list').respond(200, [{id:"jenkins",name:"jenkins",app:"",yarnfile:{}}]);
httpBackend.expectGET('/v1/app_list').respond(200, [{id:"jenkins",name:"jenkins",app:"",yarnfile:{}}]);
httpBackend.expectGET('partials/home.html').respond(200, "");
scope.$apply(function() {
scope.refreshList();
});
httpBackend.flush();
expect(rootScope.$emit).toHaveBeenCalledWith('hideLoadScreen', {});
})
});
// Unit test for inspect YARN application details.
describe('AppDetailsController', function() {
var scope, ctrl, http, routeParams, httpBackend;
beforeEach(module('app'));
beforeEach(inject(function ($controller, $rootScope, $http, $routeParams, $httpBackend) {
scope = $rootScope.$new();
rootScope = $rootScope;
http = $http;
routeParams = $routeParams;
httpBackend = $httpBackend;
ctrl = $controller('AppDetailsController', {$scope: scope});
}));
afterEach(function() {
httpBackend.verifyNoOutstandingExpectation();
httpBackend.verifyNoOutstandingRequest();
});
it('should contain unknown state', function () {
httpBackend.expectGET('/v1/app_details/config/undefined').respond(200, {"yarnfile":{"state":"UNKNOWN","components":[]}});
httpBackend.expectGET('partials/home.html').respond(200, "");
httpBackend.flush();
expect(scope.details.yarnfile.state).toBe("UNKNOWN");
});
it('should run test to refrshed details', function () {
httpBackend.expectGET('/v1/app_details/config/undefined').respond(200, {"yarnfile":{"state":"UNKNOWN","components":[]}});
httpBackend.expectGET('/v1/app_details/status/aabbccdd').respond(200, {yarnfile:{state: "ACCEPTED", components:[]}});
httpBackend.expectGET('partials/home.html').respond(200, "");
scope.$apply(function() {
routeParams.id = "aabbccdd";
scope.appName = "aabbccdd";
scope.refreshAppDetails();
});
httpBackend.flush();
expect(scope.details.yarnfile.state).toBe("ACCEPTED");
});
it('should run test to restart app', function () {
httpBackend.expectGET('/v1/app_details/config/undefined').respond(200, {"yarnfile":{"state":"UNKNOWN","components":[]}});
httpBackend.expectPOST('/v1/app_details/restart/aabbccdd').respond(200, {yarnfile:{state: "ACCEPTED", components:[]}});
httpBackend.expectGET('partials/home.html').respond(200, "");
httpBackend.expectGET('/v1/app_details/status/undefined').respond(200, {yarnfile:{state: "ACCEPTED", components:[]}});
scope.$apply(function() {
scope.restartApp("aabbccdd");
});
httpBackend.flush();
expect(scope.details.yarnfile.components).toBe();
});
it('should run test to stop app', function () {
httpBackend.expectGET('/v1/app_details/config/undefined').respond(200, {"yarnfile":{"state":"UNKNOWN","components":[]}});
httpBackend.expectPOST('/v1/app_details/stop/aabbccdd').respond(200, {yarnfile:{state: "STOPPED", components:[]}});
httpBackend.expectGET('partials/home.html').respond(200, "");
httpBackend.expectGET('/v1/app_details/status/undefined').respond(200, {yarnfile:{state: "ACCEPTED", components:[]}});
scope.$apply(function() {
scope.stopApp("aabbccdd");
});
httpBackend.flush();
expect(scope.details.yarnfile.components).toBe();
});
});
// Unit test for deploying app, and search for apps from Yarn Appstore.
describe('AppStoreController', function() {
var scope, ctrl, http, httpBackend;
beforeEach(module('app'));
beforeEach(inject(function ($controller, $rootScope, $http, $httpBackend) {
scope = $rootScope.$new();
http = $http;
httpBackend = $httpBackend;
ctrl = $controller('AppStoreController', {$scope: scope});
}));
afterEach(function() {
httpBackend.verifyNoOutstandingExpectation();
httpBackend.verifyNoOutstandingRequest();
});
it('should contain appStore', function () {
httpBackend.expectGET('/v1/app_store/recommended').respond(200, "");
httpBackend.expectGET('partials/home.html').respond(200, "");
httpBackend.flush();
expect(scope.appStore.length).toBe(0);
});
it('should run test to deploy app', function() {
httpBackend.expectGET('/v1/app_store/recommended').respond(200, "");
httpBackend.expectGET('partials/home.html').respond(200, "");
httpBackend.flush();
scope.$apply(function() {
scope.deployApp("aabbccdd");
});
expect(scope.appStore.length).toBe(0);
});
it('should run test to search for apps', function() {
httpBackend.expectGET('/v1/app_store/recommended').respond(200, "");
httpBackend.expectGET('/v1/app_store/search?q=aabbccdd').respond(204, {data:'ACCEPTED'});
httpBackend.expectGET('partials/home.html').respond(200, "");
scope.$apply(function() {
scope.searchText = "aabbccdd";
scope.change("aabbccdd");
});
httpBackend.flush();
expect(scope.appStore.data).toBe('ACCEPTED');
});
});
// Unit test cases for creating a new YARN application.
describe('NewAppController', function() {
var scope, ctrl, http, httpBackend;
beforeEach(module('app'));
beforeEach(inject(function ($controller, $rootScope, $http, $httpBackend) {
scope = $rootScope.$new();
http = $http;
httpBackend = $httpBackend;
ctrl = $controller('NewAppController', {$scope: scope});
}));
afterEach(function() {
httpBackend.verifyNoOutstandingExpectation();
httpBackend.verifyNoOutstandingRequest();
});
it('should contain details', function () {
httpBackend.expectGET('partials/home.html').respond(200, "");
httpBackend.flush();
expect(scope.details.name).toBe("");
});
it('should run test to register data to backend', function() {
httpBackend.expectPOST('/v1/app_store/register').respond(204, {data:'ACCEPTED'});
httpBackend.expectGET('partials/home.html').respond(200, "");
scope.$apply(function() {
scope.save();
});
httpBackend.flush();
expect(scope.message).toEqual("Application published successfully.");
});
it('should run test to fail register data to backend', function() {
httpBackend.expectPOST('/v1/app_store/register').respond(500, {data:'INTERNAL SERVER ERROR'});
httpBackend.expectGET('partials/home.html').respond(200, "");
scope.$apply(function() {
scope.save();
});
httpBackend.flush();
expect(scope.error).toEqual("Error in registering application configuration.");
});
it('should run test to add more component to details', function() {
httpBackend.expectGET('partials/home.html').respond(200, "");
expect(scope.details.components.length).toEqual(1);
scope.$apply(function() {
scope.add();
});
httpBackend.flush();
expect(scope.details.components.length).toEqual(2);
});
it('should run test to remove second component', function() {
httpBackend.expectGET('partials/home.html').respond(200, "");
expect(scope.details.components.length).toEqual(1);
scope.$apply(function() {
scope.add();
scope.remove(1);
});
httpBackend.flush();
expect(scope.details.components.length).toEqual(1);
});
});
});

View File

@ -0,0 +1,34 @@
module.exports = function(config){
config.set({
basePath : '../../../',
files : [
'target/generated-sources/vendor/angular**/**.min.js',
'target/generated-sources/vendor/angular-mocks/angular-mocks.js',
'src/main/javascript/**/*.js',
'src/test/javascript/**/*Spec.js',
'src/test/javascript/**/!(karma.conf).js'
],
autoWatch : true,
frameworks: ['jasmine'],
browsers: ['PhantomJS'],
plugins : [
'karma-chrome-launcher',
'karma-firefox-launcher',
'karma-phantomjs-launcher',
'karma-jasmine',
'karma-junit-reporter'
],
junitReporter : {
outputFile: 'target/test_out/unit.xml',
suite: 'src/test/javascript'
}
});
};

View File

@ -0,0 +1,54 @@
# Licensed to the Apache Software Foundation (ASF) under one or more
# contributor license agreements. See the NOTICE file distributed with
# this work for additional information regarding copyright ownership.
# The ASF licenses this file to You 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.
# a couple of test stopwords to test that the words are really being
# configured from this file:
stopworda
stopwordb
# Standard english stop words taken from Lucene's StopAnalyzer
a
an
and
are
as
at
be
but
by
for
if
in
into
is
it
no
not
of
on
or
such
that
the
their
then
there
these
they
this
to
was
will
with

View File

@ -0,0 +1,20 @@
{"params":{
"query":{
"defType":"edismax",
"q.alt":"*:*",
"rows":"10",
"fl":"*,score",
"":{"v":0}
},
"facets":{
"facet":"on",
"facet.mincount": "1",
"":{"v":0}
},
"velocity":{
"wt": "velocity",
"v.template":"browse",
"v.layout": "layout",
"":{"v":0}
}
}}

View File

@ -0,0 +1,20 @@
# The ASF licenses this file to You 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.
#-----------------------------------------------------------------------
# Use a protected word file to protect against the stemmer reducing two
# unrelated words to the same base word.
# Some non-words that normally won't be encountered,
# just to test that they won't be stemmed.
dontstems
zwhacky

View File

@ -0,0 +1,115 @@
<?xml version="1.0" encoding="UTF-8" ?>
<!--
Licensed to the Apache Software Foundation (ASF) under one or more
contributor license agreements. See the NOTICE file distributed with
this work for additional information regarding copyright ownership.
The ASF licenses this file to You 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.
-->
<schema name="exampleCollection" version="1.6">
<!-- Defined fields -->
<field name="id" type="string" indexed="true" stored="true" required="true" multiValued="false" docValues="true" />
<field name="type_s" type="string" indexed="true" stored="true" required="false" multiValued="false" docValues="true" />
<field name="org_s" type="string" indexed="true" stored="true" required="false" multiValued="false" docValues="true" />
<field name="name_s" type="string" indexed="true" stored="true" required="false" multiValued="false" docValues="true" />
<field name="desc_s" type="string" indexed="true" stored="true" required="false" multiValued="false" docValues="true" />
<field name="icon_s" type="string" indexed="true" stored="true" required="false" multiValued="false" docValues="true" />
<field name="yarnfile_s" type="string" indexed="true" stored="true" required="false" multiValued="false" docValues="true" />
<field name="like_i" type="int" indexed="true" stored="true" required="false" multiValued="false" docValues="true" />
<field name="download_i" type="int" indexed="true" stored="true" required="false" multiValued="false" docValues="true" />
<field name="_version_" type="long" indexed="true" stored="false"/>
<uniqueKey>id</uniqueKey>
<!-- Field types -->
<fieldType name="string" class="solr.StrField" sortMissingLast="true" docValues="true" />
<fieldType name="strings" class="solr.StrField" sortMissingLast="true" multiValued="true" docValues="true" />
<fieldType name="boolean" class="solr.BoolField" sortMissingLast="true"/>
<fieldType name="booleans" class="solr.BoolField" sortMissingLast="true" multiValued="true"/>
<fieldType name="int" class="solr.TrieIntField" docValues="true" precisionStep="0" positionIncrementGap="0"/>
<fieldType name="float" class="solr.TrieFloatField" docValues="true" precisionStep="0" positionIncrementGap="0"/>
<fieldType name="long" class="solr.TrieLongField" docValues="true" precisionStep="0" positionIncrementGap="0"/>
<fieldType name="double" class="solr.TrieDoubleField" docValues="true" precisionStep="0" positionIncrementGap="0"/>
<fieldType name="ints" class="solr.TrieIntField" docValues="true" precisionStep="0" positionIncrementGap="0" multiValued="true"/>
<fieldType name="floats" class="solr.TrieFloatField" docValues="true" precisionStep="0" positionIncrementGap="0" multiValued="true"/>
<fieldType name="longs" class="solr.TrieLongField" docValues="true" precisionStep="0" positionIncrementGap="0" multiValued="true"/>
<fieldType name="doubles" class="solr.TrieDoubleField" docValues="true" precisionStep="0" positionIncrementGap="0" multiValued="true"/>
<fieldType name="tint" class="solr.TrieIntField" docValues="true" precisionStep="8" positionIncrementGap="0"/>
<fieldType name="tfloat" class="solr.TrieFloatField" docValues="true" precisionStep="8" positionIncrementGap="0"/>
<fieldType name="tlong" class="solr.TrieLongField" docValues="true" precisionStep="8" positionIncrementGap="0"/>
<fieldType name="tdouble" class="solr.TrieDoubleField" docValues="true" precisionStep="8" positionIncrementGap="0"/>
<fieldType name="tints" class="solr.TrieIntField" docValues="true" precisionStep="8" positionIncrementGap="0" multiValued="true"/>
<fieldType name="tfloats" class="solr.TrieFloatField" docValues="true" precisionStep="8" positionIncrementGap="0" multiValued="true"/>
<fieldType name="tlongs" class="solr.TrieLongField" docValues="true" precisionStep="8" positionIncrementGap="0" multiValued="true"/>
<fieldType name="tdoubles" class="solr.TrieDoubleField" docValues="true" precisionStep="8" positionIncrementGap="0" multiValued="true"/>
<fieldType name="date" class="solr.TrieDateField" docValues="true" precisionStep="0" positionIncrementGap="0"/>
<fieldType name="dates" class="solr.TrieDateField" docValues="true" precisionStep="0" positionIncrementGap="0" multiValued="true"/>
<fieldType name="tdate" class="solr.TrieDateField" docValues="true" precisionStep="6" positionIncrementGap="0"/>
<fieldType name="tdates" class="solr.TrieDateField" docValues="true" precisionStep="6" positionIncrementGap="0" multiValued="true"/>
<fieldType name="binary" class="solr.BinaryField"/>
<fieldType name="random" class="solr.RandomSortField" indexed="true" />
<!-- A text field with defaults appropriate for English: it
tokenizes with StandardTokenizer, removes English stop words
(lang/stopwords_en.txt), down cases, protects words from protwords.txt, and
finally applies Porter's stemming. The query time analyzer
also applies synonyms from synonyms.txt. -->
<fieldType name="text_en" class="solr.TextField" positionIncrementGap="100">
<analyzer type="index">
<tokenizer class="solr.StandardTokenizerFactory"/>
<!-- in this example, we will only use synonyms at query time
<filter class="solr.SynonymFilterFactory" synonyms="index_synonyms.txt" ignoreCase="true" expand="false"/> -->
<!-- Case insensitive stop word removal. -->
<filter class="solr.StopFilterFactory"
ignoreCase="true"
words="lang/stopwords_en.txt"
/>
<filter class="solr.LowerCaseFilterFactory"/>
<filter class="solr.EnglishPossessiveFilterFactory"/>
<filter class="solr.KeywordMarkerFilterFactory" protected="protwords.txt"/>
<!-- Optionally you may want to use this less aggressive stemmer instead of PorterStemFilterFactory:
<filter class="solr.EnglishMinimalStemFilterFactory"/>
-->
<filter class="solr.PorterStemFilterFactory"/>
</analyzer>
<analyzer type="query">
<tokenizer class="solr.StandardTokenizerFactory"/>
<filter class="solr.SynonymFilterFactory" synonyms="synonyms.txt" ignoreCase="true" expand="true"/>
<filter class="solr.StopFilterFactory"
ignoreCase="true"
words="lang/stopwords_en.txt"
/>
<filter class="solr.LowerCaseFilterFactory"/>
<filter class="solr.EnglishPossessiveFilterFactory"/>
<filter class="solr.KeywordMarkerFilterFactory" protected="protwords.txt"/>
<!-- Optionally you may want to use this less aggressive stemmer instead of PorterStemFilterFactory:
<filter class="solr.EnglishMinimalStemFilterFactory"/>
-->
<filter class="solr.PorterStemFilterFactory"/>
</analyzer>
</fieldType>
<!-- lowercases the entire field value, keeping it as a single token. -->
<fieldType name="lowercase" class="solr.TextField" positionIncrementGap="100">
<analyzer>
<tokenizer class="solr.KeywordTokenizerFactory"/>
<filter class="solr.LowerCaseFilterFactory" />
</analyzer>
</fieldType>
<!-- since fields of this type are by default not stored or indexed,
any data added to them will be ignored outright. -->
<fieldType name="ignored" stored="false" indexed="false" docValues="false" multiValued="true" class="solr.StrField" />
</schema>

View File

@ -0,0 +1,36 @@
<?xml version="1.0" encoding="UTF-8" ?>
<!--
Licensed to the Apache Software Foundation (ASF) under one or more
contributor license agreements. See the NOTICE file distributed with
this work for additional information regarding copyright ownership.
The ASF licenses this file to You 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.
-->
<config>
<luceneMatchVersion>6.2.1</luceneMatchVersion>
<dataDir>${solr.data.dir:}</dataDir>
<schemaFactory class="ClassicIndexSchemaFactory"/>
<indexConfig>
<lockType>single</lockType>
</indexConfig>
<requestDispatcher handleSelect="false">
<httpCaching never304="true" />
</requestDispatcher>
<requestHandler name="/select" class="solr.SearchHandler" />
<requestHandler name="/update" class="solr.UpdateRequestHandler" />
</config>

View File

@ -0,0 +1,14 @@
# Licensed to the Apache Software Foundation (ASF) under one or more
# contributor license agreements. See the NOTICE file distributed with
# this work for additional information regarding copyright ownership.
# The ASF licenses this file to You 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.

View File

@ -0,0 +1,28 @@
# The ASF licenses this file to You 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.
#-----------------------------------------------------------------------
#some test synonym mappings unlikely to appear in real input text
aaafoo => aaabar
bbbfoo => bbbfoo bbbbar
cccfoo => cccbar cccbaz
fooaaa,baraaa,bazaaa
# Some synonym groups specific to this example
GB,gib,gigabyte,gigabytes
MB,mib,megabyte,megabytes
Television, Televisions, TV, TVs
#notice we use "gib" instead of "GiB" so any WordDelimiterFilter coming
#after us won't split it into two words.
# Synonym mappings can be used for spelling correction too
pixima => pixma

View File

@ -0,0 +1,11 @@
log4j.rootLogger = INFO, CATALINA
# Define all the appenders
log4j.appender.CATALINA = org.apache.log4j.DailyRollingFileAppender
log4j.appender.CATALINA.File = target/appcatalog.log
log4j.appender.CATALINA.Append = true
log4j.appender.CATALINA.Encoding = UTF-8
# Roll-over the log once per day
log4j.appender.CATALINA.DatePattern = '.'yyyy-MM-dd'.log'
log4j.appender.CATALINA.layout = org.apache.log4j.PatternLayout
log4j.appender.CATALINA.layout.ConversionPattern = %d [%t] %-5p %c- %m%n

View File

@ -0,0 +1,37 @@
<?xml version="1.0" encoding="UTF-8"?>
<!--
Licensed to the Apache Software Foundation (ASF) under one or more
contributor license agreements. See the NOTICE file distributed with
this work for additional information regarding copyright ownership.
The ASF licenses this file to You 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.
-->
<project xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns="http://maven.apache.org/POM/4.0.0"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/maven-v4_0_0.xsd">
<modelVersion>4.0.0</modelVersion>
<parent>
<artifactId>hadoop-yarn-applications</artifactId>
<groupId>org.apache.hadoop</groupId>
<version>3.3.0-SNAPSHOT</version>
</parent>
<groupId>org.apache.hadoop</groupId>
<artifactId>hadoop-yarn-applications-catalog</artifactId>
<packaging>pom</packaging>
<name>YARN Application Catalog</name>
<url>http://hadoop.apache.org</url>
<modules>
<module>hadoop-yarn-applications-catalog-webapp</module>
<module>hadoop-yarn-applications-catalog-docker</module>
</modules>
</project>

View File

@ -77,6 +77,13 @@ public class ApiServiceClient extends AppAdminClient {
private static final Base64 BASE_64_CODEC = new Base64(0);
protected YarnClient yarnClient;
public ApiServiceClient() {
}
public ApiServiceClient(Configuration c) throws Exception {
serviceInit(c);
}
@Override protected void serviceInit(Configuration configuration)
throws Exception {
yarnClient = YarnClient.createYarnClient();
@ -151,7 +158,7 @@ List<String> getRMHAWebAddresses(Configuration conf) {
* @return URI to API Service
* @throws IOException
*/
private String getServicePath(String appName) throws IOException {
public String getServicePath(String appName) throws IOException {
String url = getRMWebAddress();
StringBuilder api = new StringBuilder();
api.append(url)
@ -212,7 +219,7 @@ private void appendUserNameIfRequired(StringBuilder builder) {
}
}
private Builder getApiClient() throws IOException {
public Builder getApiClient() throws IOException {
return getApiClient(getServicePath(null));
}
@ -223,7 +230,7 @@ private Builder getApiClient() throws IOException {
* @return
* @throws IOException
*/
private Builder getApiClient(String requestPath)
public Builder getApiClient(String requestPath)
throws IOException {
Client client = Client.create(getClientConfig());
client.setChunkedEncodingSize(null);

View File

@ -0,0 +1,28 @@
{
"name": "appcatalog",
"version": "1",
"components" :
[
{
"name": "catalog",
"number_of_containers": 1,
"artifact": {
"id": "apache/hadoop-yarn-applications-catalog-docker:3.3.0-SNAPSHOT",
"type": "DOCKER"
},
"resource": {
"cpus": 1,
"memory": "2048"
},
"configuration": {
"env": {
"YARN_CONTAINER_RUNTIME_DOCKER_RUN_OVERRIDE_DISABLE":"true",
"YARN_CONTAINER_RUNTIME_DOCKER_MOUNTS":"/etc/hadoop/conf:/etc/hadoop/conf:ro,/var/lib/sss/pipes:/var/lib/sss/pipes:rw",
"JAVA_HOME":"/usr/lib/jvm/jre-1.8.0"
},
"properties": {
}
}
}
]
}

View File

@ -37,6 +37,7 @@
<module>hadoop-yarn-applications-distributedshell</module>
<module>hadoop-yarn-applications-unmanaged-am-launcher</module>
<module>hadoop-yarn-services</module>
<module>hadoop-yarn-applications-catalog</module>
</modules>
<profiles>

View File

@ -165,6 +165,19 @@ where `service-name` is optional. If omitted, it uses the name defined in the `Y
Look up your IPs at the RM REST endpoint `http://<RM host>:8088/app/v1/services/httpd-service`.
Then visit port 8080 for each IP to view the pages.
## Application Catalog - appcatalog
Application Catalog introduces many exciting new features for deploying Hadoop software that benefit both administrators and end users. With Application Catalog, user gets a personalized view of the software status in Hadoop. In addition, users can install or register applications by using web-based user interface.
To start Application Catalog service with the command:
```
yarn app -launch <service-name> appcatalog
```
where `service-name` is user defined name.
The deployment progress of the application catalog is located in Resource Manager UI. When the service reaches STABLE state, application catalog UI is available at:
http://appcatalog.${SERVICE_NAME}.${USER}.${DOMAIN}:8080/
## Docker image ENTRYPOINT support
Docker images may have built with ENTRYPOINT to enable start up of docker image without any parameters.