Gradle execute commandLine with dynamic list of parameters - gradle

We have an application that one can run either locally or on a remote server. When run locally, one needs to set proxies as the app uses another service on the cloud.
This is done like that:
commandLine "java", "-Xmx227m",
"-Dapplication.name=showcase-rest",
"-Dserver.port=$applicationPort",
"-Dspring.profiles.active=$springActiveProfiles",
"-Didm.realm=develop",
"-Dhttp.proxyHost=10.xx.xxx.129", "-Dhttp.proxyPort=3xxx",
"-Dhttps.proxyHost=10.xx.xxx.129", "-Dhttps.proxyPort=3xxx",
"-Dhttp.nonProxyHosts=localhost|127.0.0.1",
"-jar", tasks.jar.destinationDir.toString() + "/showcase-rest-SNAPSHOT.jar"
However, when run on proper server on the cloud, we don't need the proxy settings:
commandLine "java", "-Xmx227m",
"-Dapplication.name=showcase-rest",
"-Dserver.port=$applicationPort",
"-Dspring.profiles.active=$springActiveProfiles",
"-Didm.realm=develop",
"-jar", tasks.jar.destinationDir.toString() + "/showcase-rest-SNAPSHOT.jar"
How that could be achieved?
I know how to provide and parse jvm arguments to gradle; the question is how can I "inject" these proxy settings in the commandLine dynamically.
So far I tried:
def proxyConfig = ["-Dhttp.proxyHost=10.xx.xxx.129", "-Dhttp.proxyPort=3xxx",
"-Dhttps.proxyHost=10.xx.xxx.129", "-Dhttps.proxyPort=3xxx",
"-Dhttp.nonProxyHosts=localhost|127.0.0.1"] as List<String>
commandLine "java", "-Xmx227m",
"-Dapplication.name=showcase-rest",
"-Dserver.port=$applicationPort",
"-Dspring.profiles.active=$springActiveProfiles",
"-Didm.realm=develop",
proxyConfig,
"-jar", tasks.jar.destinationDir.toString() + "/showcase-rest-SNAPSHOT.jar"
But obviously that doesn't work.

Try this :
def params = ["java", "-Xmx227m",
"-Dapplication.name=showcase-rest",
"-Dserver.port=$applicationPort",
"-Dspring.profiles.active=$springActiveProfiles",
"-Didm.realm=develop",
"-Dhttp.proxyHost=10.xx.xxx.129", "-Dhttp.proxyPort=3xxx",
"-Dhttps.proxyHost=10.xx.xxx.129", "-Dhttps.proxyPort=3xxx",
"-Dhttp.nonProxyHosts=localhost|127.0.0.1",
"-jar", tasks.jar.destinationDir.toString() + "/showcase-rest-SNAPSHOT.jar"]
def withProxy = true
commandLine (*(params.findAll { withProxy || !it.toLowerCase().contains('proxy') }))

Thank you ToYonos. My solution, not as groovy-ninja, is below:
def proxyConfig = ["-Dhttp.proxyHost=10.xx.xxx.129",
"-Dhttp.proxyPort=3xxx",
"-Dhttps.proxyHost=10.xx.xxx.129",
"-Dhttps.proxyPort=3xxx",
"-Dhttp.nonProxyHosts=localhost|127.0.0.1"]
if (springActiveProfiles == 'cicd') {
proxyConfig = []
}
println("proxy config: $proxyConfig")
def args = ["java", "-Xmx227m",
"-Dapplication.name=showcase-rest",
"-Dserver.port=$applicationPort",
"-Dspring.profiles.active=$springActiveProfiles",
"-Didm.realm=develop"]
def jarArg = ["-jar", tasks.jar.destinationDir.toString() + "/showcase-rest-SNAPSHOT.jar"]
args.addAll(proxyConfig)
args.addAll(jarArg)
println("Running with arguments: $args")
commandLine args

Related

Terraform execute script before lambda creation

