Skip to content

Commit

Permalink
Add --config-file to import config json file (#13)
Browse files Browse the repository at this point in the history

Signed-off-by: Paolo Di Tommaso <[email protected]>
Co-authored-by: Paolo Di Tommaso <[email protected]>
  • Loading branch information
munishchouhan and pditommaso committed Aug 12, 2023
1 parent ae482d3 commit a212bb3
Show file tree
Hide file tree
Showing 6 changed files with 275 additions and 125 deletions.
1 change: 1 addition & 0 deletions app/build.gradle
Original file line number Diff line number Diff line change
Expand Up @@ -37,6 +37,7 @@ dependencies {
testImplementation ("org.objenesis:objenesis:3.2")
testImplementation ("org.spockframework:spock-core:2.3-groovy-3.0") { exclude group: 'org.codehaus.groovy'; exclude group: 'net.bytebuddy' }
testImplementation ('org.spockframework:spock-junit4:2.3-groovy-3.0') { exclude group: 'org.codehaus.groovy'; exclude group: 'net.bytebuddy' }
testImplementation 'com.github.tomakehurst:wiremock:2.27.2'
}

test {
Expand Down
41 changes: 37 additions & 4 deletions app/src/main/java/io/seqera/wavelit/App.java
Original file line number Diff line number Diff line change
Expand Up @@ -16,8 +16,10 @@

import java.io.FileNotFoundException;
import java.io.IOException;
import java.io.InputStream;
import java.net.URI;
import java.net.URISyntaxException;
import java.net.URL;
import java.nio.file.Files;
import java.nio.file.NoSuchFileException;
import java.nio.file.Path;
Expand Down Expand Up @@ -47,12 +49,11 @@
import picocli.CommandLine;
import static io.seqera.wave.util.DockerHelper.addPackagesToSpackFile;
import static io.seqera.wave.util.DockerHelper.spackPackagesToSpackFile;
import static io.seqera.wavelit.util.Checkers.isEnvVar;
import static org.apache.commons.lang3.StringUtils.isEmpty;
import static picocli.CommandLine.Command;
import static picocli.CommandLine.Option;

import static io.seqera.wavelit.util.Checkers.isEnvVar;

/**
* Wavelit main class
*/
Expand Down Expand Up @@ -111,6 +112,9 @@ public class App implements Runnable {
@Option(names = {"--config-entrypoint"}, paramLabel = "''", description = "Overwrite the default ENTRYPOINT of the image.")
private String entrypoint;

@Option(names = {"--config-file"}, paramLabel = "''", description = "Configuration file in JSON format to overwrite the default configuration of the image.")
private String configFile;

@Option(names = {"--config-working-dir"}, paramLabel = "''", description = "Overwrite the default WORKDIR of the image e.g. /some/work/dir.")
private String workingDir;

Expand Down Expand Up @@ -340,7 +344,9 @@ private String encodePathBase64(String value) {
return null;
// read the text from a URI resource and encode to base64
if( value.startsWith("file:/") || value.startsWith("http://") || value.startsWith("https://")) {
return Base64.getEncoder().encodeToString(Files.readAllBytes(Path.of(new URI(value))));
try(InputStream stream=new URI(value).toURL().openStream()) {
return Base64.getEncoder().encodeToString(stream.readAllBytes());
}
}
// read the text from a local file and encode to base64
return Base64.getEncoder().encodeToString(Files.readAllBytes(Path.of(value)));
Expand Down Expand Up @@ -380,7 +386,13 @@ protected BuildContext prepareContext() {
}

protected ContainerConfig prepareConfig() {
final ContainerConfig result = new ContainerConfig();
ContainerConfig result = new ContainerConfig();

// add configuration from config file if specified
if( configFile != null ){
if( "".equals(configFile.trim()) ) throw new IllegalCliArgumentException("The specified config file is an empty string");
result = readConfig(configFile);
}

// add the entrypoint if specified
if( entrypoint!=null )
Expand Down Expand Up @@ -490,4 +502,25 @@ protected String dumpOutput(SubmitContainerTokenResponse resp) {
? resp.containerImage
: resp.targetImage;
}

protected ContainerConfig readConfig(String path) {
try {
if( path.startsWith("http://") || path.startsWith("https://") || path.startsWith("file:/")) {
try (InputStream stream=new URL(path).openStream()) {
return JsonHelper.fromJson(new String(stream.readAllBytes()), ContainerConfig.class);
}
}
else {
return JsonHelper.fromJson(Files.readString(Path.of(path)), ContainerConfig.class);
}
}
catch (FileNotFoundException | NoSuchFileException e) {
throw new IllegalCliArgumentException("Invalid container config file - File not found: " + path);
}
catch (IOException e) {
String msg = String.format("Unable to read container config file: %s - Cause: %s", path, e.getMessage());
throw new IllegalCliArgumentException(msg, e);
}
}

}
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@ import io.seqera.wavelit.exception.IllegalCliArgumentException
import picocli.CommandLine
import spock.lang.Specification
/**
* Test App Conda prefixed options
*
* @author Paolo Di Tommaso <[email protected]>
*/
Expand Down
233 changes: 233 additions & 0 deletions app/src/test/groovy/io/seqera/wavelit/AppConfigOptsTest.groovy
Original file line number Diff line number Diff line change
@@ -0,0 +1,233 @@
/*
* Copyright (c) 2023, Seqera Labs.
*
* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/.
*
* This Source Code Form is "Incompatible With Secondary Licenses", as
* defined by the Mozilla Public License, v. 2.0.
*/

package io.seqera.wavelit

import java.nio.file.Files

import com.github.tomakehurst.wiremock.WireMockServer
import com.github.tomakehurst.wiremock.client.WireMock
import com.github.tomakehurst.wiremock.core.WireMockConfiguration
import io.seqera.wave.api.ContainerConfig
import io.seqera.wavelit.exception.IllegalCliArgumentException
import picocli.CommandLine
import spock.lang.Specification

/**
* Test App config prefixed options
*
* @author Paolo Di Tommaso <[email protected]>
*/
class AppConfigOptsTest extends Specification {

def CONFIG_JSON = '''\
{
"entrypoint": [ "/some", "--entrypoint" ],
"layers": [
{
"location": "https://location",
"gzipDigest": "sha256:gzipDigest",
"gzipSize": 100,
"tarDigest": "sha256:tarDigest",
"skipHashing": true
}
]
}
'''

WireMockServer wireMockServer
def setup() {
wireMockServer = new WireMockServer(WireMockConfiguration.wireMockConfig().port(8080))
wireMockServer.start()

WireMock.stubFor(
WireMock.get(WireMock.urlEqualTo("/api/data"))
.willReturn(
WireMock.aResponse()
.withStatus(200)
.withHeader("Content-Type", "application/json")
.withBody(CONFIG_JSON)
)
)
}

def cleanup() {
wireMockServer.stop()
}


def "test valid entrypoint"() {
given:
def app = new App()
String[] args = ["--config-entrypoint", "entryPoint"]
def cli = new CommandLine(app)

when:
cli.parseArgs(args)
then:
app.@entrypoint == "entryPoint"

when:
def config = app.prepareConfig()
then:
config == new ContainerConfig(entrypoint: ['entryPoint'])
}

def "test invalid entrypoint"() {
given:
def app = new App()
String[] args = ["--config-entrypoint"]

when:
new CommandLine(app).parseArgs(args)

then:
thrown(CommandLine.MissingParameterException)
}

def "test valid command"() {
given:
def app = new App()
String[] args = ["--config-cmd", "/some/command"]

when:
new CommandLine(app).parseArgs(args)
then:
app.@command == "/some/command"

when:
def config = app.prepareConfig()
then:
config == new ContainerConfig(cmd: ['/some/command'])
}

def "test invalid command"() {
given:
def app = new App()
String[] args = ["--config-cmd", ""]

when:
new CommandLine(app).parseArgs(args)
app.prepareConfig()
then:
thrown(IllegalCliArgumentException)

}

def "test valid environment"() {
given:
def app = new App()
String[] args = ["--config-env", "var1=value1","--config-env", "var2=value2"]

when:
new CommandLine(app).parseArgs(args)
then:
app.@environment[0] == "var1=value1"
app.@environment[1] == "var2=value2"

when:
def config = app.prepareConfig()
then:
config == new ContainerConfig(env: ['var1=value1', 'var2=value2'])
}

def "test invalid environment"() {
given:
def app = new App()
String[] args = ["--config-env", "VAR"]

when:
new CommandLine(app).parseArgs(args)
app.prepareConfig()
then:
def e = thrown(IllegalCliArgumentException)
e.message == 'Invalid environment variable syntax - offending value: VAR'

}

def "test valid working directory"() {
given:
def app = new App()
String[] args = ["--config-working-dir", "/work/dir"]

when:
new CommandLine(app).parseArgs(args)
then:
app.@workingDir == "/work/dir"

when:
def config = app.prepareConfig()
then:
config == new ContainerConfig(workingDir: '/work/dir')
}

def "test invalid working directory"() {
given:
def app = new App()
String[] args = ["--config-working-dir", " "]

when:
new CommandLine(app).parseArgs(args)
app.prepareConfig()
then:
thrown(IllegalCliArgumentException)

}

def "test valid config file from a path"() {
given:
def folder = Files.createTempDirectory('test')
def configFile = folder.resolve('config.json')
configFile.text = CONFIG_JSON
and:
def app = new App()
String[] args = ["--config-file", configFile.toString()]

when:
new CommandLine(app).parseArgs(args)
then:
app.@configFile == configFile.toString()

when:
def config = app.prepareConfig()
then:
config.entrypoint == [ "/some", "--entrypoint" ]
def layer = config.layers[0]
layer.location == "https://location"
layer.gzipDigest == "sha256:gzipDigest"
layer.tarDigest == "sha256:tarDigest"
layer.gzipSize == 100

cleanup:
folder?.deleteDir()
}

def "test valid config file from a URL"() {
given:
def app = new App()
String[] args = ["--config-file", "http://localhost:8080/api/data"]

when:
new CommandLine(app).parseArgs(args)
then:
app.@configFile == "http://localhost:8080/api/data"

when:
def config = app.prepareConfig()
then:
config.entrypoint == [ "/some", "--entrypoint" ]
def layer = config.layers[0]
layer.location == "https://location"
layer.gzipDigest == "sha256:gzipDigest"
layer.tarDigest == "sha256:tarDigest"
layer.gzipSize == 100
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@ import io.seqera.wavelit.exception.IllegalCliArgumentException
import picocli.CommandLine
import spock.lang.Specification
/**
* Test App Spack prefixed options
*
* @author Paolo Di Tommaso <[email protected]>
*/
Expand Down
Loading

0 comments on commit a212bb3

Please sign in to comment.