Skip to content

Commit

Permalink
* replace zip4j with builtin zip
Browse files Browse the repository at this point in the history
* scripts support
  • Loading branch information
imakunin committed May 4, 2024
1 parent 6d25de1 commit b098732
Show file tree
Hide file tree
Showing 7 changed files with 133 additions and 86 deletions.
4 changes: 0 additions & 4 deletions lzy/execution-env/pom.xml
Original file line number Diff line number Diff line change
Expand Up @@ -17,10 +17,6 @@
</properties>

<dependencies>
<dependency>
<groupId>net.lingala.zip4j</groupId>
<artifactId>zip4j</artifactId>
</dependency>
<dependency>
<groupId>org.apache.logging.log4j</groupId>
<artifactId>log4j-api</artifactId>
Expand Down
52 changes: 0 additions & 52 deletions lzy/execution-env/src/main/java/ai/lzy/env/aux/AuxEnvironment.java
Original file line number Diff line number Diff line change
Expand Up @@ -2,18 +2,8 @@

import ai.lzy.env.Environment;
import ai.lzy.env.base.BaseEnvironment;
import ai.lzy.env.logs.LogStream;
import net.lingala.zip4j.ZipFile;
import org.apache.logging.log4j.Logger;

import java.io.File;
import java.io.IOException;
import java.io.InputStream;
import java.net.URL;
import java.nio.file.Files;
import java.nio.file.Path;
import java.nio.file.StandardCopyOption;
import java.util.Map;