I have a terraform configuration that correctly creates a lambda function on aws with a zip file provided.
My problem is that I always have to package the lambda first (I use serverless package method for this), so I would like to execute a script that package my function and move the zip to the right directory before terraform creates the lambda function.
Is that possible? Maybe using a combination of null_resource and local-exec?
You already proposed the best answer :)
When you add a depends_on = ["null_resource.serverless_execution"] to your lambda resource, you can ensure, that packaging will be done before uploading the zip file.
Example:
resource "null_resource" "serverless_execution" {
provisioner "local-exec" {
command = "serverless package ..."
}
}
resource "aws_lambda_function" "update_lambda" {
depends_on = ["null_resource.serverless_execution"]
filename = "${path.module}/path/to/package.zip"
[...]
}
https://www.terraform.io/docs/provisioners/local-exec.html
The answer is already given, but I was looking for a way to install NPM modules on the fly, zip and then deploy Lambda function along with timeout if your lambda function size is large. So here is my finding may help someone else.
#Install NPM module before creating ZIP
resource "null_resource" "npm" {
provisioner "local-exec" {
command = "cd ../lambda-functions/loadbalancer-to-es/ && npm install --prod=only"
}
}
# Zip the Lamda function on the fly
data "archive_file" "source" {
type = "zip"
source_dir = "../lambda-functions/loadbalancer-to-es"
output_path = "../lambda-functions/loadbalancer-to-es.zip"
depends_on = ["null_resource.npm"]
}
# Created AWS Lamdba Function: Memory Size, NodeJS version, handler, endpoint, doctype and environment settings
resource "aws_lambda_function" "elb_logs_to_elasticsearch" {
filename = "${data.archive_file.source.output_path}"
function_name = "someprefix-alb-logs-to-elk"
description = "elb-logs-to-elasticsearch"
memory_size = 1024
timeout = 900
timeouts {
create = "30m"
}
runtime = "nodejs8.10"
role = "${aws_iam_role.role.arn}"
source_code_hash = "${base64sha256(data.archive_file.source.output_path)}"
handler = "index.handler"
# source_code_hash = "${base64sha256(file("/elb-logs-to-elasticsearch.zip"))}"
environment {
variables = {
ELK_ENDPOINT = "someprefix-elk.dns.co"
ELK_INDEX = "test-web-server-"
ELK_REGION = "us-west-2"
ELK_DOCKTYPE = "elb-access-logs"
}
}
}

How should I run a mysql command with nomad and arguments?

I am interested in using nomad to initialize a mysql database on a windows server that is without docker. I tried to create a job using the "exec" driver, "mysql" command, and an args argument with the host, username, password, etc.
job "gavin-setup" {
datacenters = ["dc1"]
group "gavin-setup-group" {
task "setup" {
driver = "exec"
config = {
command = "mysql"
args = [
"-hlocalhost",
"-ugavin",
"-pgavin_secret",
"gavin_database",
"<",
"C:\\gavin\\config\\create.sql"
]
}
}
}
}
The arguments aren't being passed along. I also tried removing the args and just using the command which did not work:
mysql -hlocalhost -ugavin -pgavin_secret gavin_database < C:\\gavin\\config\\create.sql
Is it possible to do this kind of database initialization setup on a first-time run of the application? Should nomad be capable of doing this? If not - should I be using some other process to do this kind of setup?
I ended up using the full path to the mysql.exe and discovered that nomad logs -stderr <assoc-id> was really helpful.
job "gavin-setup" {
datacenters = ["dc1"]
group "gavin-setup-group" {
task "create_db" {
driver = "exec"
config = {
command = "C:/Program Files (x86)/MySQL/MySQL Server 5.6/bin/mysql.exe"
args = [
"-uroot",
"-pgavin_secret",
"-e",
"create database if not exists gavin_db;"
]
}
}
}
}

Why doesn't gradle exec work with scripts?

Is there any way to make gradle exec work like a shell exec? ie - understand executable files in the path?
We have code that needs to work on windows and unix - and many many scripts that are obviously different on both machines. While I can do a hack like this:
npmCommand = Os.isFamily(Os.FAMILY_WINDOWS) ? 'npm.cmd' : '/usr/local/bin/npm'
and then run that command - the paths for some scripts are not necessarily set in stone - and it's just horrible code.
Is there any way to fix the problem - ie extend the exec task to find executable files in the path and run them?
How about something like:
if (System.getProperty('os.name').toLowerCase(Locale.ROOT).contains('windows'))
{
commandLine 'cmd', '/c', 'commandGoesHere'
}
else
{
commandLine 'sh', '-c', 'commandGoesHere'
}
I'd love a better way - but I made a simple function that I put in our common that you use like this:
exec {
commandLine command("npm"), "install"
}
the function looks like this:
//
// find a command with this name in the path
//
String command( String name )
{
def onWindows = (System.env.PATH==null);
def pathBits = onWindows ? System.env.Path.split(";") : System.env.PATH.split(":");
def isMatch = onWindows ? {path ->
for (String extension : System.env.PATHEXT.split(";"))
{
File theFile = new File( path, name + extension);
if (theFile.exists())
return theFile;
}
return null;
} : {path -> def file = new File(path,name);if (file.exists() && file.canExecute()) return file;return null;}
def foundLocal = isMatch(file("."));
if (foundLocal)
return foundLocal;
for (String pathBit : pathBits)
{
def found = isMatch(pathBit);
if (found)
return found;
}
throw new RuntimeException("Failed to find " + name + " in the path")
}

