mirror of
https://github.com/fnproject/fn.git
synced 2022-10-28 21:29:17 +03:00
* Bug: Fix building of Java project with no deps Currently there is no way to perform a Docker `COPY` where the source directory has no files, i.e. no way to ignore the error. So this change puts all the dependencies into the /function/target directory whereas they were in the /function/target/dependency directory before. This has the benefit of knowing that the COPY command will always succeed even if there are no dependencies as atleast the function jar itself will be in /function/target directory. * Add fail-never to first maven invocation This is important as if the pom.xml has a compilation step, for example, generate some source for me from my proto files in my src/main/resource directory, this would fail as we have not copied the source directory in yet.
222 lines
6.5 KiB
Go
222 lines
6.5 KiB
Go
package langs
|
|
|
|
import (
|
|
"bytes"
|
|
"errors"
|
|
"fmt"
|
|
"io/ioutil"
|
|
"net/url"
|
|
"os"
|
|
"path/filepath"
|
|
"strings"
|
|
)
|
|
|
|
// JavaLangHelper provides a set of helper methods for the lifecycle of Java Maven projects
|
|
type JavaLangHelper struct {
|
|
BaseHelper
|
|
}
|
|
|
|
// BuildFromImage returns the Docker image used to compile the Maven function project
|
|
func (lh *JavaLangHelper) BuildFromImage() string { return "maven:3.5-jdk-8-alpine" }
|
|
|
|
// RunFromImage returns the Docker image used to run the Java function.
|
|
func (lh *JavaLangHelper) RunFromImage() string {
|
|
return "registry.oracledx.com/skeppare/jfaas-runtime:latest"
|
|
}
|
|
|
|
// HasPreBuild returns whether the Java runtime has boilerplate that can be generated.
|
|
func (lh *JavaLangHelper) HasBoilerplate() bool { return true }
|
|
|
|
// GenerateBoilerplate will generate function boilerplate for a Java runtime. The default boilerplate is for a Maven
|
|
// project.
|
|
func (lh *JavaLangHelper) GenerateBoilerplate() error {
|
|
wd, err := os.Getwd()
|
|
if err != nil {
|
|
return err
|
|
}
|
|
|
|
pathToPomFile := filepath.Join(wd, "pom.xml")
|
|
if exists(pathToPomFile) {
|
|
return ErrBoilerplateExists
|
|
}
|
|
|
|
if err := ioutil.WriteFile(pathToPomFile, []byte(pomFile), os.FileMode(0644)); err != nil {
|
|
return err
|
|
}
|
|
|
|
mkDirAndWriteFile := func(dir, filename, content string) error {
|
|
fullPath := filepath.Join(wd, dir)
|
|
if err = os.MkdirAll(fullPath, os.FileMode(0755)); err != nil {
|
|
return err
|
|
}
|
|
|
|
fullFilePath := filepath.Join(fullPath, filename)
|
|
return ioutil.WriteFile(fullFilePath, []byte(content), os.FileMode(0644))
|
|
}
|
|
|
|
err = mkDirAndWriteFile("src/main/java/com/example/faas", "HelloFunction.java", helloJavaSrcBoilerplate)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
|
|
return mkDirAndWriteFile("src/test/java/com/example/faas", "HelloFunctionTest.java", helloJavaTestBoilerplate)
|
|
}
|
|
|
|
// Entrypoint returns the Java runtime Docker entrypoint that will be executed when the function is executed.
|
|
func (lh *JavaLangHelper) Cmd() string {
|
|
return "com.example.faas.HelloFunction::handleRequest"
|
|
}
|
|
|
|
// DockerfileCopyCmds returns the Docker COPY command to copy the compiled Java function jar and dependencies.
|
|
func (lh *JavaLangHelper) DockerfileCopyCmds() []string {
|
|
return []string{
|
|
"COPY --from=build-stage /function/target/*.jar /function/app/",
|
|
}
|
|
}
|
|
|
|
// DockerfileBuildCmds returns the build stage steps to compile the Maven function project.
|
|
func (lh *JavaLangHelper) DockerfileBuildCmds() []string {
|
|
return []string{
|
|
fmt.Sprintf("ENV MAVEN_OPTS %s", mavenOpts()),
|
|
"ADD pom.xml /function/pom.xml",
|
|
"RUN [\"mvn\", \"package\", \"dependency:copy-dependencies\", \"-DincludeScope=runtime\", " +
|
|
"\"-DskipTests=true\", \"-Dmdep.prependGroupId=true\", \"-DoutputDirectory=target\", \"--fail-never\"]",
|
|
"ADD src /function/src",
|
|
"RUN [\"mvn\", \"package\"]",
|
|
}
|
|
}
|
|
|
|
// HasPreBuild returns whether the Java Maven runtime has a pre-build step.
|
|
func (lh *JavaLangHelper) HasPreBuild() bool { return true }
|
|
|
|
// PreBuild ensures that the expected the function is based is a maven project.
|
|
func (lh *JavaLangHelper) PreBuild() error {
|
|
wd, err := os.Getwd()
|
|
if err != nil {
|
|
return err
|
|
}
|
|
|
|
if !exists(filepath.Join(wd, "pom.xml")) {
|
|
return errors.New("Could not find pom.xml - are you sure this is a Maven project?")
|
|
}
|
|
|
|
return nil
|
|
}
|
|
|
|
func mavenOpts() string {
|
|
var opts bytes.Buffer
|
|
|
|
if parsedURL, err := url.Parse(os.Getenv("http_proxy")); err == nil {
|
|
opts.WriteString(fmt.Sprintf("-Dhttp.proxyHost=%s ", parsedURL.Hostname()))
|
|
opts.WriteString(fmt.Sprintf("-Dhttp.proxyPort=%s ", parsedURL.Port()))
|
|
}
|
|
|
|
if parsedURL, err := url.Parse(os.Getenv("https_proxy")); err == nil {
|
|
opts.WriteString(fmt.Sprintf("-Dhttps.proxyHost=%s ", parsedURL.Hostname()))
|
|
opts.WriteString(fmt.Sprintf("-Dhttps.proxyPort=%s ", parsedURL.Port()))
|
|
}
|
|
|
|
nonProxyHost := os.Getenv("no_proxy")
|
|
opts.WriteString(fmt.Sprintf("-Dhttp.nonProxyHosts=%s ", strings.Replace(nonProxyHost, ",", "|", -1)))
|
|
|
|
opts.WriteString("-Dmaven.repo.local=/usr/share/maven/ref/repository")
|
|
|
|
return opts.String()
|
|
}
|
|
|
|
/* TODO temporarily generate maven project boilerplate from hardcoded values.
|
|
Will eventually move to using a maven archetype.
|
|
*/
|
|
|
|
const (
|
|
pomFile = `<?xml version="1.0" encoding="UTF-8"?>
|
|
<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/xsd/maven-4.0.0.xsd">
|
|
<modelVersion>4.0.0</modelVersion>
|
|
<properties>
|
|
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
|
|
</properties>
|
|
<groupId>com.example.faas</groupId>
|
|
<artifactId>hello</artifactId>
|
|
<version>1.0.0</version>
|
|
|
|
<repositories>
|
|
<repository>
|
|
<id>nexus-box</id>
|
|
<url>http://10.167.103.241:8081/repository/maven-snapshots/</url>
|
|
</repository>
|
|
</repositories>
|
|
|
|
<dependencies>
|
|
<dependency>
|
|
<groupId>com.oracle.faas</groupId>
|
|
<artifactId>api</artifactId>
|
|
<version>1.0.0-SNAPSHOT</version>
|
|
</dependency>
|
|
<dependency>
|
|
<groupId>com.oracle.faas</groupId>
|
|
<artifactId>testing</artifactId>
|
|
<version>1.0.0-SNAPSHOT</version>
|
|
<scope>test</scope>
|
|
</dependency>
|
|
<dependency>
|
|
<groupId>junit</groupId>
|
|
<artifactId>junit</artifactId>
|
|
<version>4.12</version>
|
|
<scope>test</scope>
|
|
</dependency>
|
|
</dependencies>
|
|
|
|
<build>
|
|
<plugins>
|
|
<plugin>
|
|
<groupId>org.apache.maven.plugins</groupId>
|
|
<artifactId>maven-compiler-plugin</artifactId>
|
|
<version>3.3</version>
|
|
<configuration>
|
|
<source>1.8</source>
|
|
<target>1.8</target>
|
|
</configuration>
|
|
</plugin>
|
|
</plugins>
|
|
</build>
|
|
</project>
|
|
`
|
|
|
|
helloJavaSrcBoilerplate = `package com.example.faas;
|
|
|
|
public class HelloFunction {
|
|
|
|
public String handleRequest(String input) {
|
|
String name = (input == null || input.isEmpty()) ? "world" : input;
|
|
|
|
return "Hello, " + name + "!";
|
|
}
|
|
|
|
}`
|
|
|
|
helloJavaTestBoilerplate = `package com.example.faas;
|
|
|
|
import com.oracle.faas.testing.*;
|
|
import org.junit.*;
|
|
|
|
import static org.junit.Assert.*;
|
|
|
|
public class HelloFunctionTest {
|
|
|
|
@Rule
|
|
public final FnTesting testing = FnTesting.createDefault();
|
|
|
|
@Test
|
|
public void shouldReturnGreeting() {
|
|
testing.givenEvent().enqueue();
|
|
testing.thenRun(HelloFunction.class, "handleRequest");
|
|
|
|
FnResult result = testing.getOnlyResult();
|
|
assertEquals("Hello, world!", result.getBodyAsString());
|
|
}
|
|
|
|
}`
|
|
)
|