From 86b5c28ff317ec53aa043140c8a81fc4e9fd29df Mon Sep 17 00:00:00 2001 From: Dhiru Pandey Date: Fri, 24 Sep 2021 06:42:30 -0700 Subject: [PATCH] COH-24004 - The Operator runner should support the JIB JVM argument files (#507) --- java/operator-test-helidon/pom.xml | 2 +- pkg/runner/cmd_server.go | 11 + pkg/runner/runner.go | 34 ++- pkg/runner/runner_jvm_jibclasspath_test.go | 243 +++++++++++++++++++++ 4 files changed, 285 insertions(+), 5 deletions(-) create mode 100644 pkg/runner/runner_jvm_jibclasspath_test.go diff --git a/java/operator-test-helidon/pom.xml b/java/operator-test-helidon/pom.xml index ced54c51f..a1140e8ac 100644 --- a/java/operator-test-helidon/pom.xml +++ b/java/operator-test-helidon/pom.xml @@ -63,7 +63,7 @@ ${version.plugin.jib} - io.helidon.microprofile.cdi.com.oracle.coherence.k8s.testing.Main + io.helidon.microprofile.server.Main packaged diff --git a/pkg/runner/cmd_server.go b/pkg/runner/cmd_server.go index 1e7767f79..c3b04412b 100644 --- a/pkg/runner/cmd_server.go +++ b/pkg/runner/cmd_server.go @@ -41,6 +41,17 @@ func server(details *RunDetails, _ *cobra.Command) { // If the main class environment variable is set then use that // otherwise run Coherence DCS. mc, found := details.lookupEnv(v1.EnvVarAppMainClass) + appDir := details.getenvOrDefault(v1.EnvVarCohAppDir, "/app") + jibMainClassFileName := appDir + "/jib-main-class-file" + fi, err := os.Stat(jibMainClassFileName) + mainCls := "" + if err == nil && (fi.Size() != 0) { + mainCls = readFirstLineFromFile(jibMainClassFileName, fi) + } + if !found && (len(mainCls) != 0) { + mc = mainCls + found = true + } switch { case found && details.AppType != AppTypeSpring: // we have a main class specified, and we're not a Spring Boot app diff --git a/pkg/runner/runner.go b/pkg/runner/runner.go index 6bbc40ba1..88002ba3d 100644 --- a/pkg/runner/runner.go +++ b/pkg/runner/runner.go @@ -7,6 +7,7 @@ package runner import ( + "bufio" "bytes" "context" "fmt" @@ -273,10 +274,18 @@ func createCommand(details *RunDetails) (string, *exec.Cmd, error) { // are running a Spring Boot application. if !details.isBuildPacks() && details.AppType != AppTypeSpring && details.isEnvTrueOrBlank(v1.EnvVarJvmClasspathJib) { appDir := details.getenvOrDefault(v1.EnvVarCohAppDir, "/app") - details.addClasspathIfExists(appDir + "/resources") - details.addClasspathIfExists(appDir + "/classes") - details.addJarsToClasspath(appDir + "/classpath") - details.addJarsToClasspath(appDir + "/libs") + fi, e := os.Stat(appDir + "/jib-classpath-file") + if e == nil && (fi.Size() != 0) { + clsPath := readFirstLineFromFile(appDir+"/jib-classpath-file", fi) + if len(clsPath) != 0 { + details.addClasspath(clsPath) + } + } else { + details.addClasspathIfExists(appDir + "/resources") + details.addClasspathIfExists(appDir + "/classes") + details.addJarsToClasspath(appDir + "/classpath") + details.addJarsToClasspath(appDir + "/libs") + } } // Add the Operator Utils jar to the classpath @@ -557,6 +566,23 @@ func createJavaCommand(javaCmd string, details *RunDetails) (*exec.Cmd, error) { return _createJavaCommand(javaCmd, details, args) } +func readFirstLineFromFile(fqfn string, fi os.FileInfo) string { + log.Info(fmt.Sprintf("%s size=%d", fi.Name(), fi.Size())) + file, _ := os.Open(fqfn) + scanner := bufio.NewScanner(file) + scanner.Split(bufio.ScanLines) + var text []string + for scanner.Scan() { + text = append(text, scanner.Text()) + } + file.Close() + if len(text) == 0 { + return "" + } + log.Info(fmt.Sprintf("First Line of the %s:\n%s\n", fi.Name(), text[0])) + return text[0] +} + func createSpringBootCommand(javaCmd string, details *RunDetails) (*exec.Cmd, error) { if details.isBuildPacks() { return _createBuildPackCommand(details, SpringBootMain, details.getSpringBootArgs()) diff --git a/pkg/runner/runner_jvm_jibclasspath_test.go b/pkg/runner/runner_jvm_jibclasspath_test.go new file mode 100644 index 000000000..db795bb24 --- /dev/null +++ b/pkg/runner/runner_jvm_jibclasspath_test.go @@ -0,0 +1,243 @@ +/* + * Copyright (c) 2020, 2021 Oracle and/or its affiliates. + * Licensed under the Universal Permissive License v 1.0 as shown at + * http://oss.oracle.com/licenses/upl. + */ + +package runner + +import ( + "bufio" + "fmt" + . "github.com/onsi/gomega" + coh "github.com/oracle/coherence-operator/api/v1" + metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" + "k8s.io/utils/pointer" + "os" + "testing" +) + +func TestJibClasspath(t *testing.T) { + g := NewGomegaWithT(t) + + d := &coh.Coherence{ + ObjectMeta: metav1.ObjectMeta{Name: "test"}, + Spec: coh.CoherenceResourceSpec{ + JVM: &coh.JVMSpec{ + UseJibClasspath: pointer.BoolPtr(true), + }, + }, + } + + args := []string{"server", "--dry-run"} + env := EnvVarsFromDeployment(d) + + expectedCommand := GetJavaCommand() + expectedArgs := GetMinimalExpectedArgs() + + e, err := ExecuteWithArgs(env, args) + g.Expect(err).NotTo(HaveOccurred()) + g.Expect(e).NotTo(BeNil()) + g.Expect(e.OsCmd).NotTo(BeNil()) + + g.Expect(e.OsCmd.Dir).To(Equal(TestAppDir)) + g.Expect(e.OsCmd.Path).To(Equal(expectedCommand)) + g.Expect(e.OsCmd.Args).To(ConsistOf(expectedArgs)) +} + +func TestJibClasspathFile(t *testing.T) { + g := NewGomegaWithT(t) + + d := &coh.Coherence{ + ObjectMeta: metav1.ObjectMeta{Name: "test"}, + Spec: coh.CoherenceResourceSpec{ + JVM: &coh.JVMSpec{ + UseJibClasspath: pointer.BoolPtr(true), + }, + }, + } + + args := []string{"server", "--dry-run"} + env := EnvVarsFromDeployment(d) + + expectedCommand := GetJavaCommand() + f := createJibClasspathFile() + defer os.Remove(f.Name()) + expectedArgs := GetMinimalExpectedArgsWithAppClasspathFile() + + e, err := ExecuteWithArgs(env, args) + g.Expect(err).NotTo(HaveOccurred()) + g.Expect(e).NotTo(BeNil()) + g.Expect(e.OsCmd).NotTo(BeNil()) + + g.Expect(e.OsCmd.Dir).To(Equal(TestAppDir)) + g.Expect(e.OsCmd.Path).To(Equal(expectedCommand)) + g.Expect(e.OsCmd.Args).To(ConsistOf(expectedArgs)) +} + +func TestJibMainClassFile(t *testing.T) { + g := NewGomegaWithT(t) + + d := &coh.Coherence{ + ObjectMeta: metav1.ObjectMeta{Name: "test"}, + Spec: coh.CoherenceResourceSpec{ + JVM: &coh.JVMSpec{ + UseJibClasspath: pointer.BoolPtr(true), + }, + }, + } + + args := []string{"server", "--dry-run"} + env := EnvVarsFromDeployment(d) + + expectedCommand := GetJavaCommand() + f := createJibMainClassFile() + defer os.Remove(f.Name()) + expectedArgs := GetMinimalExpectedArgsWithAppMainClassFile() + + e, err := ExecuteWithArgs(env, args) + g.Expect(err).NotTo(HaveOccurred()) + g.Expect(e).NotTo(BeNil()) + g.Expect(e.OsCmd).NotTo(BeNil()) + + g.Expect(e.OsCmd.Dir).To(Equal(TestAppDir)) + g.Expect(e.OsCmd.Path).To(Equal(expectedCommand)) + g.Expect(e.OsCmd.Args).To(ConsistOf(expectedArgs)) +} + +func TestJibClasspathFileAndMainClassFile(t *testing.T) { + g := NewGomegaWithT(t) + + d := &coh.Coherence{ + ObjectMeta: metav1.ObjectMeta{Name: "test"}, + Spec: coh.CoherenceResourceSpec{ + JVM: &coh.JVMSpec{ + UseJibClasspath: pointer.BoolPtr(true), + }, + }, + } + + args := []string{"server", "--dry-run"} + env := EnvVarsFromDeployment(d) + + expectedCommand := GetJavaCommand() + f1 := createJibClasspathFile() + defer os.Remove(f1.Name()) + f2 := createJibMainClassFile() + defer os.Remove(f2.Name()) + expectedArgs := GetMinimalExpectedArgsWithAppClasspathFileAndMainClassFile() + + e, err := ExecuteWithArgs(env, args) + g.Expect(err).NotTo(HaveOccurred()) + g.Expect(e).NotTo(BeNil()) + g.Expect(e.OsCmd).NotTo(BeNil()) + + g.Expect(e.OsCmd.Dir).To(Equal(TestAppDir)) + g.Expect(e.OsCmd.Path).To(Equal(expectedCommand)) + g.Expect(e.OsCmd.Args).To(ConsistOf(expectedArgs)) +} + +func createJibClasspathFile() *os.File { + f, err := os.Create(TestAppDir + string(os.PathSeparator) + "jib-classpath-file") + if err != nil { + fmt.Print(err) + os.Exit(1) + } + + _, err = f.WriteString(fmt.Sprintf("%s/classpath/*:%s/libs/*", TestAppDir, TestAppDir)) + if err != nil { + fmt.Print(err) + os.Exit(1) + } + err = f.Close() + if err != nil { + fmt.Print(err) + os.Exit(1) + } + + return f +} + +func createJibMainClassFile() *os.File { + f, err := os.Create(TestAppDir + string(os.PathSeparator) + "jib-main-class-file") + if err != nil { + fmt.Print(err) + os.Exit(1) + } + + _, err = f.WriteString("com.tangosol.net.DefaultCacheServer") + if err != nil { + fmt.Print(err) + os.Exit(1) + } + err = f.Close() + if err != nil { + fmt.Print(err) + os.Exit(1) + } + + return f +} + +func GetMinimalExpectedArgsWithAppClasspathFile() []string { + fileName := fmt.Sprintf("%s/jib-classpath-file", TestAppDir) + cp := readFirstLine(fileName) + args := []string{ + GetJavaCommand(), + "-cp", + cp + ":/coherence-operator/utils/lib/coherence-operator.jar:/coherence-operator/utils/config", + } + + return append(AppendCommonExpectedArgs(args), + "com.oracle.coherence.k8s.Main", + "com.tangosol.net.DefaultCacheServer") +} + +func GetMinimalExpectedArgsWithAppMainClassFile() []string { + cp := fmt.Sprintf("%s/resources:%s/classes:%s/classpath/bar2.JAR:%s/classpath/foo2.jar:%s/libs/bar1.JAR:%s/libs/foo1.jar", + TestAppDir, TestAppDir, TestAppDir, TestAppDir, TestAppDir, TestAppDir) + + args := []string{ + GetJavaCommand(), + "-cp", + cp + ":/coherence-operator/utils/lib/coherence-operator.jar:/coherence-operator/utils/config", + } + + fileName := fmt.Sprintf("%s/jib-main-class-file", TestAppDir) + mainCls := readFirstLine(fileName) + return append(AppendCommonExpectedArgs(args), + "com.oracle.coherence.k8s.Main", + mainCls) +} + +func GetMinimalExpectedArgsWithAppClasspathFileAndMainClassFile() []string { + fileName := fmt.Sprintf("%s/jib-classpath-file", TestAppDir) + cp := readFirstLine(fileName) + + args := []string{ + GetJavaCommand(), + "-cp", + cp + ":/coherence-operator/utils/lib/coherence-operator.jar:/coherence-operator/utils/config", + } + + fileName = fmt.Sprintf("%s/jib-main-class-file", TestAppDir) + mainCls := readFirstLine(fileName) + return append(AppendCommonExpectedArgs(args), + "com.oracle.coherence.k8s.Main", + mainCls) +} + +func readFirstLine(fqfn string) string { + file, _ := os.Open(fqfn) + scanner := bufio.NewScanner(file) + scanner.Split(bufio.ScanLines) + var text []string + for scanner.Scan() { + text = append(text, scanner.Text()) + } + file.Close() + if len(text) == 0 { + return "" + } + return text[0] +}