All Downloads are FREE. Search and download functionalities are using the official Maven repository.

io.trino.tests.product.launcher.docker.DockerFiles Maven / Gradle / Ivy

There is a newer version: 451
Show newest version
/*
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *     http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */
package io.trino.tests.product.launcher.docker;

import com.google.common.reflect.ClassPath;
import com.google.errorprone.annotations.concurrent.GuardedBy;
import dev.failsafe.Failsafe;
import dev.failsafe.RetryPolicy;
import io.airlift.log.Logger;
import jakarta.annotation.PreDestroy;

import java.io.IOException;
import java.io.InputStream;
import java.io.UncheckedIOException;
import java.nio.file.Files;
import java.nio.file.Path;
import java.nio.file.Paths;
import java.nio.file.attribute.PosixFilePermissions;

import static com.google.common.base.Preconditions.checkArgument;
import static com.google.common.base.Preconditions.checkState;
import static com.google.common.base.Verify.verify;
import static com.google.common.io.MoreFiles.deleteRecursively;
import static com.google.common.io.RecursiveDeleteOption.ALLOW_INSECURE;
import static java.nio.file.Files.copy;
import static java.util.UUID.randomUUID;

public final class DockerFiles
        implements AutoCloseable
{
    public static final String ROOT_PATH = "docker/presto-product-tests/";

    private static final Logger log = Logger.get(DockerFiles.class);

    @GuardedBy("this")
    private Path dockerFilesHostPath;
    @GuardedBy("this")
    private boolean closed;

    @PreDestroy
    @Override
    public synchronized void close()
    {
        if (closed) {
            return;
        }
        if (dockerFilesHostPath != null) {
            Failsafe.with(RetryPolicy.builder().withMaxAttempts(5).build())
                    .run(() -> deleteRecursively(dockerFilesHostPath, ALLOW_INSECURE));
            dockerFilesHostPath = null;
        }
        closed = true;
    }

    public synchronized Path getDockerFilesHostPath()
    {
        checkState(!closed, "Already closed");
        if (dockerFilesHostPath == null) {
            dockerFilesHostPath = unpackDockerFilesFromClasspath();
            verify(dockerFilesHostPath != null);
        }
        return dockerFilesHostPath;
    }

    public ResourceProvider getDockerFilesHostDirectory(String directory)
    {
        Path hostPath = getDockerFilesHostPath(directory);
        return file -> {
            checkArgument(file != null && !file.isEmpty() && !(file.charAt(0) == '/'), "Invalid file: %s", file);
            Path filePath = hostPath.resolve(file);
            checkArgument(Files.exists(filePath), "'%s' resolves to '%s', but it does not exist", file, filePath);
            return filePath;
        };
    }

    public Path getDockerFilesHostPath(String file)
    {
        checkArgument(file != null && !file.isEmpty() && !(file.charAt(0) == '/'), "Invalid file: %s", file);
        Path filePath = getDockerFilesHostPath().resolve(file);
        checkArgument(Files.exists(filePath), "'%s' resolves to '%s', but it does not exist", file, filePath);
        return filePath;
    }

    private static Path unpackDockerFilesFromClasspath()
    {
        try {
            Path dockerFilesHostPath = createTemporaryDirectoryForDocker();
            ClassPath.from(Thread.currentThread().getContextClassLoader())
                    .getResources().stream()
                    .filter(resourceInfo -> resourceInfo.getResourceName().startsWith(ROOT_PATH))
                    .forEach(resourceInfo -> {
                        try {
                            Path target = dockerFilesHostPath.resolve(resourceInfo.getResourceName().replaceFirst("^" + ROOT_PATH, ""));
                            Files.createDirectories(target.getParent());

                            try (InputStream inputStream = resourceInfo.asByteSource().openStream()) {
                                copy(inputStream, target);
                            }
                            if (resourceInfo.getResourceName().endsWith(".sh")) {
                                Files.setPosixFilePermissions(target, PosixFilePermissions.fromString("r-x------"));
                            }
                        }
                        catch (IOException e) {
                            throw new UncheckedIOException(e);
                        }
                    });
            return dockerFilesHostPath;
        }
        catch (IOException e) {
            throw new UncheckedIOException(e);
        }
    }

    public static Path createTemporaryDirectoryForDocker()
    {
        Path temporaryDirectoryForDocker;
        try {
            // Cannot use Files.createTempDirectory() because on Mac by default it uses /var/folders/ which is not visible to Docker for Mac
            temporaryDirectoryForDocker = Files.createDirectory(Paths.get("/tmp/docker-files-" + randomUUID().toString()));
        }
        catch (IOException e) {
            throw new UncheckedIOException(e);
        }

        // Best-effort cleanup
        Runtime.getRuntime().addShutdownHook(new Thread(() -> {
            try {
                deleteRecursively(temporaryDirectoryForDocker, ALLOW_INSECURE);
            }
            catch (IOException e) {
                log.warn(e, "Failed to clean up docker files temporary directory '%s'", temporaryDirectoryForDocker);
            }
        }));
        return temporaryDirectoryForDocker;
    }

    public interface ResourceProvider
    {
        Path getPath(String resourceName);
    }
}




© 2015 - 2024 Weber Informatics LLC | Privacy Policy