URISyntaxException in gradle environment variable while testing

I have a build.gradle as follows
task setDockerHost {
group 'Docker'
description 'Gets the docker host ip from your OS'
def stdout = new ByteArrayOutputStream()
exec {
commandLine './src/main/resources/scripts/get-docker-host.sh', '60'
standardOutput = stdout
}
project.ext.set('DOCKERHOST', "$stdout")
}
tasks.withType(Test) {
doFirst { println "DockerHost is $project.DOCKERHOST" }
environment 'DOCKERHOST', "$project.DOCKERHOST"
outputs.upToDateWhen { false }
testLogging {
events 'passed', 'skipped', 'failed', 'standardOut'
}
reports.html.destination = file("${reporting.baseDir}/${name}")
}
I define a DOCKERHOST env variable as above and want to use in my groovy test class:
class MyClass extends Specification {
RESTClient client = new RESTClient("http://"+System.getenv('DOCKERHOST')+":9090/")
...
}
In the execution my println works: DockerHost is 192.168.99.100
But when I run the this test it throws:
I already tried replacing \n, \r and spaces by "". I also try removing the protocol from the URL (aka 192.168.99.10:9090) and it tells me that the same error occurs at index 0
How can I solve this?
I didn't figure it out in which char was the problem but I was able to solve it but replacing strings like crazy:
String url = ("http://" + System.getenv('DOCKERHOST') + ":9090/").replace("\n", "").replace("\r", "").replace(" ", "")
RESTClient client = new RESTClient(url)
I've spent like a day trying to figure this out... hopefully for someone else will be quicker.

platform independent symlinks in build script

I need to add to gradle build script a command that will create a symlink. I know that the person who builds that has cygwin installed. The problem is with export command. Here is what I got so far
if(OS == 'win32') {
exec { commandLine "C:\\cygwin\\bin\\mintty.exe", "--hold always", "/bin/bash", "-l", "-e", "export", "CYGWIN=winsymlinks", "&&", "-e", "ln", "-s", link, file}
//exec { commandLine "cmd", "/c", "mklink", link, file}
//exec { commandLine "export", "CYGWIN=winsymlinks" }
//exec { commandLine "C:\\cygwin\\bin\\ln.exe" , "-s", link, file}
}
else {
exec { commandLine "ln", "-s", link, file}
}
Is there a standard way of doing it?
Gradle doesn't offer a public API for creating symlinks. If your builds run under JDK 7 or higher, you could try the JDK's symlink API. Otherwise, it should be possible to solve this with exec as well, as long as you can figure out the correct command.
The Ant builder, provided by every task has one. Seems like a solution, but as pointed out in the comments is not really cross platform:
task createLink << {
ant.symlink(resource: "file", link: "link")
}
Instead you could call the NIO API in Java, but you will need 1.7. Take a look at the
createSymbolicLink
Example of Files.createSymbolicLink from build.gradle:
File devDir = new File("${project.rootDir}/.dev")
File configDir = new File("${project.projectDir}/config")
Files.createSymbolicLink(devLink.toPath(), devDir.toPath())
In the special case where your aim is to create a symlink to an executable file, there is a cross platform workaround, which is to create a shell script that will forward to the target :
File forwarding_script_template:
#! /bin/bash
_DIR_=$(dirname "$'{'BASH_SOURCE[0]'}'")
"$_DIR_/"{0} "$#"
In Gradle build file:
String templateScript = new File(projectDir,
"gradle/forwarding_script_template.sh").text;
String script = MessageFormat.format(templateScript, target);
writeFile(destination, script);
destination.setExecutable(true);
with the writeFile method equal to:
void writeFile(File destination, String content) {
Writer writer = new FileWriter(destination);
writer.write(content);
writer.close();
}

Resources