Skip to content

Commit

Permalink
refactor: initial changes to support multiplatform build
Browse files Browse the repository at this point in the history
Signed-off-by: Marc Nuri <[email protected]>
  • Loading branch information
manusa authored May 16, 2024
1 parent bfbf08d commit c1a4ffd
Show file tree
Hide file tree
Showing 3 changed files with 165 additions and 203 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,6 @@
import java.util.ArrayList;
import java.util.List;
import java.util.Map;
import java.util.Objects;
import java.util.Optional;
import java.util.concurrent.ExecutionException;
import java.util.concurrent.ExecutorService;
Expand All @@ -32,6 +31,7 @@
import org.eclipse.jkube.kit.build.api.assembly.BuildDirs;
import org.eclipse.jkube.kit.common.Assembly;
import org.eclipse.jkube.kit.common.AssemblyFileEntry;
import org.eclipse.jkube.kit.common.JKubeException;
import org.eclipse.jkube.kit.config.image.ImageConfiguration;
import org.eclipse.jkube.kit.config.image.ImageName;
import org.eclipse.jkube.kit.common.Arguments;
Expand All @@ -56,197 +56,172 @@
import org.apache.commons.io.FilenameUtils;
import org.apache.commons.lang3.StringUtils;

import javax.annotation.Nonnull;

import static com.google.cloud.tools.jib.api.buildplan.FileEntriesLayer.DEFAULT_FILE_PERMISSIONS_PROVIDER;

