diff --git a/src/com/sap/piper/FileUtils.groovy b/src/com/sap/piper/FileUtils.groovy index 5f62d9987..1d6b3c24e 100644 --- a/src/com/sap/piper/FileUtils.groovy +++ b/src/com/sap/piper/FileUtils.groovy @@ -18,4 +18,10 @@ class FileUtils implements Serializable { def file = new File(dir) if (file.list().size() == 0) throw new AbortException("'${file.getAbsolutePath()}' is empty.") } + + static validateFile(filePath) { + if (!filePath) throw new IllegalArgumentException("The parameter 'filePath' can not be null or empty.") + def file = new File(filePath) + if (!file.exists()) throw new AbortException("'${file.getAbsolutePath()}' does not exist.") + } } diff --git a/src/com/sap/piper/tools/Tool.groovy b/src/com/sap/piper/tools/Tool.groovy new file mode 100644 index 000000000..f981b0ad0 --- /dev/null +++ b/src/com/sap/piper/tools/Tool.groovy @@ -0,0 +1,23 @@ +package com.sap.piper.tools + + +class Tool implements Serializable { + + final name + final environmentKey + final stepConfigurationKey + final executablePath + final executableName + final version + final versionOption + + Tool(name, environmentKey, stepConfigurationKey, executablePath, executableName, version, versionOption) { + this.name = name + this.environmentKey = environmentKey + this.stepConfigurationKey = stepConfigurationKey + this.executablePath = executablePath + this.executableName = executableName + this.version = version + this.versionOption = versionOption + } +} diff --git a/src/com/sap/piper/tools/ToolUtils.groovy b/src/com/sap/piper/tools/ToolUtils.groovy new file mode 100644 index 000000000..4817fa9fd --- /dev/null +++ b/src/com/sap/piper/tools/ToolUtils.groovy @@ -0,0 +1,43 @@ +package com.sap.piper.tools + + +class ToolUtils implements Serializable { + + def static getToolHome(tool, script, configuration, environment) { + + def home + if (environment?."$tool.environmentKey") { + home = environment."$tool.environmentKey" + script.echo "$tool.name home '$home' retrieved from environment." + } + else if (configuration.containsKey(tool.stepConfigurationKey)) { + home = configuration.get(tool.stepConfigurationKey) + script.echo "$tool.name home '$home' retrieved from configuration." + } else { + home = '' + script.echo "$tool.name expected on PATH." + } + return home + } + + def static getToolExecutable(tool, script, configuration, environment) { + + def home = getToolHome(tool, script, configuration, environment) + return getToolExecutable(tool, script, home) + } + + def static getToolExecutable(tool, script, home) { + + def path = "$tool.executablePath" + def executable = "$tool.executableName" + def toolExecutable + + if (home) { + toolExecutable = "$home$path$executable" + } else { + toolExecutable = "$executable" + } + script.echo "Using $tool.name executable '$toolExecutable'." + return toolExecutable + } +} diff --git a/src/com/sap/piper/tools/ToolVerifier.groovy b/src/com/sap/piper/tools/ToolVerifier.groovy new file mode 100644 index 000000000..f37c024cf --- /dev/null +++ b/src/com/sap/piper/tools/ToolVerifier.groovy @@ -0,0 +1,57 @@ +package com.sap.piper.tools + +import com.sap.piper.FileUtils +import com.sap.piper.Version +import com.sap.piper.tools.ToolUtils + +import hudson.AbortException + + +class ToolVerifier implements Serializable { + + def static verifyToolHome(tool, script, configuration, environment) { + + def home = ToolUtils.getToolHome(tool, script, configuration, environment) + if (home) { + script.echo "Verifying $tool.name home '$home'." + FileUtils.validateDirectoryIsNotEmpty(script, home) + script.echo "Verification success. $tool.name home '$home' exists and it is not empty." + } + return home + } + + def static verifyToolExecutable(tool, script, configuration, environment) { + + def home = verifyToolHome(tool, script, configuration, environment) + def executable = ToolUtils.getToolExecutable(tool, script, home) + if (home) { + script.echo "Verifying $tool.name executable." + FileUtils.validateFile(executable) + } + return executable + } + + def static verifyToolVersion(tool, script, configuration, environment) { + + def executable = ToolUtils.getToolExecutable(tool, script, configuration, environment) + if (tool.name == 'SAP Multitarget Application Archive Builder'){ + def java = new Tool('Java', 'JAVA_HOME', '', '/bin/', 'java', '1.8.0', '-version 2>&1') + def javaExecutable = ToolUtils.getToolExecutable(java, script, configuration, environment) + executable = "$javaExecutable -jar $executable" + } + + script.echo "Verifying $tool.name version $tool.version or compatible version." + + def toolVersion + try { + toolVersion = script.sh returnStdout: true, script: "$executable $tool.versionOption" + } catch(AbortException e) { + throw new AbortException("The verification of $tool.name failed. Please check '$executable'. $e.message.") + } + def version = new Version(toolVersion) + if (!version.isCompatibleVersion(new Version(tool.version))) { + throw new AbortException("The installed version of $tool.name is ${version.toString()}. Please install version $tool.version or a compatible version.") + } + script.echo "$tool.name version ${version.toString()} is installed." + } +} diff --git a/test/groovy/com/sap/piper/FileUtilsTest.groovy b/test/groovy/com/sap/piper/FileUtilsTest.groovy index 63a8395a7..70ec9982b 100644 --- a/test/groovy/com/sap/piper/FileUtilsTest.groovy +++ b/test/groovy/com/sap/piper/FileUtilsTest.groovy @@ -1,15 +1,10 @@ package com.sap.piper -import org.junit.ClassRule -import org.junit.BeforeClass -import org.junit.Rule -import org.junit.Test -import org.junit.rules.ExpectedException -import org.junit.rules.TemporaryFolder - -import hudson.AbortException +import org.junit.* +import org.junit.rules.* import com.sap.piper.FileUtils +import hudson.AbortException class FileUtilsTest { @@ -91,5 +86,40 @@ class FileUtilsTest { FileUtils.validateDirectoryIsNotEmpty(notEmptyDir) } + + @Test + void validateFileNoFilePathTest() { + + thrown.expect(IllegalArgumentException) + thrown.expectMessage("The parameter 'filePath' can not be null or empty.") + + FileUtils.validateFile(null) + } + + @Test + void validateFileEmptyFilePathTest() { + + thrown.expect(IllegalArgumentException) + thrown.expectMessage("The parameter 'filePath' can not be null or empty.") + + FileUtils.validateFile('') + } + + @Test + void validateFileDoesNotExistFileTest() { + + def path = new File("$emptyDir", 'test').getAbsolutePath() + + thrown.expect(AbortException) + thrown.expectMessage("'$path' does not exist.") + + FileUtils.validateFile(path) + } + + @Test + void validateFileTest() { + + FileUtils.validateFile(file) + } } diff --git a/test/groovy/com/sap/piper/tools/ToolUtilsTest.groovy b/test/groovy/com/sap/piper/tools/ToolUtilsTest.groovy new file mode 100644 index 000000000..5d8e235e2 --- /dev/null +++ b/test/groovy/com/sap/piper/tools/ToolUtilsTest.groovy @@ -0,0 +1,168 @@ +package com.sap.piper.tools + +import org.junit.BeforeClass +import org.junit.Before +import org.junit.Rule +import org.junit.Test +import org.junit.rules.ExpectedException +import org.junit.rules.RuleChain + +import util.JenkinsLoggingRule +import util.Rules + +import com.lesfurets.jenkins.unit.BasePipelineTest + +import com.sap.piper.tools.Tool +import com.sap.piper.tools.ToolUtils + + +class ToolUtilsTest extends BasePipelineTest { + + private ExpectedException thrown = new ExpectedException() + private JenkinsLoggingRule jlr = new JenkinsLoggingRule(this) + + @Rule + public RuleChain rules = Rules.getCommonRules(this) + .around(thrown) + .around(jlr) + + private cpe + + private static java + private static mta + private static neo + private static cmCli + + + @BeforeClass + static void createTools() { + + java = new Tool('Java', 'JAVA_HOME', '', '/bin/', 'java', '1.8.0', '-version 2>&1') + mta = new Tool('SAP Multitarget Application Archive Builder', 'MTA_JAR_LOCATION', 'mtaJarLocation', '/', 'mta.jar', '1.0.6', '-v') + neo = new Tool('SAP Cloud Platform Console Client', 'NEO_HOME', 'neoHome', '/tools/', 'neo.sh', '3.39.10', 'version') + cmCli = new Tool('Change Management Command Line Interface', 'CM_CLI_HOME', 'cmCliHome', '/bin/', 'cmclient', '0.0.1', '-v') + } + + @Before + void setup() { + + cpe = loadScript('commonPipelineEnvironment.groovy').commonPipelineEnvironment + } + + @Test + void getMtaJarFromConfigurationTest() { + + def environment = [:] + def configuration = [mtaJarLocation: '/config/mta'] + + def mtaJar = ToolUtils.getToolExecutable(mta, cpe, configuration, environment) + + assert mtaJar == '/config/mta/mta.jar' + assert jlr.log.contains("$mta.name home '/config/mta' retrieved from configuration.") + assert jlr.log.contains("Using $mta.name executable '/config/mta/mta.jar'.") + } + + @Test + void getMtaJarFromEnvironmentTest() { + + def environment = [MTA_JAR_LOCATION: '/env/mta'] + def configuration = [mtaJarLocation: '/config/mta'] + + def mtaJar = ToolUtils.getToolExecutable(mta, cpe, configuration, environment) + + assert mtaJar == '/env/mta/mta.jar' + assert jlr.log.contains("$mta.name home '/env/mta' retrieved from environment.") + assert jlr.log.contains("Using $mta.name executable '/env/mta/mta.jar'.") + } + + @Test + void getMtaJarFromCurrentWorkingDirectoryTest() { + + def environment = [:] + def configuration = [:] + + def mtaJar = ToolUtils.getToolExecutable(mta, cpe, configuration, environment) + + assert mtaJar == 'mta.jar' + assert jlr.log.contains("$mta.name expected on PATH.") + assert jlr.log.contains("Using $mta.name executable 'mta.jar'.") + } + + @Test + void getNeoExecutableFromConfigurationTest() { + + def environment = [:] + def configuration = [neoHome: '/config/neo'] + + def neoExecutable = ToolUtils.getToolExecutable(neo, cpe, configuration, environment) + + assert neoExecutable == '/config/neo/tools/neo.sh' + assert jlr.log.contains("$neo.name home '/config/neo' retrieved from configuration.") + assert jlr.log.contains("Using $neo.name executable '/config/neo/tools/neo.sh'.") + } + + @Test + void getNeoExecutableFromEnvironmentTest() { + + def environment = [NEO_HOME: '/env/neo'] + def configuration = [neoHome: '/config/neo'] + + def neoExecutable = ToolUtils.getToolExecutable(neo, cpe, configuration, environment) + + assert neoExecutable == '/env/neo/tools/neo.sh' + assert jlr.log.contains("$neo.name home '/env/neo' retrieved from environment.") + assert jlr.log.contains("Using $neo.name executable '/env/neo/tools/neo.sh'.") + } + + @Test + void getNeoExecutableFromCurrentWorkingDirectoryTest() { + + def environment = [:] + def configuration = [:] + + def neoExecutable = ToolUtils.getToolExecutable(neo, cpe, configuration, environment) + + assert neoExecutable == 'neo.sh' + assert jlr.log.contains("$neo.name expected on PATH.") + assert jlr.log.contains("Using $neo.name executable 'neo.sh'.") + } + + @Test + void getCmCliExecutableFromConfigurationTest() { + + def environment = [:] + def configuration = [cmCliHome: '/config/cmclient'] + + def cmCliExecutable = ToolUtils.getToolExecutable(cmCli, cpe, configuration, environment) + + assert cmCliExecutable == '/config/cmclient/bin/cmclient' + assert jlr.log.contains("$cmCli.name home '/config/cmclient' retrieved from configuration.") + assert jlr.log.contains("Using $cmCli.name executable '/config/cmclient/bin/cmclient'.") + } + + @Test + void getCmCliExecutableFromEnvironmentTest() { + + def environment = [CM_CLI_HOME: '/env/cmclient'] + def configuration = [cmCliHome: '/config/cmclient'] + + def cmCliExecutable = ToolUtils.getToolExecutable(cmCli, cpe, configuration, environment) + + assert cmCliExecutable == '/env/cmclient/bin/cmclient' + assert jlr.log.contains("$cmCli.name home '/env/cmclient' retrieved from environment.") + assert jlr.log.contains("Using $cmCli.name executable '/env/cmclient/bin/cmclient'.") + } + + @Test + void getCmCliExecutableFromCurrentWorkingDirectoryTest() { + + def environment = [:] + def configuration = [:] + + def cmCliExecutable = ToolUtils.getToolExecutable(cmCli, cpe, configuration, environment) + + assert cmCliExecutable == 'cmclient' + assert jlr.log.contains("$cmCli.name expected on PATH.") + assert jlr.log.contains("Using $cmCli.name executable 'cmclient'.") + } +} diff --git a/test/groovy/com/sap/piper/tools/ToolVerifierTest.groovy b/test/groovy/com/sap/piper/tools/ToolVerifierTest.groovy new file mode 100644 index 000000000..110f72ccc --- /dev/null +++ b/test/groovy/com/sap/piper/tools/ToolVerifierTest.groovy @@ -0,0 +1,257 @@ +package com.sap.piper.tools + +import org.junit.BeforeClass +import org.junit.ClassRule +import org.junit.Before +import org.junit.Rule +import org.junit.Test +import org.junit.rules.ExpectedException +import org.junit.rules.TemporaryFolder +import org.junit.rules.RuleChain + +import util.JenkinsLoggingRule +import util.Rules + +import com.lesfurets.jenkins.unit.BasePipelineTest + +import com.sap.piper.tools.Tool +import com.sap.piper.tools.ToolVerifier + +import hudson.AbortException + + +class ToolVerifierTest extends BasePipelineTest { + + @ClassRule + public static TemporaryFolder tmp = new TemporaryFolder() + + private ExpectedException thrown = new ExpectedException().none() + private JenkinsLoggingRule jlr = new JenkinsLoggingRule(this) + + @Rule + public RuleChain ruleChain = Rules.getCommonRules(this) + .around(thrown) + .around(jlr) + + private static home + private static mtaJar + private static neoExecutable + private static cmCliExecutable + + private script + private static stepName + private static configuration + private static environment + + private static java + private static mta + private static neo + private static cmCli + + + @BeforeClass + static void createTestFiles() { + + home = "${tmp.getRoot()}" + tmp.newFolder('bin') + tmp.newFolder('bin', 'java') + tmp.newFile('mta.jar') + tmp.newFolder('tools') + tmp.newFile('tools/neo.sh') + tmp.newFile('bin/cmclient') + + mtaJar = "$home/mta.jar" + neoExecutable = "$home/tools/neo.sh" + cmCliExecutable = "$home/bin/cmclient" + + java = new Tool('Java', 'JAVA_HOME', '', '/bin/', 'java', '1.8.0', '-version 2>&1') + mta = new Tool('SAP Multitarget Application Archive Builder', 'MTA_JAR_LOCATION', 'mtaJarLocation', '/', 'mta.jar', '1.0.6', '-v') + neo = new Tool('SAP Cloud Platform Console Client', 'NEO_HOME', 'neoHome', '/tools/', 'neo.sh', '3.39.10', 'version') + cmCli = new Tool('Change Management Command Line Interface', 'CM_CLI_HOME', 'cmCliHome', '/bin/', 'cmclient', '0.0.1', '-v') + + configuration = [mtaJarLocation: home, neoHome: home, cmCliHome: home] + environment = [JAVA_HOME: home] + } + + @Before + void init() { + + script = loadScript('commonPipelineEnvironment.groovy').commonPipelineEnvironment + } + + + @Test + void unableToVerifyJavaTest() { + + thrown.expect(AbortException) + thrown.expectMessage('The verification of Java failed.') + + helper.registerAllowedMethod('sh', [Map], { Map m -> getNoVersion(m) }) + + ToolVerifier.verifyToolVersion(java, script, configuration, environment) + } + + @Test + void unableToVerifyMtaTest() { + + thrown.expect(AbortException) + thrown.expectMessage('The verification of SAP Multitarget Application Archive Builder failed.') + + helper.registerAllowedMethod('sh', [Map], { Map m -> getNoVersion(m) }) + + ToolVerifier.verifyToolVersion(mta, script, configuration, environment) + } + + @Test + void unableToVerifyNeoTest() { + + thrown.expect(AbortException) + thrown.expectMessage('The verification of SAP Cloud Platform Console Client failed.') + + helper.registerAllowedMethod('sh', [Map], { Map m -> getNoVersion(m) }) + + ToolVerifier.verifyToolVersion(neo, script, configuration, environment) + } + + @Test + void unableToVefifyCmTest() { + + thrown.expect(AbortException) + thrown.expectMessage('The verification of Change Management Command Line Interface failed.') + + helper.registerAllowedMethod('sh', [Map], { Map m -> getNoVersion(m) }) + + ToolVerifier.verifyToolVersion(cmCli, script, configuration, environment) + + script.execute() + } + + @Test + void verifyIncompatibleVersionJavaTest() { + + thrown.expect(AbortException) + thrown.expectMessage('The installed version of Java is 1.7.0.') + + helper.registerAllowedMethod('sh', [Map], { Map m -> getIncompatibleVersion(m) }) + + ToolVerifier.verifyToolVersion(java, script, configuration, environment) + } + + @Test + void verifyIncompatibleVersionMtaTest() { + + thrown.expect(AbortException) + thrown.expectMessage('The installed version of SAP Multitarget Application Archive Builder is 1.0.5.') + + helper.registerAllowedMethod('sh', [Map], { Map m -> getIncompatibleVersion(m) }) + + ToolVerifier.verifyToolVersion(mta, script, configuration, environment) + } + + @Test + void verifyNeoIncompatibleVersionTest() { + + thrown.expect(AbortException) + thrown.expectMessage('The installed version of SAP Cloud Platform Console Client is 1.126.51.') + + helper.registerAllowedMethod('sh', [Map], { Map m -> getIncompatibleVersion(m) }) + + ToolVerifier.verifyToolVersion(neo, script, configuration, environment) + } + + @Test + void verifyCmIncompatibleVersionTest() { + + thrown.expect(AbortException) + thrown.expectMessage('The installed version of Change Management Command Line Interface is 0.0.0.') + + helper.registerAllowedMethod('sh', [Map], { Map m -> getIncompatibleVersion(m) }) + binding.setVariable('tool', 'cm') + + ToolVerifier.verifyToolVersion(cmCli, script, configuration, environment) + } + + @Test + void verifyJavaTest() { + + helper.registerAllowedMethod('sh', [Map], { Map m -> getVersion(m) }) + + ToolVerifier.verifyToolVersion(java, script, configuration, environment) + + assert jlr.log.contains('Verifying Java version 1.8.0 or compatible version.') + assert jlr.log.contains('Java version 1.8.0 is installed.') + } + + @Test + void verifyMtaTest() { + + helper.registerAllowedMethod('sh', [Map], { Map m -> getVersion(m) }) + + ToolVerifier.verifyToolVersion(mta, script, configuration, environment) + + assert jlr.log.contains('Verifying SAP Multitarget Application Archive Builder version 1.0.6 or compatible version.') + assert jlr.log.contains('SAP Multitarget Application Archive Builder version 1.0.6 is installed.') + } + + @Test + void verifyNeoTest() { + + helper.registerAllowedMethod('sh', [Map], { Map m -> getVersion(m) }) + + ToolVerifier.verifyToolVersion(neo, script, configuration, environment) + + assert jlr.log.contains('Verifying SAP Cloud Platform Console Client version 3.39.10 or compatible version.') + assert jlr.log.contains('SAP Cloud Platform Console Client version 3.39.10 is installed.') + } + + @Test + void verifyCmTest() { + + helper.registerAllowedMethod('sh', [Map], { Map m -> getVersion(m) }) + + ToolVerifier.verifyToolVersion(cmCli, script, configuration, environment) + + assert jlr.log.contains('Verifying Change Management Command Line Interface version 0.0.1 or compatible version.') + assert jlr.log.contains('Change Management Command Line Interface version 0.0.1 is installed.') + } + + + private getNoVersion(Map m) { + throw new AbortException('script returned exit code 127') + } + + private getVersion(Map m) { + + if(m.script.contains('java -version')) { + return '''openjdk version \"1.8.0_121\" + OpenJDK Runtime Environment (build 1.8.0_121-8u121-b13-1~bpo8+1-b13) + OpenJDK 64-Bit Server VM (build 25.121-b13, mixed mode)''' + } else if(m.script.contains('mta.jar -v')) { + return '1.0.6' + } else if(m.script.contains('neo.sh version')) { + return '''SAP Cloud Platform Console Client + SDK version : 3.39.10 + Runtime : neo-java-web''' + } else if(m.script.contains('cmclient -v')) { + return '0.0.1-beta-2 : fc9729964a6acf5c1cad9c6f9cd6469727625a8e' + } + } + + private getIncompatibleVersion(Map m) { + + if(m.script.contains('java -version')) { + return '''openjdk version \"1.7.0_121\" + OpenJDK Runtime Environment (build 1.7.0_121-8u121-b13-1~bpo8+1-b13) + OpenJDK 64-Bit Server VM (build 25.121-b13, mixed mode)''' + } else if(m.script.contains('mta.jar -v')) { + return '1.0.5' + } else if(m.script.contains('neo.sh version')) { + return '''SAP Cloud Platform Console Client + SDK version : 1.126.51 + Runtime : neo-java-web''' + } else if(m.script.contains('cmclient -v')) { + return '0.0.0-beta-1 : fc9729964a6acf5c1cad9c6f9cd6469727625a8e' + } + } +} +