public interface AuxEnvironment extends Environment {
BaseEnvironment base();
Expand All @@ -27,46 +17,4 @@ public interface AuxEnvironment extends Environment {
default void close() throws Exception {
base().close();
}

static void installLocalModules(Map<String, String> localModules, Path localModulesPath, Logger log,
LogStream userOut, LogStream userErr)
throws InstallationException, IOException
{
var msg = "Install python local modules to %s".formatted(localModulesPath);
log.info(msg);
userOut.log(msg);
try {
Files.createDirectories(localModulesPath);
} catch (IOException e) {
String errorMessage = "Failed to create directory to download local modules into;\n"
+ " Directory name: " + localModulesPath + "\n";
log.error(errorMessage);
userErr.log(errorMessage);
throw new InstallationException(errorMessage);
}

log.info("Created directory {} to download local modules into", localModulesPath);
for (var entry : localModules.entrySet()) {
String name = entry.getKey();
String url = entry.getValue();
log.info("Installing local module with name " + name + " and url " + url);
userOut.log("Installing local module '%s'".formatted(name));

File tempFile = File.createTempFile("tmp-file", ".zip");

try (InputStream in = new URL(url).openStream()) {
Files.copy(in, tempFile.toPath(), StandardCopyOption.REPLACE_EXISTING);
}

extractFiles(tempFile, localModulesPath.toString(), log);
tempFile.delete();
}
}

private static void extractFiles(File file, String destinationDirectory, Logger log) throws IOException {
log.debug("Trying to unzip module archive {}", file.getAbsolutePath());
try (ZipFile zipFile = new ZipFile(file.getAbsolutePath())) {
zipFile.extractAll(destinationDirectory);
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -22,9 +22,7 @@
import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReentrantLock;

import static ai.lzy.env.aux.AuxEnvironment.installLocalModules;

public class CondaEnvironment implements AuxEnvironment {
public class CondaEnvironment implements PythonBaseEnvironment {
private static volatile boolean RECONFIGURE_CONDA = true; // Only for tests

private static final Logger LOG = LogManager.getLogger(CondaEnvironment.class);
Expand All @@ -33,25 +31,30 @@ public class CondaEnvironment implements AuxEnvironment {
private final String condaYaml;
private final BaseEnvironment baseEnv;
private String envName;
private final Map<String, String> localModules;
private final Map<String, String> localModulesUrls; // name -> url
@Nullable
private final String binsUrl;

private final Path hostWorkingDir;
private final Path baseEnvWorkingDir;

@Nullable
private String pythonPath;
private String pythonPath = null;
@Nullable
private String binsPath = null;

@VisibleForTesting
public static void reconfigureConda(boolean reconfigure) {
RECONFIGURE_CONDA = reconfigure;
}

public CondaEnvironment(BaseEnvironment baseEnv, String condaYaml, Map<String, String> localModules,
Path hostWorkingDir, Path baseEnvWorkingDir)
public CondaEnvironment(BaseEnvironment baseEnv, String condaYaml, Map<String, String> localModulesUrls,
@Nullable String binsUrl, Path hostWorkingDir, Path baseEnvWorkingDir)
{
this.condaYaml = condaYaml;
this.baseEnv = baseEnv;
this.localModules = localModules;
this.localModulesUrls = localModulesUrls;
this.binsUrl = binsUrl;
this.hostWorkingDir = hostWorkingDir;
this.baseEnvWorkingDir = baseEnvWorkingDir;
}
Expand All @@ -66,13 +69,19 @@ public void install(LogStream outStream, LogStream errStream) throws Installatio
try {
final var condaPackageRegistry = baseEnv.getPackageRegistry();

try {
installLocalModules(localModules, hostWorkingDir, LOG, outStream, errStream);
} catch (IOException e) {
String errorMessage = "Failed to install local modules";
LOG.error(errorMessage, e);
errStream.log(errorMessage);
throw new InstallationException(errorMessage);
PythonBaseEnvironment.installLocalModules(localModulesUrls, hostWorkingDir, LOG, outStream, errStream);

if (binsUrl != null && !binsUrl.isBlank()) {
Path binsHostPath = hostWorkingDir.resolve("_bin");
PythonBaseEnvironment.installLocalModule("bins", binsUrl, binsHostPath, LOG, outStream, errStream);

Files.list(binsHostPath)
.map(Path::toFile)
.filter(File::isFile)
.forEach(f -> f.setExecutable(true));

binsPath = baseEnvWorkingDir.resolve("_bin").toAbsolutePath().toString();
LOG.info("Bins host path={}, docker path={}", binsHostPath, binsPath);
}

if (!RECONFIGURE_CONDA) { // Only for tests
Expand All @@ -95,6 +104,8 @@ public void install(LogStream outStream, LogStream errStream) throws Installatio
file.write(envYaml);
}

LOG.debug("About to configure conda with parameters:\n{}", envYaml);

var condaFile = baseEnvWorkingDir.resolve("conda.yaml");
// Conda env create or update: https://github.com/conda/conda/issues/7819
final LzyProcess lzyProcess = execInEnv(
Expand Down Expand Up @@ -165,7 +176,8 @@ private int waitFor(LzyProcess lzyProcess, LogStream outStream, LogStream errStr
}

private LzyProcess execInEnv(String command, @Nullable String[] envp, @Nullable String workingDir) {
LOG.info("Executing command " + command);
LOG.info("Executing command '{}' at cwd '{}' and env [{}]",
command, workingDir, envp != null ? String.join(", ", envp) : "<none>");
assert baseEnvWorkingDir != null;

var bashCmd = new String[] {
Expand Down Expand Up @@ -196,6 +208,9 @@ public LzyProcess runProcess(String[] command, @Nullable String[] envp, @Nullabl

var cmd = String.join(" ", command);

if (binsPath != null) {
cmd = "export PATH=$PATH:" + binsPath + " && " + cmd;
}
if (pythonPath != null) {
// Adding export here to prevent conda from updating PYTHONPATH
cmd = "export PYTHONPATH=" + pythonPath + " && " + cmd;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -15,9 +15,7 @@
import java.util.List;
import java.util.Map;

import static ai.lzy.env.aux.AuxEnvironment.installLocalModules;

public class PlainPythonEnvironment implements AuxEnvironment {
public class PlainPythonEnvironment implements PythonBaseEnvironment {
private static final Logger LOG = LogManager.getLogger(PlainPythonEnvironment.class);

private final BaseEnvironment baseEnv;
Expand Down Expand Up @@ -96,13 +94,7 @@ public void install(LogStream outStream, LogStream errStream) throws Installatio
}

LOG.info("Using provided python with version \"{}\"", output);

try {
installLocalModules(localModules, hostWorkingDir, LOG, outStream, errStream);
} catch (IOException e) {
LOG.error("Cannot install local modules", e);
throw new InstallationException("Cannot install local modules: " + e.getMessage());
}
PythonBaseEnvironment.installLocalModules(localModules, hostWorkingDir, LOG, outStream, errStream);
}

@Override
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,96 @@
package ai.lzy.env.aux;

import ai.lzy.env.logs.LogStream;
import org.apache.logging.log4j.Logger;

import java.io.File;
import java.io.FileInputStream;
import java.io.IOException;
import java.io.InputStream;
import java.net.URL;
import java.nio.file.Files;
import java.nio.file.Path;
import java.nio.file.StandardCopyOption;
import java.util.Map;
import java.util.zip.ZipEntry;
import java.util.zip.ZipInputStream;

public interface PythonBaseEnvironment extends AuxEnvironment {

static void installLocalModules(Map<String, String> localModules, Path localModulesPath, Logger log,
LogStream userOut, LogStream userErr) throws InstallationException
{
var msg = "Install python local modules to %s".formatted(localModulesPath);
log.info(msg);
userOut.log(msg);
try {
Files.createDirectories(localModulesPath);
} catch (IOException e) {
String errorMessage = "Failed to create directory to download local modules into;\n"
+ " Directory name: " + localModulesPath + "\n";
log.error(errorMessage);
userErr.log(errorMessage);
throw new InstallationException(errorMessage);
}

log.info("Created directory {} to download local modules into", localModulesPath);
for (var entry : localModules.entrySet()) {
installLocalModule(entry.getKey(), entry.getValue(), localModulesPath, log, userOut, userErr);
}
}

static void installLocalModule(String name, String url, Path path, Logger log, LogStream userOut, LogStream userErr)
throws InstallationException
{
log.info("Installing local module '{}' from {}", name, url);
userOut.log("Installing local module '%s'".formatted(name));

File tempFile = null;
try {
tempFile = File.createTempFile("tmp-file", ".zip");

try (InputStream in = new URL(url).openStream()) {
Files.copy(in, tempFile.toPath(), StandardCopyOption.REPLACE_EXISTING);
}

log.info("Trying to unzip module archive {}", tempFile.getAbsolutePath());
extractFiles(tempFile, path);
} catch (Exception e) {
log.error("Failed to install local module '{}'", name, e);
var errorMessage = "Failed to install local module '%s': %s".formatted(name, e.getMessage());
userErr.log(errorMessage);

if (tempFile != null) {
try {
//noinspection ResultOfMethodCallIgnored
tempFile.delete();
} catch (Exception ignored) {
// ignore
}
}

throw new InstallationException(errorMessage);
}
}

private static void extractFiles(File zip, Path targetDir) throws IOException {
try (var zipStream = new ZipInputStream(new FileInputStream(zip))) {
ZipEntry zipEntry = zipStream.getNextEntry();
while (zipEntry != null) {
final Path entryTargetPath = targetDir.resolve(zipEntry.getName());
if (!entryTargetPath.startsWith(targetDir)) {
throw new IOException(
"Zip entry '%s' is trying to escape target path '%s'".formatted(entryTargetPath, targetDir));
}
if (zipEntry.isDirectory()) {
Files.createDirectories(entryTargetPath);
} else {
Files.createDirectories(entryTargetPath.getParent());
Files.copy(zipStream, entryTargetPath);
}
zipEntry = zipStream.getNextEntry();
}
}
}

}
Original file line number Diff line number Diff line change
Expand Up @@ -106,7 +106,7 @@ public LzyProcess runProcess(String[] command, @Nullable String[] envp, @Nullabl
throw new RuntimeException(e);
}

LOG.info("Creating cmd {}", String.join(" ", command));
LOG.info("Creating cmd '{}'", String.join(" ", command));
final ExecCreateCmd execCmd = client.execCreateCmd(containerId)
.withCmd(command)
.withUser(config.user())
Expand All @@ -121,7 +121,7 @@ public LzyProcess runProcess(String[] command, @Nullable String[] envp, @Nullabl
execCmd.withEnv(List.of(envp));
}
final ExecCreateCmdResponse exec = retry.executeSupplier(execCmd::exec);
LOG.info("Executing cmd {}", String.join(" ", command));
LOG.info("Executing cmd '{}'", String.join(" ", command));

var feature = new CompletableFuture<>();

Expand All @@ -130,7 +130,7 @@ public LzyProcess runProcess(String[] command, @Nullable String[] envp, @Nullabl
.exec(new ResultCallbackTemplate<>() {
@Override
public void onComplete() {
LOG.info("Closing stdout, stderr of cmd {}", String.join(" ", command));
LOG.info("Closing stdout, stderr of cmd '{}'", String.join(" ", command));
try {
stdout.close();
stderr.close();
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -176,7 +176,7 @@ public AuxEnvironment create(String fsRoot, LME.EnvSpec env, String lzyMount, Lo
LOG.error("Cannot find conda version", e);
}

auxEnv = new CondaEnvironment(baseEnv, env.getPyenv().getYaml(), localModules, resourcesDir,
auxEnv = new CondaEnvironment(baseEnv, env.getPyenv().getYaml(), localModules, null, resourcesDir,
resourcesDir);
}
} else if (env.hasProcessEnv()) {
Expand Down

0 comments on commit b098732

Please sign in to comment.