public class JibServiceUtil {

private JibServiceUtil() {
}

private static final long JIB_EXECUTOR_SHUTDOWN_TIMEOUT_SECONDS = 10L;
private static final String BUSYBOX = "busybox:latest";

/**
* Build container image using JIB
*
* @param jibContainerBuilder jib container builder object
* @param image tarball for image
* @param logger kit logger
* @throws InterruptedException in case thread is interrupted
*/
public static void buildContainer(JibContainerBuilder jibContainerBuilder, TarImage image, JibLogger logger)
throws InterruptedException {

final ExecutorService jibBuildExecutor = Executors.newCachedThreadPool();
try {
jibContainerBuilder.setCreationTime(Instant.now());
jibContainerBuilder.containerize(Containerizer.to(image)
.setAllowInsecureRegistries(true)
.setExecutorService(jibBuildExecutor)
.addEventHandler(LogEvent.class, logger)
.addEventHandler(ProgressEvent.class, logger.progressEventHandler()));
logger.updateFinished();
} catch (CacheDirectoryCreationException | IOException | ExecutionException | RegistryException ex) {
throw new IllegalStateException("Unable to build the image tarball: " + ex.getMessage(), ex);
} catch (InterruptedException ex) {
Thread.currentThread().interrupt();
throw ex;
} finally {
jibBuildExecutor.shutdown();
jibBuildExecutor.awaitTermination(JIB_EXECUTOR_SHUTDOWN_TIMEOUT_SECONDS, TimeUnit.SECONDS);
}
}

public static JibContainerBuilder containerFromImageConfiguration(
ImageConfiguration imageConfiguration, String pullRegistry, Credential pullRegistryCredential) throws InvalidImageReferenceException {
final JibContainerBuilder containerBuilder = Jib
.from(toRegistryImage(getBaseImage(imageConfiguration, pullRegistry), pullRegistryCredential))
.setFormat(ImageFormat.Docker);
return populateContainerBuilderFromImageConfiguration(containerBuilder, imageConfiguration);
}

public static String getFullImageName(ImageConfiguration imageConfiguration, String tag) {
ImageName imageName;
if (tag != null) {
imageName = new ImageName(imageConfiguration.getName(), tag);
} else {
imageName = new ImageName(imageConfiguration.getName());
}
return imageName.getFullName();
}

/**
* Push Image to registry using JIB
*
* @param imageConfiguration ImageConfiguration
* @param pushCredentials push credentials
* @param tarArchive tar archive built during build goal
* @param log Logger
*/
public static void jibPush(ImageConfiguration imageConfiguration, Credential pushCredentials, File tarArchive, JibLogger log) {
String imageName = getFullImageName(imageConfiguration, null);
List<String> additionalTags = imageConfiguration.getBuildConfiguration().getTags();
try {
pushImage(TarImage.at(tarArchive.toPath()), additionalTags, imageName, pushCredentials, log);
} catch (InterruptedException e) {
Thread.currentThread().interrupt();
throw new IllegalStateException("Thread Interrupted", e);
}
private JibServiceUtil() {
}

private static final long JIB_EXECUTOR_SHUTDOWN_TIMEOUT_SECONDS = 10L;
private static final String BUSYBOX = "busybox:latest";

/**
* Build container image using JIB
*
* @param jibContainerBuilder jib container builder object
* @param image tarball for image
* @param logger kit logger
*/
public static void buildContainer(JibContainerBuilder jibContainerBuilder, TarImage image, JibLogger logger) {
final ExecutorService jibBuildExecutor = Executors.newCachedThreadPool();
try {
jibContainerBuilder.setCreationTime(Instant.now());
jibContainerBuilder.containerize(Containerizer.to(image)
.setAllowInsecureRegistries(true)
.setExecutorService(jibBuildExecutor)
.addEventHandler(LogEvent.class, logger)
.addEventHandler(ProgressEvent.class, logger.progressEventHandler()));
logger.updateFinished();
} catch (CacheDirectoryCreationException | IOException | ExecutionException | RegistryException ex) {
throw new JKubeException("Unable to build the image tarball: " + ex.getMessage(), ex);
} catch (InterruptedException ex) {
Thread.currentThread().interrupt();
throw new JKubeException("Thread Interrupted", ex);
} finally {
shutdownAndWait(jibBuildExecutor);
}

private static void pushImage(TarImage baseImage, List<String> additionalTags, String targetImageName, Credential credential, JibLogger logger)
throws InterruptedException {

final ExecutorService jibBuildExecutor = Executors.newCachedThreadPool();
try {
submitPushToJib(baseImage, toRegistryImage(targetImageName, credential), additionalTags, jibBuildExecutor, logger);
} catch (RegistryException | CacheDirectoryCreationException | InvalidImageReferenceException | IOException | ExecutionException e) {
throw new IllegalStateException("Exception occurred while pushing the image: " + targetImageName + ", " + e.getMessage(), e);
} finally {
jibBuildExecutor.shutdown();
jibBuildExecutor.awaitTermination(JIB_EXECUTOR_SHUTDOWN_TIMEOUT_SECONDS, TimeUnit.SECONDS);
}
}

private static JibContainerBuilder populateContainerBuilderFromImageConfiguration(JibContainerBuilder containerBuilder, ImageConfiguration imageConfiguration) {
final Optional<BuildConfiguration> bic =
Optional.ofNullable(Objects.requireNonNull(imageConfiguration).getBuildConfiguration());
bic.map(BuildConfiguration::getEntryPoint)
.map(Arguments::asStrings)
.ifPresent(containerBuilder::setEntrypoint);
bic.map(BuildConfiguration::getEnv)
.ifPresent(containerBuilder::setEnvironment);
bic.map(BuildConfiguration::getPorts).map(List::stream)
.map(s -> s.map(Integer::parseInt).map(Port::tcp))
.map(s -> s.collect(Collectors.toSet()))
.ifPresent(containerBuilder::setExposedPorts);
bic.map(BuildConfiguration::getLabels)
.map(Map::entrySet)
.ifPresent(labels -> labels.forEach(l -> {
if (l.getKey() != null && l.getValue() != null) {
containerBuilder.addLabel(l.getKey(), l.getValue());
}
}));
bic.map(BuildConfiguration::getCmd)
.map(Arguments::asStrings)
.ifPresent(containerBuilder::setProgramArguments);
bic.map(BuildConfiguration::getUser)
.ifPresent(containerBuilder::setUser);
bic.map(BuildConfiguration::getVolumes).map(List::stream)
.map(s -> s.map(AbsoluteUnixPath::get))
.map(s -> s.collect(Collectors.toSet()))
.ifPresent(containerBuilder::setVolumes);
bic.map(BuildConfiguration::getWorkdir)
.filter(((Predicate<String>) String::isEmpty).negate())
.map(AbsoluteUnixPath::get)
.ifPresent(containerBuilder::setWorkingDirectory);
return containerBuilder;
}

public static JibContainerBuilder containerFromImageConfiguration(
ImageConfiguration imageConfiguration, String pullRegistry, Credential pullRegistryCredential) throws InvalidImageReferenceException {
final JibContainerBuilder containerBuilder = Jib
.from(toRegistryImage(getBaseImage(imageConfiguration, pullRegistry), pullRegistryCredential))
.setFormat(ImageFormat.Docker);
if (imageConfiguration.getBuildConfiguration() != null) {
final BuildConfiguration bic = imageConfiguration.getBuildConfiguration();
Optional.ofNullable(bic.getEntryPoint())
.map(Arguments::asStrings)
.ifPresent(containerBuilder::setEntrypoint);
Optional.ofNullable(bic.getEnv())
.ifPresent(containerBuilder::setEnvironment);
Optional.ofNullable(bic.getPorts()).map(List::stream)
.map(s -> s.map(Integer::parseInt).map(Port::tcp))
.map(s -> s.collect(Collectors.toSet()))
.ifPresent(containerBuilder::setExposedPorts);
Optional.ofNullable(bic.getLabels())
.map(Map::entrySet)
.ifPresent(labels -> labels.forEach(l -> {
if (l.getKey() != null && l.getValue() != null) {
containerBuilder.addLabel(l.getKey(), l.getValue());
}
}));
Optional.ofNullable(bic.getCmd())
.map(Arguments::asStrings)
.ifPresent(containerBuilder::setProgramArguments);
Optional.ofNullable(bic.getUser())
.ifPresent(containerBuilder::setUser);
Optional.ofNullable(bic.getVolumes())
.map(List::stream)
.map(s -> s.map(AbsoluteUnixPath::get))
.map(s -> s.collect(Collectors.toSet()))
.ifPresent(containerBuilder::setVolumes);
Optional.ofNullable(bic.getWorkdir())
.filter(((Predicate<String>) String::isEmpty).negate())
.map(AbsoluteUnixPath::get)
.ifPresent(containerBuilder::setWorkingDirectory);
}

private static void submitPushToJib(TarImage baseImage, RegistryImage targetImage, List<String> additionalTags, ExecutorService jibBuildExecutor, JibLogger logger) throws InterruptedException, ExecutionException, RegistryException, CacheDirectoryCreationException, IOException {
Jib
.from(baseImage)
.setCreationTime(Instant.now())
.containerize(createJibContainerizer(targetImage, additionalTags, jibBuildExecutor, logger));
logger.updateFinished();
return containerBuilder;
}

/**
* Push Image to registry using JIB.
*
* @param imageConfiguration ImageConfiguration.
* @param pushCredentials push credentials.
* @param tarArchive tar archive built during build goal.
* @param logger the JibLogger.
*/
public static void jibPush(ImageConfiguration imageConfiguration, Credential pushCredentials, File tarArchive, JibLogger logger) {
final String imageName = new ImageName(imageConfiguration.getName()).getFullName();
final TarImage image = TarImage.at(tarArchive.toPath());
final ExecutorService jibBuildExecutor = Executors.newCachedThreadPool();
try {
final RegistryImage registryImage = toRegistryImage(imageName, pushCredentials);
final Containerizer containerizer = customizeContainerizer(Containerizer.to(registryImage), imageConfiguration, logger)
.setExecutorService(jibBuildExecutor);
Jib
.from(image)
.setCreationTime(Instant.now())
.containerize(containerizer);
logger.updateFinished();
} catch (Exception e) {
throw new JKubeException("Exception occurred while pushing the image: " + imageName + ", " + e.getMessage(), e);
} finally {
shutdownAndWait(jibBuildExecutor);
}

private static Containerizer createJibContainerizer(RegistryImage targetImage, List<String> additionalTags, ExecutorService jibBuildExecutor, JibLogger logger) {
Containerizer containerizer = Containerizer.to(targetImage)
.setAllowInsecureRegistries(true)
.setExecutorService(jibBuildExecutor)
.addEventHandler(LogEvent.class, logger)
.addEventHandler(ProgressEvent.class, logger.progressEventHandler());
if (additionalTags != null) {
additionalTags.forEach(containerizer::withAdditionalTag);
}
return containerizer;
}

private static Containerizer customizeContainerizer(Containerizer c, ImageConfiguration imageConfiguration, JibLogger logger) {
c.setAllowInsecureRegistries(true);
c.addEventHandler(LogEvent.class, logger);
c.addEventHandler(ProgressEvent.class, logger.progressEventHandler());
if (imageConfiguration.getBuildConfiguration().getTags() != null) {
imageConfiguration.getBuildConfiguration().getTags().forEach(c::withAdditionalTag);
}
return c;
}

private static RegistryImage toRegistryImage(String imageReference, Credential credential) throws InvalidImageReferenceException {
RegistryImage registryImage = RegistryImage.named(imageReference);
if (credential != null && !credential.getUsername().isEmpty() && !credential.getPassword().isEmpty()) {
registryImage.addCredential(credential.getUsername(), credential.getPassword());
}
return registryImage;
private static RegistryImage toRegistryImage(String imageReference, Credential credential) throws InvalidImageReferenceException {
RegistryImage registryImage = RegistryImage.named(imageReference);
if (credential != null && !credential.getUsername().isEmpty() && !credential.getPassword().isEmpty()) {
registryImage.addCredential(credential.getUsername(), credential.getPassword());
}

public static String getBaseImage(ImageConfiguration imageConfiguration, String optionalRegistry) {
String baseImage = Optional.ofNullable(imageConfiguration)
.map(ImageConfiguration::getBuildConfiguration)
.map(BuildConfiguration::getFrom)
.filter(((Predicate<String>) String::isEmpty).negate())
.orElse(BUSYBOX);
return new ImageName(baseImage).getFullName(optionalRegistry);
return registryImage;
}

public static String getBaseImage(ImageConfiguration imageConfiguration, String optionalRegistry) {
String baseImage = Optional.ofNullable(imageConfiguration)
.map(ImageConfiguration::getBuildConfiguration)
.map(BuildConfiguration::getFrom)
.filter(((Predicate<String>) String::isEmpty).negate())
.orElse(BUSYBOX);
return new ImageName(baseImage).getFullName(optionalRegistry);
}

public static List<FileEntriesLayer> layers(BuildDirs buildDirs, Map<Assembly, List<AssemblyFileEntry>> layers) {
final List<FileEntriesLayer> fileEntriesLayers = new ArrayList<>();
for (Map.Entry<Assembly, List<AssemblyFileEntry>> layer : layers.entrySet()) {
final FileEntriesLayer.Builder fel = FileEntriesLayer.builder();
final String layerId = layer.getKey().getId();
final Path outputPath;
if (StringUtils.isBlank(layerId)) {
outputPath = buildDirs.getOutputDirectory().toPath();
} else {
fel.setName(layerId);
outputPath = new File(buildDirs.getOutputDirectory(), layerId).toPath();
}
for (AssemblyFileEntry afe : layer.getValue()) {
final Path source = afe.getSource().toPath();
final AbsoluteUnixPath target = AbsoluteUnixPath.get(StringUtils.prependIfMissing(
FilenameUtils.separatorsToUnix(outputPath.relativize(afe.getDest().toPath()).normalize().toString()), "/"));
final FilePermissions permissions = StringUtils.isNotBlank(afe.getFileMode()) ?
FilePermissions.fromOctalString(StringUtils.right(afe.getFileMode(), 3)) :
DEFAULT_FILE_PERMISSIONS_PROVIDER.get(source, target);
fel.addEntry(source, target, permissions);
}
fileEntriesLayers.add(fel.build());
}

@Nonnull
public static List<FileEntriesLayer> layers(BuildDirs buildDirs, Map<Assembly, List<AssemblyFileEntry>> layers) {
final List<FileEntriesLayer> fileEntriesLayers = new ArrayList<>();
for (Map.Entry<Assembly, List<AssemblyFileEntry>> layer : layers.entrySet()) {
final FileEntriesLayer.Builder fel = FileEntriesLayer.builder();
final String layerId = layer.getKey().getId();
final Path outputPath;
if (StringUtils.isBlank(layerId)) {
outputPath = buildDirs.getOutputDirectory().toPath();
} else {
fel.setName(layerId);
outputPath = new File(buildDirs.getOutputDirectory(), layerId).toPath();
}
for (AssemblyFileEntry afe : layer.getValue()) {
final Path source = afe.getSource().toPath();
final AbsoluteUnixPath target = AbsoluteUnixPath.get(StringUtils.prependIfMissing(
FilenameUtils.separatorsToUnix(outputPath.relativize(afe.getDest().toPath()).normalize().toString()), "/"));
final FilePermissions permissions = StringUtils.isNotBlank(afe.getFileMode()) ?
FilePermissions.fromOctalString(StringUtils.right(afe.getFileMode(), 3)) :
DEFAULT_FILE_PERMISSIONS_PROVIDER.get(source, target);
fel.addEntry(source, target, permissions);
}
fileEntriesLayers.add(fel.build());
}
return fileEntriesLayers;
return fileEntriesLayers;
}

private static void shutdownAndWait(ExecutorService executorService) {
try {
executorService.shutdown();
executorService.awaitTermination(JIB_EXECUTOR_SHUTDOWN_TIMEOUT_SECONDS, TimeUnit.SECONDS);
} catch (InterruptedException e) {
Thread.currentThread().interrupt();
throw new JKubeException("Thread Interrupted", e);
}

}
}
Loading

0 comments on commit c1a4ffd

Please sign in to comment.