diff --git a/build.go b/build.go index c7ee257..b56280b 100644 --- a/build.go +++ b/build.go @@ -55,7 +55,8 @@ func (b Build) Build(context libcnb.BuildContext) (libcnb.BuildResult, error) { cl := NewCertificateLoader() cl.Logger = b.Logger.BodyWriter() - v, err := b.getJVMVersion(context.Application.Path, cr) + jvmVersion := JVMVersion{Logger: b.Logger} + v, err := jvmVersion.GetJVMVersion(context.Application.Path, cr) if err != nil { return libcnb.BuildResult{}, fmt.Errorf("unable to determine jvm version\n%w", err) } @@ -174,46 +175,3 @@ func (b Build) warnIfJreNotUsed(jreAvailable, jreSkipped bool) { b.Logger.Header(color.New(color.FgYellow, color.Bold).Sprint(msg)) } - -func (b Build) getJVMVersion(appPath string, cr libpak.ConfigurationResolver) (string, error) { - version, explicit := cr.Resolve("BP_JVM_VERSION") - - if !explicit { - manifest, err := NewManifest(appPath) - if err != nil { - return version, err - } - - javaVersion := "" - - buildJdkSpecVersion, ok := manifest.Get("Build-Jdk-Spec") - if ok { - javaVersion = buildJdkSpecVersion - } - - buildJdkVersion, ok := manifest.Get("Build-Jdk") - if ok { - javaVersion = buildJdkVersion - } - - if len(javaVersion) > 0 { - javaVersionFromMaven := extractMajorVersion(javaVersion) - f := color.New(color.Faint) - b.Logger.Header(f.Sprint("Context specific overrides:")) - b.Logger.Body(f.Sprintf("$BP_JVM_VERSION \t\t%s\t\tthe Java version, extracted from main class", javaVersionFromMaven)) - return javaVersionFromMaven, nil - } - } - - return version, nil -} - -func extractMajorVersion(version string) string { - versionParts := strings.Split(version, ".") - - if versionParts[0] == "1" { - return versionParts[1] - } - - return versionParts[0] -} diff --git a/build_test.go b/build_test.go index 2c76d7a..6074653 100644 --- a/build_test.go +++ b/build_test.go @@ -17,11 +17,7 @@ package libjvm_test import ( - "bytes" - "encoding/binary" - "io/ioutil" "os" - "path/filepath" "testing" "github.com/buildpacks/libcnb" @@ -93,58 +89,6 @@ func testBuild(t *testing.T, context spec.G, it spec.S) { Expect(result.BOM.Entries[1].Launch).To(BeTrue()) }) - it("contributes JRE read from JAR", func() { - ctx.Plan.Entries = append(ctx.Plan.Entries, libcnb.BuildpackPlanEntry{Name: "jre", Metadata: LaunchContribution}) - ctx.Buildpack.API = "0.6" - ctx.Buildpack.Metadata = map[string]interface{}{ - "dependencies": []map[string]interface{}{ - { - "id": "jre", - "version": "11", - "stacks": []interface{}{"test-stack-id"}, - }, - }, - } - ctx.StackID = "test-stack-id" - temp, err := ioutil.TempDir("", "jre-application") - ctx.Application.Path = temp - Expect(err).NotTo(HaveOccurred()) - - defer os.RemoveAll(temp) - - err = os.Mkdir(filepath.Join(temp, "META-INF"), 0744) - Expect(err).NotTo(HaveOccurred()) - manifest := filepath.Join(temp, "META-INF", "MANIFEST.MF") - manifestContent := []byte("Main-Class: main") - err = ioutil.WriteFile(manifest, manifestContent, 0644) - Expect(err).NotTo(HaveOccurred()) - - classFile := filepath.Join(temp, "main.class") - classFileContent := bytes.NewBuffer(nil) - binary.Write(classFileContent, binary.BigEndian, uint8(0xCA)) - binary.Write(classFileContent, binary.BigEndian, uint8(0xFE)) - binary.Write(classFileContent, binary.BigEndian, uint8(0xBA)) - binary.Write(classFileContent, binary.BigEndian, uint8(0xBE)) - binary.Write(classFileContent, binary.BigEndian, uint16(0)) - binary.Write(classFileContent, binary.BigEndian, uint16(55)) - err = ioutil.WriteFile(classFile, classFileContent.Bytes(), 0666) - Expect(err).NotTo(HaveOccurred()) - - result, err := libjvm.Build{}.Build(ctx) - Expect(err).NotTo(HaveOccurred()) - - Expect(result.Layers).To(HaveLen(3)) - Expect(result.Layers[0].Name()).To(Equal("jre")) - Expect(result.Layers[1].Name()).To(Equal("helper")) - Expect(result.Layers[2].Name()).To(Equal("java-security-properties")) - - Expect(result.BOM.Entries).To(HaveLen(2)) - Expect(result.BOM.Entries[0].Name).To(Equal("jre")) - Expect(result.BOM.Entries[0].Launch).To(BeTrue()) - Expect(result.BOM.Entries[1].Name).To(Equal("helper")) - Expect(result.BOM.Entries[1].Launch).To(BeTrue()) - }) - it("contributes security-providers-classpath-8 before Java 9", func() { ctx.Plan.Entries = append(ctx.Plan.Entries, libcnb.BuildpackPlanEntry{Name: "jre", Metadata: LaunchContribution}) ctx.Buildpack.Metadata = map[string]interface{}{ diff --git a/init_test.go b/init_test.go index 11352dd..fdf4aa7 100644 --- a/init_test.go +++ b/init_test.go @@ -41,5 +41,6 @@ func TestUnit(t *testing.T) { suite("Manifest", testManifest) suite("MavenJARListing", testMavenJARListing) suite("Versions", testVersions) + suite("JVMVersions", testJVMVersion) suite.Run(t) } diff --git a/jvm_version.go b/jvm_version.go new file mode 100644 index 0000000..cdca5ff --- /dev/null +++ b/jvm_version.go @@ -0,0 +1,56 @@ +package libjvm + +import ( + "strings" + + "github.com/heroku/color" + "github.com/paketo-buildpacks/libpak" + "github.com/paketo-buildpacks/libpak/bard" +) + +type JVMVersion struct { + Logger bard.Logger +} + +func (jvmVersion JVMVersion) GetJVMVersion(appPath string, cr libpak.ConfigurationResolver) (string, error) { + version, explicit := cr.Resolve("BP_JVM_VERSION") + + if !explicit { + manifest, err := NewManifest(appPath) + if err != nil { + return version, err + } + + javaVersion := "" + + buildJdkSpecVersion, ok := manifest.Get("Build-Jdk-Spec") + if ok { + javaVersion = buildJdkSpecVersion + } + + buildJdkVersion, ok := manifest.Get("Build-Jdk") + if ok { + javaVersion = buildJdkVersion + } + + if len(javaVersion) > 0 { + javaVersionFromMaven := extractMajorVersion(javaVersion) + f := color.New(color.Faint) + jvmVersion.Logger.Header(f.Sprint("Context specific overrides:")) + jvmVersion.Logger.Body(f.Sprintf("$BP_JVM_VERSION \t\t %s \t\tthe Java version, extracted from main class", javaVersionFromMaven)) + return javaVersionFromMaven, nil + } + } + + return version, nil +} + +func extractMajorVersion(version string) string { + versionParts := strings.Split(version, ".") + + if versionParts[0] == "1" { + return versionParts[1] + } + + return versionParts[0] +} diff --git a/jvm_version_test.go b/jvm_version_test.go new file mode 100644 index 0000000..2c4b6db --- /dev/null +++ b/jvm_version_test.go @@ -0,0 +1,150 @@ +/* + * Copyright 2018-2020 the original author or authors. + * + * 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 + * + * https://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 libjvm_test + +import ( + "github.com/paketo-buildpacks/libpak" + "github.com/paketo-buildpacks/libpak/bard" + "io/ioutil" + "os" + "path/filepath" + "testing" + + "github.com/buildpacks/libcnb" + . "github.com/onsi/gomega" + "github.com/sclevine/spec" + + "github.com/paketo-buildpacks/libjvm" +) + +func testJVMVersion(t *testing.T, context spec.G, it spec.S) { + var ( + Expect = NewWithT(t).Expect + appPath string + logger bard.Logger + buildpack libcnb.Buildpack + ) + + it.Before(func() { + buildpack = libcnb.Buildpack{ + Metadata: map[string]interface{}{ + "configurations": []map[string]interface{}{ + { + "name": "BP_JVM_VERSION", + "default": "1.1.1", + }, + }, + }, + } + logger = bard.NewLogger(ioutil.Discard) + }) + + it("detecting JVM version from default", func() { + jvmVersion := libjvm.JVMVersion{Logger: logger} + + cr, err := libpak.NewConfigurationResolver(buildpack, &logger) + Expect(err).ToNot(HaveOccurred()) + version, err := jvmVersion.GetJVMVersion(appPath, cr) + Expect(err).ToNot(HaveOccurred()) + Expect(version).To(Equal("1.1.1")) + }) + + context("detecting JVM version", func() { + it.Before(func() { + Expect(os.Setenv("BP_JVM_VERSION", "17")).To(Succeed()) + }) + + it.After(func() { + Expect(os.Unsetenv("BP_JVM_VERSION")).To(Succeed()) + }) + + it("from environment variable", func() { + jvmVersion := libjvm.JVMVersion{Logger: logger} + + cr, err := libpak.NewConfigurationResolver(buildpack, &logger) + Expect(err).ToNot(HaveOccurred()) + version, err := jvmVersion.GetJVMVersion(appPath, cr) + Expect(err).ToNot(HaveOccurred()) + Expect(version).To(Equal("17")) + }) + }) + + context("detecting JVM version", func() { + it.Before(func() { + temp, err := prepareAppWithEntry("Build-Jdk: 1.8") + Expect(err).ToNot(HaveOccurred()) + appPath = temp + }) + + it.After(func() { + os.RemoveAll(appPath) + }) + + it("from manifest via Build-Jdk-Spec", func() { + jvmVersion := libjvm.JVMVersion{Logger: logger} + + cr, err := libpak.NewConfigurationResolver(buildpack, &logger) + Expect(err).ToNot(HaveOccurred()) + version, err := jvmVersion.GetJVMVersion(appPath, cr) + Expect(err).ToNot(HaveOccurred()) + Expect(version).To(Equal("8")) + }) + }) + + context("detecting JVM version", func() { + it.Before(func() { + Expect(os.Setenv("BP_JVM_VERSION", "17")).To(Succeed()) + temp, err := prepareAppWithEntry("Build-Jdk: 1.8") + Expect(err).ToNot(HaveOccurred()) + appPath = temp + }) + + it.After(func() { + Expect(os.Unsetenv("BP_JVM_VERSION")).To(Succeed()) + os.RemoveAll(appPath) + }) + + it("prefers environment variable over manifest", func() { + jvmVersion := libjvm.JVMVersion{Logger: logger} + + cr, err := libpak.NewConfigurationResolver(buildpack, &logger) + Expect(err).ToNot(HaveOccurred()) + version, err := jvmVersion.GetJVMVersion(appPath, cr) + Expect(err).ToNot(HaveOccurred()) + Expect(version).To(Equal("17")) + }) + }) + +} + +func prepareAppWithEntry(entry string) (string, error) { + temp, err := ioutil.TempDir("", "jre-app") + if err != nil { + return "", err + } + err = os.Mkdir(filepath.Join(temp, "META-INF"), 0744) + if err != nil { + return "", err + } + manifest := filepath.Join(temp, "META-INF", "MANIFEST.MF") + manifestContent := []byte(entry) + err = ioutil.WriteFile(manifest, manifestContent, 0644) + if err != nil { + return "", err + } + return temp, nil +}