diff --git a/environments/jvm-jersey/.gitignore b/environments/jvm-jersey/.gitignore new file mode 100644 index 00000000..809cc8f2 --- /dev/null +++ b/environments/jvm-jersey/.gitignore @@ -0,0 +1,2 @@ +target/ +bin/ diff --git a/environments/jvm-jersey/Dockerfile b/environments/jvm-jersey/Dockerfile new file mode 100644 index 00000000..a4b04096 --- /dev/null +++ b/environments/jvm-jersey/Dockerfile @@ -0,0 +1,15 @@ +FROM maven:3.5-jdk-8 as BUILD +WORKDIR /usr/src/myapp/ + +# To reuse the build cache, here we split maven dependency +# download and package into two RUN commands to avoid cache invalidation. +COPY pom.xml . +RUN mvn dependency:go-offline + +COPY src /usr/src/myapp/src/ +RUN mvn package + +FROM openjdk:8-jre-alpine +COPY --from=BUILD /usr/src/myapp/target/env-jvm-jersey-0.0.1.jar /app.jar +ENTRYPOINT java ${JVM_OPTS} -Djava.security.egd=file:/dev/./urandom -jar app.jar 8888 +EXPOSE 8888 diff --git a/environments/jvm-jersey/Dockerfile-11 b/environments/jvm-jersey/Dockerfile-11 new file mode 100644 index 00000000..37353798 --- /dev/null +++ b/environments/jvm-jersey/Dockerfile-11 @@ -0,0 +1,15 @@ +FROM maven:3.6-jdk-11 as BUILD +WORKDIR /usr/src/myapp/ + +# To reuse the build cache, here we split maven dependency +# download and package into two RUN commands to avoid cache invalidation. +COPY pom.xml . +RUN mvn dependency:go-offline + +COPY src /usr/src/myapp/src/ +RUN mvn package + +FROM openjdk:11-jre +COPY --from=BUILD /usr/src/myapp/target/env-jvm-jersey-0.0.1.jar /app.jar +ENTRYPOINT java ${JVM_OPTS} -Djava.security.egd=file:/dev/./urandom -jar /app.jar --server.port=8888 +EXPOSE 8888 diff --git a/environments/jvm-jersey/README.md b/environments/jvm-jersey/README.md new file mode 100644 index 00000000..2a6278d9 --- /dev/null +++ b/environments/jvm-jersey/README.md @@ -0,0 +1,68 @@ +# Fission: Java and JVM-Jersey Environment + +This is the JVM (Jersey based) environment for Fission. + +It's a Docker image containing a OpenJDK8 runtime, along with a +dynamic loader. A few dependencies are included in the +pom.xml file. + +Unlike the other [JVM environment](../jvm) which is based on the Spring framework, this environment uses Jersey. + +Looking for ready-to-run examples? See the [JVM examples directory](../../examples/jvm-jersey). + +## Customizing this image + +To add package dependencies, edit pom.xml to add what you +need, and rebuild this image (instructions below). + +## Rebuilding and pushing the image + +You'll need access to a Docker registry to push the image: you can +sign up for Docker hub at hub.docker.com, or use registries from +gcr.io, quay.io, etc. Let's assume you're using a docker hub account +called USER. Build and push the image to the the registry: + +``` + docker build -t USER/jvm-jersey-env . && docker push USER/jvm-jersey-env +``` + +You can also create environment image based on JVM 11 using Dockerfile-11 in this directory. + +## Using the image in fission + +You can add this customized image to fission with "fission env +create": + +``` + fission env create --name jvm --image USER/jvm-jersey-env +``` + +Or, if you already have an environment, you can update its image: + +``` + fission env update --name jvm --image USER/jvm-jersey-env +``` + +After this, fission functions that have the env parameter set to the +same environment name as this command will use this environment. + +## Web Server Framework + +JVM Jersey environment uses an embedded Jetty HTTP server by default, as can be seen in the pom.xml file. + +``` + + org.eclipse.jetty + jetty-server + 9.0.4.v20130625 + + + org.eclipse.jetty + jetty-servlet + 9.0.4.v20130625 + +``` + +## Java and JVM builder + +There are two JVM environment builder based on OpenJDK8 and OpenJDK 11 and using Maven 3.5.4. The default build command runs `mvn clean package` and uses the target/*with-dependencies.jar file for function. The default build command can be overridden as long as the uber jar file is copied to ${DEPLOY_PKG}. diff --git a/environments/jvm-jersey/builder/Dockerfile b/environments/jvm-jersey/builder/Dockerfile new file mode 100644 index 00000000..24db019a --- /dev/null +++ b/environments/jvm-jersey/builder/Dockerfile @@ -0,0 +1,48 @@ +## Fission builder base image +ARG BUILDER_IMAGE=fission/builder:latest +FROM ${BUILDER_IMAGE} + +## Section copied from the openjdk:8-jdk-alpine Dockerfile - (https://github.com/docker-library/openjdk/blob/47a6539cd18023dafb45db9013455136cc0bca07/8/jdk/alpine/Dockerfile) + +ENV LANG C.UTF-8 +RUN { \ + echo '#!/bin/sh'; \ + echo 'set -e'; \ + echo; \ + echo 'dirname "$(dirname "$(readlink -f "$(which javac || which java)")")"'; \ + } > /usr/local/bin/docker-java-home \ + && chmod +x /usr/local/bin/docker-java-home +ENV JAVA_HOME /usr/lib/jvm/java-1.8-openjdk +ENV PATH $PATH:/usr/lib/jvm/java-1.8-openjdk/jre/bin:/usr/lib/jvm/java-1.8-openjdk/bin + +ENV JAVA_VERSION 8u181 +## Use "fuzzy" version matching to pin the version to a major/minor release +ENV JAVA_ALPINE_VERSION "~8" + +RUN set -x \ + && apk add --no-cache \ + openjdk8="$JAVA_ALPINE_VERSION" \ + && [ "$JAVA_HOME" = "$(docker-java-home)" ] + +## Section copied from the Maven Dockerfile + +RUN apk add --no-cache curl tar bash procps + +ARG MAVEN_VERSION=3.5.4 +ARG USER_HOME_DIR="/root" +ARG SHA=ce50b1c91364cb77efe3776f756a6d92b76d9038b0a0782f7d53acf1e997a14d +ARG BASE_URL=https://apache.osuosl.org/maven/maven-3/${MAVEN_VERSION}/binaries + +RUN mkdir -p /usr/share/maven /usr/share/maven/ref \ + && curl -fsSL -o /tmp/apache-maven.tar.gz ${BASE_URL}/apache-maven-${MAVEN_VERSION}-bin.tar.gz \ + && echo "${SHA} /tmp/apache-maven.tar.gz" | sha256sum -c - \ + && tar -xzf /tmp/apache-maven.tar.gz -C /usr/share/maven --strip-components=1 \ + && rm -f /tmp/apache-maven.tar.gz \ + && ln -s /usr/share/maven/bin/mvn /usr/bin/mvn + +ENV MAVEN_HOME /usr/share/maven +ENV MAVEN_CONFIG "$USER_HOME_DIR/.m2" + +## Fission builder specific section +ADD build.sh /usr/local/bin/build +EXPOSE 8001 diff --git a/environments/jvm-jersey/builder/Dockerfile-11 b/environments/jvm-jersey/builder/Dockerfile-11 new file mode 100644 index 00000000..8849d9af --- /dev/null +++ b/environments/jvm-jersey/builder/Dockerfile-11 @@ -0,0 +1,43 @@ +## Fission builder base image +ARG BUILDER_IMAGE=fission/builder:latest +FROM ${BUILDER_IMAGE} + +## Section referred from the openjdk:8-jdk-alpine Dockerfile - (https://github.com/docker-library/openjdk/blob/47a6539cd18023dafb45db9013455136cc0bca07/8/jdk/alpine/Dockerfile) + +ENV LANG C.UTF-8 +RUN { \ + echo '#!/bin/sh'; \ + echo 'set -e'; \ + echo; \ + echo 'dirname "$(dirname "$(readlink -f "$(which javac || which java)")")"'; \ + } > /usr/local/bin/docker-java-home \ + && chmod +x /usr/local/bin/docker-java-home +ENV JAVA_HOME /usr/lib/jvm/java-11-openjdk +ENV PATH $PATH:/usr/lib/jvm/java-11-openjdk/jre/bin:/usr/lib/jvm/java-11-openjdk/bin + +RUN set -x \ + && apk add --no-cache openjdk11 \ + && [ "$JAVA_HOME" = "$(docker-java-home)" ] + +## Section copied from the Maven Dockerfile + +RUN apk add --no-cache curl tar bash procps + +ARG MAVEN_VERSION=3.5.4 +ARG USER_HOME_DIR="/root" +ARG SHA=ce50b1c91364cb77efe3776f756a6d92b76d9038b0a0782f7d53acf1e997a14d +ARG BASE_URL=https://apache.osuosl.org/maven/maven-3/${MAVEN_VERSION}/binaries + +RUN mkdir -p /usr/share/maven /usr/share/maven/ref \ + && curl -fsSL -o /tmp/apache-maven.tar.gz ${BASE_URL}/apache-maven-${MAVEN_VERSION}-bin.tar.gz \ + && echo "${SHA} /tmp/apache-maven.tar.gz" | sha256sum -c - \ + && tar -xzf /tmp/apache-maven.tar.gz -C /usr/share/maven --strip-components=1 \ + && rm -f /tmp/apache-maven.tar.gz \ + && ln -s /usr/share/maven/bin/mvn /usr/bin/mvn + +ENV MAVEN_HOME /usr/share/maven +ENV MAVEN_CONFIG "$USER_HOME_DIR/.m2" + +## Fission builder specific section +ADD build.sh /usr/local/bin/build +EXPOSE 8001 diff --git a/environments/jvm-jersey/builder/build.sh b/environments/jvm-jersey/builder/build.sh new file mode 100755 index 00000000..bf167d59 --- /dev/null +++ b/environments/jvm-jersey/builder/build.sh @@ -0,0 +1,4 @@ +#!/bin/sh +set -eou pipefail +mvn clean package +cp ${SRC_PKG}/target/*with-dependencies.jar ${DEPLOY_PKG} \ No newline at end of file diff --git a/environments/jvm-jersey/pom.xml b/environments/jvm-jersey/pom.xml new file mode 100644 index 00000000..bdc79452 --- /dev/null +++ b/environments/jvm-jersey/pom.xml @@ -0,0 +1,86 @@ + + + 4.0.0 + + io.fission + env-jvm-jersey + 0.0.1 + + + 1.6 + 1.6 + + + + + io.fission + fission-jvm-jersey + 0.0.1 + + + org.eclipse.jetty + jetty-server + 9.0.4.v20130625 + + + org.eclipse.jetty + jetty-servlet + 9.0.4.v20130625 + + + org.glassfish.jersey.containers + jersey-container-servlet-core + 2.0 + + + org.glassfish.jersey.media + jersey-media-json-jackson + 2.0 + + + javax.xml + jaxb-api + 2.1 + + + + + + + org.apache.maven.plugins + maven-compiler-plugin + 3.1 + + ${java.source.level} + ${java.target.level} + UTF-8 + true + true + + + + maven-assembly-plugin + + + + io.fission.Main + + + + jar-with-dependencies + + false + + + + package + + single + + + + + + + diff --git a/environments/jvm-jersey/src/main/java/io/fission/FunctionLoadRequest.java b/environments/jvm-jersey/src/main/java/io/fission/FunctionLoadRequest.java new file mode 100644 index 00000000..02d3e65a --- /dev/null +++ b/environments/jvm-jersey/src/main/java/io/fission/FunctionLoadRequest.java @@ -0,0 +1,35 @@ +package io.fission; + +import org.codehaus.jackson.annotate.JsonIgnoreProperties; + +@JsonIgnoreProperties(ignoreUnknown = true) +public class FunctionLoadRequest { + + private String filepath; + private String functionName; + private String url; + + String getFilepath() { + return filepath; + } + + void setFilepath(String filepath) { + this.filepath = filepath; + } + + String getUrl() { + return url; + } + + void setUrl(String url) { + this.url = url; + } + + public String getFunctionName() { + return functionName; + } + + public void setFunctionName(String functionName) { + this.functionName = functionName; + } +} diff --git a/environments/jvm-jersey/src/main/java/io/fission/JerseyServer.java b/environments/jvm-jersey/src/main/java/io/fission/JerseyServer.java new file mode 100644 index 00000000..492d0164 --- /dev/null +++ b/environments/jvm-jersey/src/main/java/io/fission/JerseyServer.java @@ -0,0 +1,147 @@ +package io.fission; + +import java.io.File; +import java.io.IOException; +import java.io.PrintWriter; +import java.net.MalformedURLException; +import java.net.URL; +import java.net.URLClassLoader; +import java.util.Enumeration; +import java.util.jar.JarEntry; +import java.util.jar.JarFile; +import java.util.logging.Level; +import java.util.logging.Logger; +import javax.ws.rs.GET; +import javax.ws.rs.POST; +import javax.ws.rs.PUT; +import javax.ws.rs.DELETE; +import javax.ws.rs.Path; +import javax.ws.rs.core.Response; +import javax.ws.rs.core.Context; +import javax.ws.rs.container.ContainerRequestContext; + +import io.fission.Function; + +@Path("/") +public class JerseyServer { + + private static Function fn; + + private static final int CLASS_LENGTH = 6; + + private static Logger logger = Logger.getGlobal(); + + @GET + public Response home(@Context ContainerRequestContext request) { + return callUserFunction(request); + } + + @POST + public Response homePost(@Context ContainerRequestContext request) { + return callUserFunction(request); + } + + @PUT + public Response homePut(@Context ContainerRequestContext request) { + return callUserFunction(request); + } + + @DELETE + public Response homeDelete(@Context ContainerRequestContext request) { + return callUserFunction(request); + } + + @Path("v2/specialize") + @POST + public Response specialize(FunctionLoadRequest req) { + + long startTime = System.nanoTime(); + File file = new File(req.getFilepath()); + + if (!file.exists()) { + return Response.status(Response.Status.BAD_REQUEST).entity("/userfunc/usernot found").build(); + } + + String entryPoint = req.getFunctionName(); + logger.log(Level.INFO, "Entrypoint class:" + entryPoint); + if (entryPoint == null) { + return Response.status(Response.Status.BAD_REQUEST).entity("Entrypoint class is missing in the JAR or the name is incorrect") + .build(); + } + + JarFile jarFile = null; + ClassLoader cl = null; + try { + + jarFile = new JarFile(file); + Enumeration e = jarFile.entries(); + URL[] urls = { new URL("jar:file:" + file + "!/") }; + + // TODO Check if the classloading can be improved for ex. use something like: + // Thread.currentThread().setContextClassLoader(cl); + if (this.getClass().getClassLoader() == null) { + cl = URLClassLoader.newInstance(urls); + } else { + cl = URLClassLoader.newInstance(urls, this.getClass().getClassLoader()); + } + + if (cl == null) { + + return Response.status(Response.Status.BAD_REQUEST).entity("Failed to initialize the classloader") + .build(); + } + + // Load all dependent classes from libraries etc. + while (e.hasMoreElements()) { + JarEntry je = e.nextElement(); + if (je.isDirectory() || !je.getName().endsWith(".class")) { + continue; + } + String className = je.getName().substring(0, je.getName().length() - CLASS_LENGTH); + className = className.replace('/', '.'); + cl.loadClass(className); + } + + // Instantiate the function class + fn = (Function) cl.loadClass(entryPoint).newInstance(); + } catch (MalformedURLException e) { + e.printStackTrace(); + return Response.status(Response.Status.BAD_REQUEST).entity("Entrypoint class is missing in the function") + .build(); + } catch (ClassNotFoundException e) { + e.printStackTrace(); + return Response.status(Response.Status.BAD_REQUEST).entity("Error loading Function or dependent class") + .build(); + } catch (InstantiationException e) { + e.printStackTrace(); + return Response.status(Response.Status.BAD_REQUEST) + .entity("Error creating a new instance of function class").build(); + } catch (IllegalAccessException e) { + e.printStackTrace(); + return Response.status(Response.Status.BAD_REQUEST) + .entity("Error creating a new instance of function class").build(); + } catch (IOException e) { + e.printStackTrace(); + return Response.status(Response.Status.BAD_REQUEST).entity("Error reading the JAR file").build(); + } finally { + try { + jarFile.close(); + } catch (IOException e) { + e.printStackTrace(); + return Response.status(Response.Status.BAD_REQUEST) + .entity("Error closing the file while loading the class").build(); + } + } + long elapsedTime = System.nanoTime() - startTime; + logger.log(Level.INFO, "Specialize call done in: " + elapsedTime / 1000000 + " ms"); + return Response.status(Response.Status.OK).entity("Done").build(); + } + + private Response callUserFunction(ContainerRequestContext httpRequest) { + if (fn == null) { + return Response.status(Response.Status.BAD_REQUEST).entity("Container not specialized").build(); + } else { + return fn.call(httpRequest, null); + } + } +} diff --git a/environments/jvm-jersey/src/main/java/io/fission/Main.java b/environments/jvm-jersey/src/main/java/io/fission/Main.java new file mode 100644 index 00000000..a3148f1c --- /dev/null +++ b/environments/jvm-jersey/src/main/java/io/fission/Main.java @@ -0,0 +1,50 @@ +package io.fission; + +import org.eclipse.jetty.server.Server; +import org.eclipse.jetty.servlet.ServletContextHandler; +import org.eclipse.jetty.servlet.ServletHolder; +import org.glassfish.jersey.jackson.JacksonFeature; +import org.glassfish.jersey.server.ResourceConfig; +import org.glassfish.jersey.servlet.ServletContainer; + +public class Main { + + private static final int DEFAULT_PORT = 8888; + + private int serverPort; + + public Main(int serverPort) throws Exception { + this.serverPort = serverPort; + Server server = configureServer(); + server.start(); + server.join(); + } + + private Server configureServer() { + ResourceConfig resourceConfig = new ResourceConfig(); + resourceConfig.packages(JerseyServer.class.getPackage().getName()); + resourceConfig.register(JacksonFeature.class); + ServletContainer servletContainer = new ServletContainer(resourceConfig); + ServletHolder sh = new ServletHolder(servletContainer); + Server server = new Server(serverPort); + ServletContextHandler context = new ServletContextHandler(ServletContextHandler.SESSIONS); + context.setContextPath("/"); + context.addServlet(sh, "/*"); + server.setHandler(context); + return server; + } + + public static void main(String[] args) throws Exception { + + int serverPort = DEFAULT_PORT; + + if(args.length >= 1) { + try { + serverPort = Integer.parseInt(args[0]); + } catch (NumberFormatException e) { + e.printStackTrace(); + } + } + new Main(serverPort); + } +} diff --git a/examples/jvm-jersey/java/.gitignore b/examples/jvm-jersey/java/.gitignore new file mode 100644 index 00000000..4c3f8757 --- /dev/null +++ b/examples/jvm-jersey/java/.gitignore @@ -0,0 +1,4 @@ +.project +.settings +.classpath +target/ diff --git a/examples/jvm-jersey/java/README.md b/examples/jvm-jersey/java/README.md new file mode 100644 index 00000000..1c9bf76e --- /dev/null +++ b/examples/jvm-jersey/java/README.md @@ -0,0 +1,60 @@ +# Hello World in JVM/Java on Fission + +The `io.fission.HelloWorld.java` class is a very simple fission function that implements `io.fission.Function` and says "Hello, World!" . + +## Building and deploying using Fission + +Fission's builder can be used to create the binary artifact from source code. Create an environment with builder image and then create a package. + +``` +$ zip -r java-src-pkg.zip * +$ fission env create --name java --image fission/jvm-env --version 2 --keeparchive --builder fission/jvm-builder +$ fission package create --sourcearchive java-src-pkg.zip --env java +java-src-pkg-zip-tvd0 +$ fission package info --name java-src-pkg-zip-tvd0 +Name: java-src-pkg-zip-tvd0 +Environment: java +Status: succeeded +Build Logs: +[INFO] Scanning for projects... +[INFO] +[INFO] -----------------------< io.fission:hello-world >----------------------- +[INFO] Building hello-world 1.0-SNAPSHOT +[INFO] --------------------------------[ jar ]--------------------------------- +``` + +Once package's status is `succeeded` then that package can be used to create and execute a function. + +``` +$ fission fn create --name hello --pkg java-src-pkg-zip-tvd0 --env java --entrypoint io.fission.HelloWorld +$ fission fn test --name hello +Hello World! +``` + +## Building locally and deploying with Fission + +You can build the jar file in one of the two ways below based on your setup: + +- You can use docker without the need to install JDK and Maven to build the jar file from source code: + +```bash +$ bash -x ./build.sh + +``` +- If you have JDK and Maven installed, you can directly build the JAR file using command: + +``` +$ mvn clean package +``` + +Both of above steps will generate a target subdirectory which has the archive `target/hello-world-1.0-SNAPSHOT-jar-with-dependencies.jar` which will be used for creating function. + +- The archive created above will be used as a deploy package when creating the function. + +``` +$ fission env create --name jvm --image fission/jvm-env --version 2 --keeparchive=true +$ fission fn create --name hello --deploy target/hello-world-1.0-SNAPSHOT-jar-with-dependencies.jar --env jvm --entrypoint io.fission.HelloWorld +$ fission route create --function hello --url /hellop --method GET +$ fission fn test --name hello +Hello World! +``` diff --git a/examples/jvm-jersey/java/build.sh b/examples/jvm-jersey/java/build.sh new file mode 100755 index 00000000..abac4200 --- /dev/null +++ b/examples/jvm-jersey/java/build.sh @@ -0,0 +1,5 @@ +#!/bin/sh +# This script allows you to build the jar without needing Maven & JDK installed locally. +# You need docker, as it uses a Docker image to build source code +set -eou pipefail +docker run -it --rm -v "$(pwd)":/usr/src/mymaven -w /usr/src/mymaven maven:3.5-jdk-8 mvn clean package diff --git a/examples/jvm-jersey/java/pom.xml b/examples/jvm-jersey/java/pom.xml new file mode 100644 index 00000000..efc5fdf0 --- /dev/null +++ b/examples/jvm-jersey/java/pom.xml @@ -0,0 +1,76 @@ + + + 4.0.0 + io.fission + jersey-hello-world + 0.0.1 + jar + + hello-world + http://maven.apache.org + + + UTF-8 + 1.6 + 1.6 + + + + + io.fission + fission-jvm-jersey + 0.0.1 + provided + + + javax.ws.rs + javax.ws.rs-api + 2.0 + provided + + + org.glassfish.jersey.containers + jersey-container-servlet-core + 2.0 + test + + + junit + junit + 4.13 + test + + + + + + + maven-assembly-plugin + + + jar-with-dependencies + + + + + make-assembly + package + + single + + + + + + org.apache.maven.plugins + maven-surefire-plugin + 2.22.1 + + false + + + + + + diff --git a/examples/jvm-jersey/java/specs/README b/examples/jvm-jersey/java/specs/README new file mode 100644 index 00000000..1db3f9a5 --- /dev/null +++ b/examples/jvm-jersey/java/specs/README @@ -0,0 +1,42 @@ + +Fission Specs +============= + +This is a set of specifications for a Fission app. This includes functions, +environments, and triggers; we collectively call these things "resources". + +How to use these specs +---------------------- + +These specs are handled with the 'fission spec' command. See 'fission spec --help'. + +'fission spec apply' will "apply" all resources specified in this directory to your +cluster. That means it checks what resources exist on your cluster, what resources are +specified in the specs directory, and reconciles the difference by creating, updating or +deleting resources on the cluster. + +'fission spec apply' will also package up your source code (or compiled binaries) and +upload the archives to the cluster if needed. It uses 'ArchiveUploadSpec' resources in +this directory to figure out which files to archive. + +You can use 'fission spec apply --watch' to watch for file changes and continuously keep +the cluster updated. + +You can add YAMLs to this directory by writing them manually, but it's easier to generate +them. Use 'fission function create --spec' to generate a function spec, +'fission environment create --spec' to generate an environment spec, and so on. + +You can edit any of the files in this directory, except 'fission-deployment-config.yaml', +which contains a UID that you should never change. To apply your changes simply use +'fission spec apply'. + +fission-deployment-config.yaml +------------------------------ + +fission-deployment-config.yaml contains a UID. This UID is what fission uses to correlate +resources on the cluster to resources in this directory. + +All resources created by 'fission spec apply' are annotated with this UID. Resources on +the cluster that are _not_ annotated with this UID are never modified or deleted by +fission. + diff --git a/examples/jvm-jersey/java/specs/env-java.yaml b/examples/jvm-jersey/java/specs/env-java.yaml new file mode 100644 index 00000000..78b2db7b --- /dev/null +++ b/examples/jvm-jersey/java/specs/env-java.yaml @@ -0,0 +1,17 @@ +apiVersion: fission.io/v1 +kind: Environment +metadata: + creationTimestamp: null + name: java-jersey + namespace: default +spec: + builder: + command: build + image: fission/jvm-jersey-builder + imagepullsecret: "" + keeparchive: true + poolsize: 3 + resources: {} + runtime: + image: fission/jvm-jersey-env + version: 2 diff --git a/examples/jvm-jersey/java/specs/fission-deployment-config.yaml b/examples/jvm-jersey/java/specs/fission-deployment-config.yaml new file mode 100644 index 00000000..24e5e094 --- /dev/null +++ b/examples/jvm-jersey/java/specs/fission-deployment-config.yaml @@ -0,0 +1,7 @@ +# This file is generated by the 'fission spec init' command. +# See the README in this directory for background and usage information. +# Do not edit the UID below: that will break 'fission spec apply' +apiVersion: fission.io/v1 +kind: DeploymentConfig +name: java-jersey +uid: 908a303a-bf23-4bae-a22c-b1db9a1f71a9 diff --git a/examples/jvm-jersey/java/specs/function-hello.yaml b/examples/jvm-jersey/java/specs/function-hello.yaml new file mode 100644 index 00000000..9dd85f65 --- /dev/null +++ b/examples/jvm-jersey/java/specs/function-hello.yaml @@ -0,0 +1,27 @@ +apiVersion: fission.io/v1 +kind: Function +metadata: + creationTimestamp: null + name: hello + namespace: default +spec: + InvokeStrategy: + ExecutionStrategy: + ExecutorType: poolmgr + MaxScale: 0 + MinScale: 0 + SpecializationTimeout: 120 + TargetCPUPercent: 0 + StrategyType: execution + configmaps: null + environment: + name: java-jersey + namespace: default + functionTimeout: 60 + package: + functionName: io.fission.HelloWorld + packageref: + name: hellojava + namespace: default + resources: {} + secrets: null diff --git a/examples/jvm-jersey/java/specs/package-hellojava.yaml b/examples/jvm-jersey/java/specs/package-hellojava.yaml new file mode 100644 index 00000000..f606e112 --- /dev/null +++ b/examples/jvm-jersey/java/specs/package-hellojava.yaml @@ -0,0 +1,26 @@ +include: +- src +- pom.xml +kind: ArchiveUploadSpec +name: src-URlE + +--- +apiVersion: fission.io/v1 +kind: Package +metadata: + creationTimestamp: null + name: hellojavajersey + namespace: default +spec: + deployment: + checksum: {} + environment: + name: java-jersey + namespace: default + source: + checksum: {} + type: url + url: archive://src-URlE +status: + buildstatus: pending + lastUpdateTimestamp: "2020-01-29T09:43:56Z" diff --git a/examples/jvm-jersey/java/src/main/java/io/fission/HelloWorld.java b/examples/jvm-jersey/java/src/main/java/io/fission/HelloWorld.java new file mode 100644 index 00000000..29039300 --- /dev/null +++ b/examples/jvm-jersey/java/src/main/java/io/fission/HelloWorld.java @@ -0,0 +1,25 @@ +package io.fission; + +import javax.ws.rs.core.Response; +import javax.ws.rs.container.ContainerRequestContext; +import io.fission.Function; +import java.io.BufferedReader; +import java.util.stream.Collectors; +import java.io.InputStreamReader; + +public class HelloWorld implements Function { + + public static final String RETURN_STRING = "Hello World!"; + + @Override + public Response call(ContainerRequestContext request, Context arg1) { + if(request.getMethod().equals("GET")) { + return Response.ok(RETURN_STRING).build(); + } + else { + String body = new BufferedReader(new InputStreamReader(request.getEntityStream())).lines() + .parallel().collect(Collectors.joining("\n")); + return Response.ok("Echo: " + body).build(); + } + } +} diff --git a/examples/jvm-jersey/java/src/test/java/io/fission/HelloWorldTest.java b/examples/jvm-jersey/java/src/test/java/io/fission/HelloWorldTest.java new file mode 100644 index 00000000..907946d8 --- /dev/null +++ b/examples/jvm-jersey/java/src/test/java/io/fission/HelloWorldTest.java @@ -0,0 +1,25 @@ +package io.fission; + +import java.net.URI; +import java.net.URISyntaxException; +import org.glassfish.jersey.server.ContainerRequest; +import javax.ws.rs.core.Response; +import org.junit.Assert; +import org.junit.Test; + + +public class HelloWorldTest { + + @Test + public void testResponse() { + HelloWorld hw = new HelloWorld(); + ContainerRequest request = null; + try { + request = new ContainerRequest(new URI("http://example.com/"),new URI("/hello"),"GET",null,null); + } catch (URISyntaxException e) { + e.printStackTrace(); + } + Response response = hw.call(request, null); + Assert.assertTrue(response.getEntity().toString().equals(HelloWorld.RETURN_STRING)); + } +} diff --git a/hack/release-build.sh b/hack/release-build.sh index 3ca96028..3b69a2ae 100755 --- a/hack/release-build.sh +++ b/hack/release-build.sh @@ -148,6 +148,8 @@ build_all_envs() { build_env_image "$version" "python" "python-env" "2.7" build_env_image "$version" "ruby" "ruby-env" "" build_env_image "$version" "jvm" "jvm-env" "" + build_env_image "$version" "jvm-jersey" "jvm-jersey-env" "" + build_env_image "$version" "jvm-jersey" "jvm-jersey-env" "11" build_env_image "$version" "tensorflow-serving" "tensorflow-serving-env" "" } @@ -180,19 +182,20 @@ build_all_env_builders() { local version=$1 # call with version, env dir, image name base, image name variant - build_env_builder_image "$version" "python" "python-builder" "" - build_env_builder_image "$version" "binary" "binary-builder" "" - build_env_builder_image "$version" "go" "go-builder" "" - build_env_builder_image "$version" "go" "go-builder" "1.11.4" - build_env_builder_image "$version" "go" "go-builder" "1.12" - build_env_builder_image "$version" "go" "go-builder" "1.13" - build_env_builder_image "$version" "go" "go-builder" "1.14" - build_env_builder_image "$version" "jvm" "jvm-builder" "" - build_env_builder_image "$version" "nodejs" "node-builder" "" - build_env_builder_image "$version" "nodejs" "node-builder" "12.16" - build_env_builder_image "$version" "php7" "php-builder" "" - build_env_builder_image "$version" "ruby" "ruby-builder" "" - build_env_builder_image "$version" "dotnet20" "dotnet20-builder" "" + build_env_builder_image "$version" "python" "python-builder" "" + build_env_builder_image "$version" "binary" "binary-builder" "" + build_env_builder_image "$version" "go" "go-builder" "" + build_env_builder_image "$version" "go" "go-builder" "1.11.4" + build_env_builder_image "$version" "go" "go-builder" "1.12" + build_env_builder_image "$version" "go" "go-builder" "1.13" + build_env_builder_image "$version" "go" "go-builder" "1.14" + build_env_builder_image "$version" "jvm" "jvm-builder" "" + build_env_builder_image "$version" "jvm-jersey" "jvm-jersey-builder" "" + build_env_builder_image "$version" "jvm-jersey" "jvm-jersey-builder" "11" + build_env_builder_image "$version" "nodejs" "node-builder" "" + build_env_builder_image "$version" "php7" "php-builder" "" + build_env_builder_image "$version" "ruby" "ruby-builder" "" + build_env_builder_image "$version" "dotnet20" "dotnet20-builder" "" } build_charts() { diff --git a/hack/release.sh b/hack/release.sh index f20948b5..406668a7 100755 --- a/hack/release.sh +++ b/hack/release.sh @@ -89,6 +89,8 @@ push_all_envs() { push_env_image "$version" "python" "python-env" "2.7" push_env_image "$version" "ruby" "ruby-env" "" push_env_image "$version" "jvm" "jvm-env" "" + push_env_image "$version" "jvm-jersey" "jvm-jersey-env" "" + push_env_image "$version" "jvm-jersey" "jvm-jersey-env" "11" push_env_image "$version" "tensorflow-serving" "tensorflow-serving-env" "" } @@ -117,18 +119,20 @@ push_all_env_builders() { local version=$1 # call with version, env dir, image name base, image name variant - push_env_builder_image "$version" "python" "python-builder" "" - push_env_builder_image "$version" "binary" "binary-builder" "" - push_env_builder_image "$version" "go" "go-builder" "" - push_env_builder_image "$version" "go" "go-builder" "1.11.4" - push_env_builder_image "$version" "go" "go-builder" "1.12" - push_env_builder_image "$version" "go" "go-builder" "1.13" - push_env_builder_image "$version" "go" "go-builder" "1.14" - push_env_builder_image "$version" "jvm" "jvm-builder" "" - push_env_builder_image "$version" "nodejs" "node-builder" "" - push_env_builder_image "$version" "ruby" "ruby-builder" "" - push_env_builder_image "$version" "dotnet20" "dotnet20-builder" "" - push_env_builder_image "$version" "php7" "php-builder" "" + push_env_builder_image "$version" "python" "python-builder" "" + push_env_builder_image "$version" "binary" "binary-builder" "" + push_env_builder_image "$version" "go" "go-builder" "" + push_env_builder_image "$version" "go" "go-builder" "1.11.4" + push_env_builder_image "$version" "go" "go-builder" "1.12" + push_env_builder_image "$version" "go" "go-builder" "1.13" + push_env_builder_image "$version" "go" "go-builder" "1.14" + push_env_builder_image "$version" "jvm" "jvm-builder" "" + push_env_builder_image "$version" "jvm-jersey" "jvm-jersey-builder" "" + push_env_builder_image "$version" "jvm-jersey" "jvm-jersey-builder" "11" + push_env_builder_image "$version" "nodejs" "node-builder" "" + push_env_builder_image "$version" "ruby" "ruby-builder" "" + push_env_builder_image "$version" "dotnet20" "dotnet20-builder" "" + push_env_builder_image "$version" "php7" "php-builder" "" } # Push pre-upgrade-checks image diff --git a/test/build.sh b/test/build.sh index 15cc534a..373db855 100755 --- a/test/build.sh +++ b/test/build.sh @@ -29,12 +29,14 @@ build_and_push_builder $BUILDER_IMAGE:$TAG $REPO/go-mod-image-cache build_and_push_env_runtime python $REPO/python-env:$TAG "" build_and_push_env_runtime jvm $REPO/jvm-env:$TAG "" +build_and_push_env_runtime jvm-jersey $REPO/jvm-jersey-env:$TAG "" build_and_push_env_runtime go $REPO/go-env:$TAG "1.12" build_and_push_env_runtime nodejs $REPO/node-env:$TAG "12.16" build_and_push_env_runtime tensorflow-serving $REPO/tensorflow-serving-env:$TAG "" build_and_push_env_builder python $REPO/python-env-builder:$TAG $BUILDER_IMAGE:$TAG "" build_and_push_env_builder jvm $REPO/jvm-env-builder:$TAG $BUILDER_IMAGE:$TAG "" +build_and_push_env_builder jvm-jersey $REPO/jvm-jersey-env-builder:$TAG $BUILDER_IMAGE:$TAG "" build_and_push_env_builder go $REPO/go-env-builder:$TAG $BUILDER_IMAGE:$TAG "1.12" build_and_push_env_builder nodejs $REPO/node-env-builder:$TAG $BUILDER_IMAGE:$TAG "12.16" diff --git a/test/test_utils.sh b/test/test_utils.sh index 3e17d74e..c0f96ed6 100755 --- a/test/test_utils.sh +++ b/test/test_utils.sh @@ -505,6 +505,7 @@ run_all_tests() { export GO_RUNTIME_IMAGE=gcr.io/$GKE_PROJECT_NAME/go-env:${imageTag} export GO_BUILDER_IMAGE=gcr.io/$GKE_PROJECT_NAME/go-env-builder:${imageTag} export JVM_RUNTIME_IMAGE=gcr.io/$GKE_PROJECT_NAME/jvm-env:${imageTag} + export JVM_JERSEY_RUNTIME_IMAGE=gcr.io/$GKE_PROJECT_NAME/jvm-jersey-env:${imageTag} export JVM_BUILDER_IMAGE=gcr.io/$GKE_PROJECT_NAME/jvm-env-builder:${imageTag} export NODE_RUNTIME_IMAGE=gcr.io/$GKE_PROJECT_NAME/node-env:${imageTag} export NODE_BUILDER_IMAGE=gcr.io/$GKE_PROJECT_NAME/node-env-builder:${imageTag} diff --git a/test/tests/test_environments/test_jvm_jersey_env.sh b/test/tests/test_environments/test_jvm_jersey_env.sh new file mode 100755 index 00000000..4b267fbc --- /dev/null +++ b/test/tests/test_environments/test_jvm_jersey_env.sh @@ -0,0 +1,186 @@ +#!/bin/bash + + + +set -euo pipefail +source $(dirname $0)/../../utils.sh + +TEST_ID=$(generate_test_id) +echo "TEST_ID = $TEST_ID" + +ROOT=$(dirname $0)/../../.. + +env=jvm-$TEST_ID +fn_n=jvm-hello-n-$TEST_ID +fn_p=jvm-hello-p-$TEST_ID +fn_n_p=jvm-hello-n-post-$TEST_ID + +xmlBody=$(cat <<-END + + + + Gambardella, Matthew + XML Developer's Guide + Computer + 44.95 + 2000-10-01 + An in-depth look at creating applications + with XML. + + + Ralls, Kim + Midnight Rain + Fantasy + 5.95 + 2000-12-16 + A former architect battles corporate zombies, + an evil sorceress, and her own childhood to become queen + of the world. + + + Corets, Eva + Maeve Ascendant + Fantasy + 5.95 + 2000-11-17 + After the collapse of a nanotechnology + society in England, the young survivors lay the + foundation for a new society. + + + Corets, Eva + Oberon's Legacy + Fantasy + 5.95 + 2001-03-10 + In post-apocalypse England, the mysterious + agent known only as Oberon helps to create a new life + for the inhabitants of London. Sequel to Maeve + Ascendant. + + + Corets, Eva + The Sundered Grail + Fantasy + 5.95 + 2001-09-10 + The two daughters of Maeve, half-sisters, + battle one another for control of England. Sequel to + Oberon's Legacy. + + + Randall, Cynthia + Lover Birds + Romance + 4.95 + 2000-09-02 + When Carla meets Paul at an ornithology + conference, tempers fly as feathers get ruffled. + + + Thurman, Paula + Splish Splash + Romance + 4.95 + 2000-11-02 + A deep sea diver finds true love twenty + thousand leagues beneath the sea. + + + Knorr, Stefan + Creepy Crawlies + Horror + 4.95 + 2000-12-06 + An anthology of horror stories about roaches, + centipedes, scorpions and other insects. + + + Kress, Peter + Paradox Lost + Science Fiction + 6.95 + 2000-11-02 + After an inadvertant trip through a Heisenberg + Uncertainty Device, James Salway discovers the problems + of being quantum. + + + O'Brien, Tim + Microsoft .NET: The Programming Bible + Computer + 36.95 + 2000-12-09 + Microsoft's .NET initiative is explored in + detail in this deep programmer's reference. + + + O'Brien, Tim + MSXML3: A Comprehensive Guide + Computer + 36.95 + 2000-12-01 + The Microsoft MSXML3 parser is covered in + detail, with attention to XML DOM interfaces, XSLT processing, + SAX and more. + + + Galos, Mike + Visual Studio 7: A Comprehensive Guide + Computer + 49.95 + 2001-04-16 + Microsoft Visual Studio 7 is explored in depth, + looking at how Visual Basic, Visual C++, C#, and ASP+ are + integrated into a comprehensive development + environment. + + +END +) + +cleanup() { + clean_resource_by_id $TEST_ID +} + +if [ -z "${TEST_NOCLEANUP:-}" ]; then + trap cleanup EXIT +else + log "TEST_NOCLEANUP is set; not cleaning up test artifacts afterwards." +fi + +cd $ROOT/examples/jvm-jersey/java + +log "Creating the jar from application" +#Using Docker to build Jar so that maven & other Java dependencies are not needed on CI server +docker run --rm -v "$(pwd)":/usr/src/mymaven -w /usr/src/mymaven maven:3.5-jdk-8 mvn clean package -q + +log "Creating environment for Java" +fission env create --name $env --image $JVM_JERSEY_RUNTIME_IMAGE --version 2 --keeparchive=true + +log "Creating pool manager & new deployment function for Java" +fission fn create --name $fn_p --deploy target/jersey-hello-world-0.0.1-jar-with-dependencies.jar --env $env --entrypoint io.fission.HelloWorld +fission fn create --name $fn_n --deploy target/jersey-hello-world-0.0.1-jar-with-dependencies.jar --env $env --executortype newdeploy --entrypoint io.fission.HelloWorld + +log "Creating route for pool manager function" +fission route create --name $fn_p --function $fn_p --url /$fn_p --method GET + +log "Creating route for new deployment function" +fission route create --name $fn_n --function $fn_n --url /$fn_n --method GET + +log "Creating post route for new deployment function" +fission route create --name $fn_n_p --function $fn_n --url /$fn_n_p --method POST + +log "Waiting for router & pools to catch up" +sleep 10 + +log "Testing pool manager function" +timeout 60 bash -c "test_fn $fn_p Hello" + +log "Testing new deployment function" +timeout 60 bash -c "test_fn $fn_n Hello" + +log "Testing new deployment function for XML POST request" +timeout 60 bash -c "test_post_route $fn_n_p \"$xmlBody\" \"Echo: $xmlBody\"" + +log "Test PASSED" diff --git a/test/utils.sh b/test/utils.sh index d6ead6f8..0532afc3 100755 --- a/test/utils.sh +++ b/test/utils.sh @@ -186,6 +186,7 @@ export PYTHON_BUILDER_IMAGE=${PYTHON_BUILDER_IMAGE:-fission/python-builder} export GO_RUNTIME_IMAGE=${GO_RUNTIME_IMAGE:-fission/go-env-1.12} export GO_BUILDER_IMAGE=${GO_BUILDER_IMAGE:-fission/go-builder-1.12} export JVM_RUNTIME_IMAGE=${JVM_RUNTIME_IMAGE:-fission/jvm-env} +export JVM_JERSEY_RUNTIME_IMAGE=${JVM_JERSEY_RUNTIME_IMAGE:-fission/jvm-jersey-env} export JVM_BUILDER_IMAGE=${JVM_BUILDER_IMAGE:-fission/jvm-builder} export NODE_RUNTIME_IMAGE=${NODE_RUNTIME_IMAGE:-fission/node-env} export NODE_BUILDER_IMAGE=${NODE_BUILDER_IMAGE:-fission/node-env-builder}