From 6ba765f4e9ff736f5652f3a14bafe21c0d1322f2 Mon Sep 17 00:00:00 2001 From: Rohan Kumar Date: Fri, 8 Nov 2019 21:38:57 +0530 Subject: [PATCH] Ported JIB implementation This pull request shall port the following PRs: https://github.com/fabric8io/fabric8-maven-plugin/pull/1675 https://github.com/fabric8io/fabric8-maven-plugin/pull/1734 https://github.com/fabric8io/fabric8-maven-plugin/pull/1739 https://github.com/fabric8io/fabric8-maven-plugin/pull/1740 --- jkube-kit/build/service/docker/pom.xml | 9 + .../build/service/docker/BuildService.java | 7 +- .../build/service/docker/JibBuildService.java | 131 +++++++ .../build/service/docker/RegistryService.java | 9 +- .../docker/auth/AuthConfigFactory.java | 28 +- .../docker/helper/JibBuildServiceUtil.java | 324 ++++++++++++++++++ .../kit/common/util}/FatJarDetector.java | 6 +- .../kit/common/util}/FatJarDetectorTest.java | 3 +- .../src/test/resources/fatjar-simple/test.jar | Bin .../generator/javaexec/JavaExecGenerator.java | 1 + ...ecGeneratorMainClassDeterminationTest.java | 1 + .../generator/SpringBootGenerator.java | 2 +- jkube-kit/parent/pom.xml | 20 +- .../plugin/mojo/build/AbstractDockerMojo.java | 36 +- .../maven/plugin/mojo/build/BuildMojo.java | 5 - .../maven/plugin/mojo/build/PushMojo.java | 16 +- 16 files changed, 551 insertions(+), 47 deletions(-) create mode 100644 jkube-kit/build/service/docker/src/main/java/io/jkube/kit/build/service/docker/JibBuildService.java create mode 100644 jkube-kit/build/service/docker/src/main/java/io/jkube/kit/build/service/docker/helper/JibBuildServiceUtil.java rename jkube-kit/{generator/java-exec/src/main/java/io/jkube/generator/javaexec => common/src/main/java/io/jkube/kit/common/util}/FatJarDetector.java (95%) rename jkube-kit/{generator/java-exec/src/test/java/io/jkube/generator/javaexec => common/src/test/java/io/jkube/kit/common/util}/FatJarDetectorTest.java (94%) rename jkube-kit/{generator/java-exec => common}/src/test/resources/fatjar-simple/test.jar (100%) diff --git a/jkube-kit/build/service/docker/pom.xml b/jkube-kit/build/service/docker/pom.xml index d64873aa10..8ef096eb0f 100644 --- a/jkube-kit/build/service/docker/pom.xml +++ b/jkube-kit/build/service/docker/pom.xml @@ -101,5 +101,14 @@ hamcrest-all + + com.google.cloud.tools + jib-core + + + + com.google.cloud.tools + jib-maven-plugin + diff --git a/jkube-kit/build/service/docker/src/main/java/io/jkube/kit/build/service/docker/BuildService.java b/jkube-kit/build/service/docker/src/main/java/io/jkube/kit/build/service/docker/BuildService.java index 8f4ba31508..bd6ffeaf1a 100644 --- a/jkube-kit/build/service/docker/src/main/java/io/jkube/kit/build/service/docker/BuildService.java +++ b/jkube-kit/build/service/docker/src/main/java/io/jkube/kit/build/service/docker/BuildService.java @@ -65,10 +65,10 @@ public class BuildService { * @param imageConfig the image configuration * @param imagePullManager the image pull manager * @param buildContext the build context - * @throws Exception in case of any problems + * @throws IOException in case of any problems */ public void buildImage(ImageConfiguration imageConfig, ImagePullManager imagePullManager, BuildContext buildContext) - throws Exception { + throws IOException { if (imagePullManager != null) { autoPullBaseImage(imageConfig, imagePullManager, buildContext); @@ -238,8 +238,7 @@ private Map addBuildArgsFromDockerConfig() { return buildArgs; } - private void autoPullBaseImage(ImageConfiguration imageConfig, ImagePullManager imagePullManager, BuildContext buildContext) - throws Exception { + private void autoPullBaseImage(ImageConfiguration imageConfig, ImagePullManager imagePullManager, BuildContext buildContext) throws DockerAccessException, IOException { BuildConfiguration buildConfig = imageConfig.getBuildConfiguration(); if (buildConfig.getDockerArchive() != null) { diff --git a/jkube-kit/build/service/docker/src/main/java/io/jkube/kit/build/service/docker/JibBuildService.java b/jkube-kit/build/service/docker/src/main/java/io/jkube/kit/build/service/docker/JibBuildService.java new file mode 100644 index 0000000000..433ebca936 --- /dev/null +++ b/jkube-kit/build/service/docker/src/main/java/io/jkube/kit/build/service/docker/JibBuildService.java @@ -0,0 +1,131 @@ +package io.jkube.kit.build.service.docker; + +import com.google.cloud.tools.jib.api.Credential; +import com.google.cloud.tools.jib.api.ImageFormat; +import com.google.cloud.tools.jib.api.InvalidImageReferenceException; +import com.google.cloud.tools.jib.api.JibContainer; +import com.google.cloud.tools.jib.api.RegistryException; +import io.jkube.kit.build.maven.MavenBuildContext; +import io.jkube.kit.build.service.docker.helper.DeepCopy; +import io.jkube.kit.build.service.docker.helper.JibBuildServiceUtil; +import io.jkube.kit.common.KitLogger; + +import java.nio.file.Path; +import java.util.Objects; +import java.util.concurrent.ExecutionException; + +public class JibBuildService { + + private KitLogger log; + private BuildService.BuildContext dockerBuildContext; + private MavenBuildContext mojoParameters; + + public JibBuildService(BuildService.BuildContext dockerBuildContext, MavenBuildContext mojoParameters, KitLogger log) { + Objects.requireNonNull(dockerBuildContext, "dockerBuildContext"); + this.dockerBuildContext = dockerBuildContext; + this.mojoParameters = mojoParameters; + this.log = log; + } + + public void buildImage(ImageConfiguration imageConfiguration, boolean isOfflineMode) { + try { + doJibBuild(JibBuildServiceUtil.getJibBuildConfiguration(dockerBuildContext, mojoParameters, imageConfiguration, log), isOfflineMode); + } catch (Exception ex) { + throw new UnsupportedOperationException(ex); + } + } + + public JibContainer doJibBuild(JibBuildService.JibBuildConfiguration jibBuildConfiguration, boolean isOfflineMode) throws InvalidImageReferenceException, RegistryException, ExecutionException { + return JibBuildServiceUtil.buildImage(jibBuildConfiguration, log, isOfflineMode); + } + + public static class JibBuildConfiguration { + private ImageConfiguration imageConfiguration; + + private ImageFormat imageFormat; + + private Credential credential; + + private Path fatJarPath; + + private String targetDir; + + private String outputDir; + + private JibBuildConfiguration() {} + + public ImageConfiguration getImageConfiguration() { return imageConfiguration; } + + public String getTargetDir() { + return targetDir; + } + + public String getOutputDir() { + return outputDir; + } + + public Credential getCredential() { + return credential; + } + + public Path getFatJar() { + return fatJarPath; + } + + public ImageFormat getImageFormat() { + return imageFormat; + } + + public static class Builder { + private final JibBuildConfiguration configutil; + private final KitLogger logger; + + public Builder(KitLogger logger) { + this(null, logger); + } + + public Builder(JibBuildConfiguration that, KitLogger logger) { + this.logger = logger; + if (that == null) { + this.configutil = new JibBuildConfiguration(); + } else { + this.configutil = DeepCopy.copy(that); + } + } + + public Builder imageConfiguration(ImageConfiguration imageConfiguration) { + configutil.imageConfiguration = imageConfiguration; + return this; + } + + public Builder imageFormat(ImageFormat imageFormat) { + configutil.imageFormat = imageFormat; + return this; + } + + public Builder credential(Credential credential) { + configutil.credential = credential; + return this; + } + + public Builder buildDirectory(String buildDir) { + configutil.fatJarPath = JibBuildServiceUtil.getFatJar(buildDir, logger); + return this; + } + + public Builder targetDir(String targetDir) { + configutil.targetDir = targetDir; + return this; + } + + public Builder outputDir(String outputDir) { + configutil.outputDir = outputDir; + return this; + } + + public JibBuildConfiguration build() { + return configutil; + } + } + } +} diff --git a/jkube-kit/build/service/docker/src/main/java/io/jkube/kit/build/service/docker/RegistryService.java b/jkube-kit/build/service/docker/src/main/java/io/jkube/kit/build/service/docker/RegistryService.java index fafcb52a48..b7e1a081b7 100644 --- a/jkube-kit/build/service/docker/src/main/java/io/jkube/kit/build/service/docker/RegistryService.java +++ b/jkube-kit/build/service/docker/src/main/java/io/jkube/kit/build/service/docker/RegistryService.java @@ -20,14 +20,12 @@ import io.jkube.kit.build.api.auth.AuthConfig; import io.jkube.kit.build.service.docker.access.DockerAccess; -import io.jkube.kit.build.service.docker.access.DockerAccessException; import io.jkube.kit.build.service.docker.auth.AuthConfigFactory; import io.jkube.kit.common.KitLogger; import io.jkube.kit.common.util.EnvUtil; import io.jkube.kit.config.image.ImageName; import io.jkube.kit.config.image.build.BuildConfiguration; import io.jkube.kit.config.image.build.ImagePullPolicy; -import org.apache.maven.plugin.MojoExecutionException; import org.apache.maven.settings.Settings; /** @@ -90,10 +88,9 @@ public void pushImages(Collection imageConfigs, * @param pullManager image pull manager * @param registryConfig registry configuration * @param hasImage boolean variable indicating it has image or not - * @throws Exception exception + * @throws IOException exception */ - public void pullImageWithPolicy(String image, ImagePullManager pullManager, RegistryConfig registryConfig, boolean hasImage) - throws Exception { + public void pullImageWithPolicy(String image, ImagePullManager pullManager, RegistryConfig registryConfig, boolean hasImage) throws IOException { // Already pulled, so we don't need to take care if (pullManager.hasAlreadyPulled(image)) { @@ -150,7 +147,7 @@ private boolean imageRequiresPull(boolean hasImage, ImagePullPolicy pullPolicy, } private AuthConfig createAuthConfig(boolean isPush, String user, String registry, RegistryConfig config) - throws Exception { + throws IOException { return config.getAuthConfigFactory().createAuthConfig( isPush, config.isSkipExtendedAuth(), config.getAuthConfig(), diff --git a/jkube-kit/build/service/docker/src/main/java/io/jkube/kit/build/service/docker/auth/AuthConfigFactory.java b/jkube-kit/build/service/docker/src/main/java/io/jkube/kit/build/service/docker/auth/AuthConfigFactory.java index 36c7189fca..ab1f0e4e20 100644 --- a/jkube-kit/build/service/docker/src/main/java/io/jkube/kit/build/service/docker/auth/AuthConfigFactory.java +++ b/jkube-kit/build/service/docker/src/main/java/io/jkube/kit/build/service/docker/auth/AuthConfigFactory.java @@ -123,10 +123,10 @@ public void setLog(KitLogger log) { * @param registry registry to use, might be null in which case a default registry is checked, * @return the authentication configuration or null if none could be found * - * @throws MojoFailureException mojo failure exception + * @throws IOException exception */ public AuthConfig createAuthConfig(boolean isPush, boolean skipExtendedAuth, Map authConfig, Settings settings, String user, String registry) - throws Exception { + throws IOException { AuthConfig ret = createStandardAuthConfig(isPush, authConfig, settings, user, registry); if (ret != null) { @@ -202,7 +202,7 @@ private AuthConfig extendedAuthentication(AuthConfig standardAuthConfig, String * @throws MojoFailureException */ private AuthConfig createStandardAuthConfig(boolean isPush, Map authConfigMap, Settings settings, String user, String registry) - throws Exception { + throws IOException { AuthConfig ret; // Check first for specific configuration based on direction (pull or push), then for a default value @@ -319,7 +319,7 @@ private AuthConfig getAuthConfigFromEC2InstanceRole() throws IOException { } } - private AuthConfig getAuthConfigFromSystemProperties(LookupMode lookupMode) throws Exception { + private AuthConfig getAuthConfigFromSystemProperties(LookupMode lookupMode) throws IOException { Properties props = System.getProperties(); String userKey = lookupMode.asSysProperty(AUTH_USERNAME); String passwordKey = lookupMode.asSysProperty(AUTH_PASSWORD); @@ -336,7 +336,7 @@ private AuthConfig getAuthConfigFromSystemProperties(LookupMode lookupMode) thro } } - private AuthConfig getAuthConfigFromOpenShiftConfig(LookupMode lookupMode, Map authConfigMap) throws MojoExecutionException { + private AuthConfig getAuthConfigFromOpenShiftConfig(LookupMode lookupMode, Map authConfigMap) { Properties props = System.getProperties(); String useOpenAuthModeProp = lookupMode.asSysProperty(AUTH_USE_OPENSHIFT_AUTH); // Check for system property @@ -359,12 +359,12 @@ private AuthConfig getAuthConfigFromOpenShiftConfig(LookupMode lookupMode, Map a } } - private AuthConfig getAuthConfigFromPluginConfiguration(LookupMode lookupMode, Map authConfig) throws Exception { + private AuthConfig getAuthConfigFromPluginConfiguration(LookupMode lookupMode, Map authConfig) throws IllegalStateException { Map mapToCheck = getAuthConfigMapToCheck(lookupMode,authConfig); if (mapToCheck != null && mapToCheck.containsKey(AUTH_USERNAME)) { if (!mapToCheck.containsKey(AUTH_PASSWORD)) { - throw new MojoExecutionException("No 'password' given while using in configuration for mode " + lookupMode); + throw new IllegalStateException("No 'password' given while using in configuration for mode " + lookupMode); } Map cloneConfig = new HashMap<>(mapToCheck); cloneConfig.put(AUTH_PASSWORD, decrypt(cloneConfig.get(AUTH_PASSWORD))); @@ -374,7 +374,7 @@ private AuthConfig getAuthConfigFromPluginConfiguration(LookupMode lookupMode, M } } - private AuthConfig getAuthConfigFromSettings(Settings settings, String user, String registry) throws Exception { + private AuthConfig getAuthConfigFromSettings(Settings settings, String user, String registry) { Server defaultServer = null; Server found; for (Server server : settings.getServers()) { @@ -520,13 +520,13 @@ private AuthConfig parseUser(String userName, Map user) { token, null, null); } - private AuthConfig validateMandatoryOpenShiftLogin(AuthConfig openShiftAuthConfig, String useOpenAuthModeProp) throws MojoExecutionException { + private AuthConfig validateMandatoryOpenShiftLogin(AuthConfig openShiftAuthConfig, String useOpenAuthModeProp) throws IllegalStateException { if (openShiftAuthConfig != null) { return openShiftAuthConfig; } // No login found String kubeConfigEnv = System.getenv("KUBECONFIG"); - throw new MojoExecutionException( + throw new IllegalStateException( String.format("System property %s set, but not active user and/or token found in %s. " + "Please use 'oc login' for connecting to OpenShift.", useOpenAuthModeProp, kubeConfigEnv != null ? kubeConfigEnv : "~/.kube/config")); @@ -545,20 +545,20 @@ private Server checkForServer(Server server, String id, String registry, String return null; } - private String decrypt(String password) throws Exception { + private String decrypt(String password) throws IllegalStateException { try { // Done by reflection since I have classloader issues otherwise Object secDispatcher = container.lookup(SecDispatcher.ROLE, "maven"); Method method = secDispatcher.getClass().getMethod("decrypt",String.class); return (String) method.invoke(secDispatcher,password); } catch (ComponentLookupException e) { - throw new Exception("Error looking security dispatcher",e); + throw new IllegalStateException("Error looking security dispatcher",e); } catch (ReflectiveOperationException e) { - throw new Exception("Cannot decrypt password: " + e.getCause(),e); + throw new IllegalStateException("Cannot decrypt password: " + e.getCause(),e); } } - private AuthConfig createAuthConfigFromServer(Server server) throws Exception { + private AuthConfig createAuthConfigFromServer(Server server) { return new AuthConfig( server.getUsername(), decrypt(server.getPassword()), diff --git a/jkube-kit/build/service/docker/src/main/java/io/jkube/kit/build/service/docker/helper/JibBuildServiceUtil.java b/jkube-kit/build/service/docker/src/main/java/io/jkube/kit/build/service/docker/helper/JibBuildServiceUtil.java new file mode 100644 index 0000000000..20ac732f35 --- /dev/null +++ b/jkube-kit/build/service/docker/src/main/java/io/jkube/kit/build/service/docker/helper/JibBuildServiceUtil.java @@ -0,0 +1,324 @@ +/** + * Copyright 2016 Red Hat, Inc. + * + * Red Hat licenses this file to you 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.jkube.kit.build.service.docker.helper; + +import com.google.cloud.tools.jib.api.AbsoluteUnixPath; +import com.google.cloud.tools.jib.api.CacheDirectoryCreationException; +import com.google.cloud.tools.jib.api.Containerizer; +import com.google.cloud.tools.jib.api.Credential; +import com.google.cloud.tools.jib.api.ImageFormat; +import com.google.cloud.tools.jib.api.ImageReference; +import com.google.cloud.tools.jib.api.InvalidImageReferenceException; +import com.google.cloud.tools.jib.api.Jib; +import com.google.cloud.tools.jib.api.JibContainer; +import com.google.cloud.tools.jib.api.JibContainerBuilder; +import com.google.cloud.tools.jib.api.LayerConfiguration; +import com.google.cloud.tools.jib.api.LogEvent; +import com.google.cloud.tools.jib.api.Port; +import com.google.cloud.tools.jib.api.RegistryException; +import com.google.cloud.tools.jib.api.RegistryImage; +import com.google.cloud.tools.jib.api.TarImage; +import com.google.cloud.tools.jib.event.events.ProgressEvent; +import com.google.cloud.tools.jib.event.events.TimerEvent; +import com.google.cloud.tools.jib.event.progress.ProgressEventHandler; +import com.google.cloud.tools.jib.plugins.common.TimerEventHandler; +import com.google.cloud.tools.jib.plugins.common.logging.ConsoleLogger; +import com.google.cloud.tools.jib.plugins.common.logging.ConsoleLoggerBuilder; +import com.google.cloud.tools.jib.plugins.common.logging.ProgressDisplayGenerator; +import com.google.cloud.tools.jib.plugins.common.logging.SingleThreadedExecutor; +import io.jkube.kit.build.api.auth.AuthConfig; +import io.jkube.kit.build.maven.MavenBuildContext; +import io.jkube.kit.build.service.docker.BuildService; +import io.jkube.kit.build.service.docker.ImageConfiguration; +import io.jkube.kit.build.service.docker.JibBuildService; +import io.jkube.kit.build.service.docker.RegistryService; +import io.jkube.kit.common.KitLogger; +import io.jkube.kit.common.util.EnvUtil; +import io.jkube.kit.common.util.FatJarDetector; +import io.jkube.kit.config.image.ImageName; +import io.jkube.kit.config.image.build.BuildConfiguration; +import org.apache.maven.plugin.MojoExecutionException; + +import java.io.IOException; +import java.nio.file.Path; +import java.nio.file.Paths; +import java.time.Instant; +import java.util.HashSet; +import java.util.List; +import java.util.Set; +import java.util.concurrent.ExecutionException; + +public class JibBuildServiceUtil { + + private JibBuildServiceUtil() {} + + private static final String DEFAULT_JAR_NAME = "/app.jar"; + private static final String DEFAULT_USER_NAME = "fabric8/"; + private static ConsoleLogger consoleKitLogger; + + /** + * Builds a container image using JIB + * @param buildConfiguration jib build configuration + * @param log logging object + * @param offline whether offline build or not + * @return JibContainer + * @throws InvalidImageReferenceException invalid image reference exception + * @throws RegistryException registry exception + * @throws ExecutionException execution exception + */ + public static JibContainer buildImage(JibBuildService.JibBuildConfiguration buildConfiguration, KitLogger log, boolean offline) throws InvalidImageReferenceException, RegistryException, ExecutionException { + ImageConfiguration imageConfiguration = buildConfiguration.getImageConfiguration(); + Credential credential = buildConfiguration.getCredential(); + String outputDir = buildConfiguration.getOutputDir(); + String targetDir = buildConfiguration.getTargetDir(); + Path fatJar = buildConfiguration.getFatJar(); + ImageFormat imageFormat = buildConfiguration.getImageFormat() != null ? buildConfiguration.getImageFormat() : ImageFormat.Docker; + + return buildImage(imageConfiguration, imageFormat, credential, fatJar, targetDir, outputDir, log, offline); + } + + /** + * Builds a container image using Jib from all the following parameters: + * + * @param imageConfiguration image configuration + * @param imageFormat Image format whether docker or OCI + * @param credential registry auth config + * @param fatJar path to fat jar + * @param targetDir target directory + * @param outputDir output directory + * @param log log + * @param isOfflineMode whether to do offline mode or not. + * @return jib container + * @throws InvalidImageReferenceException invalid image reference + * @throws RegistryException error with registry + * @throws ExecutionException problems while making tarball + */ + protected static JibContainer buildImage(ImageConfiguration imageConfiguration, ImageFormat imageFormat, Credential credential, Path fatJar, String targetDir, String outputDir, KitLogger log, boolean isOfflineMode) throws InvalidImageReferenceException, RegistryException, ExecutionException { + String targetImage = imageConfiguration.getName(); + + JibContainerBuilder containerBuilder = getContainerBuilderFromImageConfiguration(imageConfiguration); + containerBuilder = getJibContainerBuilderFromFatJarPath(fatJar, targetDir, containerBuilder); + containerBuilder.setFormat(imageFormat); + + String imageTarName = ImageReference.parse(targetImage).getRepository().concat(".tar"); + TarImage tarImage = TarImage.named(targetImage).saveTo(Paths.get(outputDir + "/" + imageTarName)); + RegistryImage registryImage = getRegistryImage(imageConfiguration, credential); + try { + JibContainer jibContainer = isOfflineMode ? buildContainer(containerBuilder, tarImage, log, isOfflineMode) : buildContainer(containerBuilder, registryImage, log); + log.info("Image %s successfully built" + (isOfflineMode ? "." : " and pushed."), targetImage); + return jibContainer; + } catch (RegistryException re) { + log.info("Building Image Tarball at %s.", imageTarName); + buildContainer(containerBuilder, tarImage, log, false); + log.info(" %s successfully built.", Paths.get(outputDir + "/" + imageTarName)); + throw new RegistryException(re); + } catch (ExecutionException e) { + buildContainer(containerBuilder, tarImage, log, true); + log.info("%s successfully built.", Paths.get(outputDir + "/" + imageTarName)); + throw new ExecutionException(e); + } + } + + public static JibContainer buildContainer(JibContainerBuilder jibContainerBuilder, TarImage image, KitLogger logger, boolean offline) { + try { + if (offline) { + logger.info("Building image tarball in the offline mode."); + } + return jibContainerBuilder.containerize(Containerizer.to(image) + .setOfflineMode(offline)); + + } catch (CacheDirectoryCreationException | IOException | InterruptedException | ExecutionException | RegistryException ex) { + logger.error("Unable to build the image tarball: %s", ex.getMessage()); + throw new IllegalStateException(ex); + } + } + + public static JibContainer buildContainer(JibContainerBuilder jibContainerBuilder, RegistryImage image, KitLogger logger) throws RegistryException, ExecutionException { + try { + consoleKitLogger = getConsoleKitLogger(logger); + + return jibContainerBuilder.containerize(Containerizer.to(image) + .addEventHandler(LogEvent.class, JibBuildServiceUtil::log) + .addEventHandler( + TimerEvent.class, + new TimerEventHandler(message -> consoleKitLogger.log(LogEvent.Level.DEBUG, message))) + .addEventHandler( + ProgressEvent.class, + new ProgressEventHandler( + update -> + consoleKitLogger.setFooter( + ProgressDisplayGenerator.generateProgressDisplay( + update.getProgress(), update.getUnfinishedLeafTasks()))))); + } catch (CacheDirectoryCreationException | IOException | InterruptedException e) { + logger.error("Unable to build the image in the offline mode: %s", e.getMessage()); + throw new IllegalStateException(e); + } + } + + public static void log(LogEvent event) { + consoleKitLogger.log(event.getLevel(), event.getMessage()); + } + + public static ConsoleLogger getConsoleKitLogger(KitLogger logger) { + ConsoleLoggerBuilder consoleKitLoggerBuilder = ConsoleLoggerBuilder + .rich(new SingleThreadedExecutor(), true) + .lifecycle(logger::info); + if (logger.isDebugEnabled()) { + consoleKitLoggerBuilder + .debug(logger::debug) + .info(logger::info); + } + return consoleKitLoggerBuilder.build(); + } + + public static JibBuildService.JibBuildConfiguration getJibBuildConfiguration(BuildService.BuildContext dockerBuildContext, MavenBuildContext mojoParameters, ImageConfiguration imageConfiguration, KitLogger log) throws MojoExecutionException, Exception { + + RegistryService.RegistryConfig registryConfig = dockerBuildContext.getRegistryConfig(); + BuildConfiguration buildImageConfiguration = imageConfiguration.getBuildConfiguration(); + + String targetDir = buildImageConfiguration.getAssemblyConfiguration().getTargetDir(); + + String outputDir = EnvUtil.prepareAbsoluteOutputDirPath(mojoParameters.getOutputDirectory(), mojoParameters.getProject().getBasedir().getAbsolutePath(), "", "").getAbsolutePath(); + + if(targetDir == null) { + targetDir = "/deployments"; + } + + AuthConfig authConfig = registryConfig.getAuthConfigFactory() + .createAuthConfig(true, true, registryConfig.getAuthConfig(), + registryConfig.getSettings(), null, registryConfig.getRegistry()); + + JibBuildService.JibBuildConfiguration.Builder jibBuildConfigurationBuilder = new JibBuildService.JibBuildConfiguration.Builder(log) + .imageConfiguration(imageConfiguration) + .imageFormat(ImageFormat.Docker) + .targetDir(targetDir) + .outputDir(outputDir) + .buildDirectory(mojoParameters.getProject().getBuild().getDirectory()); + if(authConfig != null) { + jibBuildConfigurationBuilder.credential(Credential.from(authConfig.getUsername(), authConfig.getPassword())); + } + return jibBuildConfigurationBuilder.build(); + } + + private static RegistryImage getRegistryImage(ImageConfiguration imageConfiguration, Credential credential) throws InvalidImageReferenceException { + String username = "", password = ""; + String targetImage = imageConfiguration.getName(); + ImageReference imageReference = ImageReference.parse(targetImage); + + if (imageConfiguration.getBuildConfiguration().getTags() != null) { + // Pick first not null tag + String tag = null; + for (String currentTag : imageConfiguration.getBuildConfiguration().getTags()) { + if (currentTag != null) { + tag = currentTag; + break; + } + } + targetImage = new ImageName(imageConfiguration.getName(), tag).getFullName(); + } + + if (credential != null) { + username = credential.getUsername(); + password = credential.getPassword(); + + if (targetImage.contains(DEFAULT_USER_NAME)) { + targetImage = targetImage.replaceFirst(DEFAULT_USER_NAME, username + "/"); + } + } + + String registry = imageConfiguration.getRegistry(); + if (registry != null) { + imageReference = ImageReference.parse(new ImageName(targetImage).getFullName(registry)); + } + + return RegistryImage.named(imageReference).addCredential(username, password); + } + + private static JibContainerBuilder getContainerBuilderFromImageConfiguration(ImageConfiguration imageConfiguration) throws InvalidImageReferenceException { + if (imageConfiguration.getBuildConfiguration() == null) { + return null; + } + + BuildConfiguration buildImageConfiguration = imageConfiguration.getBuildConfiguration(); + JibContainerBuilder jibContainerBuilder = Jib.from(buildImageConfiguration.getFrom()); + if (buildImageConfiguration.getEnv() != null && !buildImageConfiguration.getEnv().isEmpty()) { + jibContainerBuilder.setEnvironment(buildImageConfiguration.getEnv()); + } + + if (buildImageConfiguration.getPorts() != null && !buildImageConfiguration.getPorts().isEmpty()) { + jibContainerBuilder.setExposedPorts(getPortSet(buildImageConfiguration.getPorts())); + } + + if (buildImageConfiguration.getLabels() != null && !buildImageConfiguration.getLabels().isEmpty()) { + jibContainerBuilder.setLabels(buildImageConfiguration.getLabels()); + } + + if (buildImageConfiguration.getEntryPoint() != null) { + jibContainerBuilder.setEntrypoint(buildImageConfiguration.getEntryPoint().asStrings()); + } + + if (buildImageConfiguration.getWorkdir() != null) { + jibContainerBuilder.setWorkingDirectory(AbsoluteUnixPath.get(buildImageConfiguration.getWorkdir())); + } + + if (buildImageConfiguration.getUser() != null) { + jibContainerBuilder.setUser(buildImageConfiguration.getUser()); + } + + if (buildImageConfiguration.getVolumes() != null) { + buildImageConfiguration.getVolumes() + .forEach(volumePath -> jibContainerBuilder.addVolume(AbsoluteUnixPath.get(volumePath))); + } + + jibContainerBuilder.setCreationTime(Instant.now()); + return jibContainerBuilder; + } + + private static JibContainerBuilder getJibContainerBuilderFromFatJarPath(Path fatJar, String targetDir, JibContainerBuilder containerBuilder) { + if (fatJar != null) { + String fatJarName = fatJar.getFileName().toString(); + String jarPath = targetDir + "/" + (fatJarName.isEmpty() ? DEFAULT_JAR_NAME: fatJarName); + containerBuilder = containerBuilder + .addLayer(LayerConfiguration.builder().addEntry(fatJar, AbsoluteUnixPath.get(jarPath)).build()); + } + return containerBuilder; + } + + private static Set getPortSet(List ports) { + + Set portSet = new HashSet(); + for(String port : ports) { + portSet.add(Port.tcp(Integer.parseInt(port))); + } + + return portSet; + } + + public static Path getFatJar(String buildDir, KitLogger log) { + FatJarDetector fatJarDetector = new FatJarDetector(buildDir); + try { + FatJarDetector.Result result = fatJarDetector.scan(); + if(result != null) { + return result.getArchiveFile().toPath(); + } + + } catch (MojoExecutionException e) { + log.error("MOJO Execution exception occurred: %s", e); + throw new UnsupportedOperationException(e); + } + return null; + } +} \ No newline at end of file diff --git a/jkube-kit/generator/java-exec/src/main/java/io/jkube/generator/javaexec/FatJarDetector.java b/jkube-kit/common/src/main/java/io/jkube/kit/common/util/FatJarDetector.java similarity index 95% rename from jkube-kit/generator/java-exec/src/main/java/io/jkube/generator/javaexec/FatJarDetector.java rename to jkube-kit/common/src/main/java/io/jkube/kit/common/util/FatJarDetector.java index 1c80710d42..b83ed6c9bb 100644 --- a/jkube-kit/generator/java-exec/src/main/java/io/jkube/generator/javaexec/FatJarDetector.java +++ b/jkube-kit/common/src/main/java/io/jkube/kit/common/util/FatJarDetector.java @@ -11,7 +11,7 @@ * Contributors: * Red Hat, Inc. - initial API and implementation */ -package io.jkube.generator.javaexec; +package io.jkube.kit.common.util; import java.io.File; import java.io.IOException; @@ -32,11 +32,11 @@ public class FatJarDetector { private File directory; private Result result; - FatJarDetector(String dir) { + public FatJarDetector(String dir) { this.directory = new File(dir); } - Result scan() throws MojoExecutionException { + public Result scan() throws MojoExecutionException { // Scanning is lazy ... if (result == null) { if (!directory.exists()) { diff --git a/jkube-kit/generator/java-exec/src/test/java/io/jkube/generator/javaexec/FatJarDetectorTest.java b/jkube-kit/common/src/test/java/io/jkube/kit/common/util/FatJarDetectorTest.java similarity index 94% rename from jkube-kit/generator/java-exec/src/test/java/io/jkube/generator/javaexec/FatJarDetectorTest.java rename to jkube-kit/common/src/test/java/io/jkube/kit/common/util/FatJarDetectorTest.java index 77d019dba7..7fa129adc4 100644 --- a/jkube-kit/generator/java-exec/src/test/java/io/jkube/generator/javaexec/FatJarDetectorTest.java +++ b/jkube-kit/common/src/test/java/io/jkube/kit/common/util/FatJarDetectorTest.java @@ -11,11 +11,12 @@ * Contributors: * Red Hat, Inc. - initial API and implementation */ -package io.jkube.generator.javaexec; +package io.jkube.kit.common.util; import java.io.File; import java.net.URL; +import io.jkube.kit.common.util.FatJarDetector; import org.apache.maven.plugin.MojoExecutionException; import org.junit.Test; diff --git a/jkube-kit/generator/java-exec/src/test/resources/fatjar-simple/test.jar b/jkube-kit/common/src/test/resources/fatjar-simple/test.jar similarity index 100% rename from jkube-kit/generator/java-exec/src/test/resources/fatjar-simple/test.jar rename to jkube-kit/common/src/test/resources/fatjar-simple/test.jar diff --git a/jkube-kit/generator/java-exec/src/main/java/io/jkube/generator/javaexec/JavaExecGenerator.java b/jkube-kit/generator/java-exec/src/main/java/io/jkube/generator/javaexec/JavaExecGenerator.java index a8200876eb..139e881245 100644 --- a/jkube-kit/generator/java-exec/src/main/java/io/jkube/generator/javaexec/JavaExecGenerator.java +++ b/jkube-kit/generator/java-exec/src/main/java/io/jkube/generator/javaexec/JavaExecGenerator.java @@ -21,6 +21,7 @@ import io.jkube.kit.build.service.docker.ImageConfiguration; import io.jkube.kit.common.Configs; +import io.jkube.kit.common.util.FatJarDetector; import io.jkube.kit.common.util.MavenUtil; import io.jkube.kit.config.image.build.AssemblyConfiguration; import io.jkube.kit.config.image.build.BuildConfiguration; diff --git a/jkube-kit/generator/java-exec/src/test/java/io/jkube/generator/javaexec/JavaExecGeneratorMainClassDeterminationTest.java b/jkube-kit/generator/java-exec/src/test/java/io/jkube/generator/javaexec/JavaExecGeneratorMainClassDeterminationTest.java index 4d72c6d1d0..fac71455a7 100644 --- a/jkube-kit/generator/java-exec/src/test/java/io/jkube/generator/javaexec/JavaExecGeneratorMainClassDeterminationTest.java +++ b/jkube-kit/generator/java-exec/src/test/java/io/jkube/generator/javaexec/JavaExecGeneratorMainClassDeterminationTest.java @@ -23,6 +23,7 @@ import com.fasterxml.jackson.databind.util.ClassUtil; import io.jkube.kit.build.service.docker.ImageConfiguration; import io.jkube.kit.common.KitLogger; +import io.jkube.kit.common.util.FatJarDetector; import io.jkube.kit.config.image.build.AssemblyConfiguration; import io.jkube.kit.config.image.build.OpenShiftBuildStrategy; import io.jkube.kit.config.resource.ProcessorConfig; diff --git a/jkube-kit/jkube-kit-spring-boot/src/main/java/io/jkube/springboot/generator/SpringBootGenerator.java b/jkube-kit/jkube-kit-spring-boot/src/main/java/io/jkube/springboot/generator/SpringBootGenerator.java index 35e683e4b5..0a44a054a8 100644 --- a/jkube-kit/jkube-kit-spring-boot/src/main/java/io/jkube/springboot/generator/SpringBootGenerator.java +++ b/jkube-kit/jkube-kit-spring-boot/src/main/java/io/jkube/springboot/generator/SpringBootGenerator.java @@ -33,7 +33,7 @@ import com.google.common.base.Strings; import io.jkube.generator.api.GeneratorContext; import io.jkube.generator.api.GeneratorMode; -import io.jkube.generator.javaexec.FatJarDetector; +import io.jkube.kit.common.util.FatJarDetector; import io.jkube.generator.javaexec.JavaExecGenerator; import io.jkube.kit.build.service.docker.ImageConfiguration; import io.jkube.kit.common.Configs; diff --git a/jkube-kit/parent/pom.xml b/jkube-kit/parent/pom.xml index 4a3f1713ad..df3608cb9a 100644 --- a/jkube-kit/parent/pom.xml +++ b/jkube-kit/parent/pom.xml @@ -66,9 +66,7 @@ 0.0.13 UTF-8 - - 4.1.0 - + 0.10.0 1.43 3.3.1 0.8.2 @@ -134,6 +132,8 @@ fabric8/jetty-9:${version.image.jetty.upstream} jboss/wildfly:${version.image.wildfly.upstream} 1.19 + 27.0.1-jre + 1.6.1 @@ -303,7 +303,7 @@ com.google.guava guava - 27.0-jre + ${guava.version} @@ -385,6 +385,18 @@ 1.7.25 + + com.google.cloud.tools + jib-core + ${jib.version} + + + + com.google.cloud.tools + jib-maven-plugin + ${jib-maven-plugin.version} + + diff --git a/kubernetes-maven-plugin/plugin/src/main/java/io/jkube/maven/plugin/mojo/build/AbstractDockerMojo.java b/kubernetes-maven-plugin/plugin/src/main/java/io/jkube/maven/plugin/mojo/build/AbstractDockerMojo.java index 3cd654f16f..b5d0fd05e0 100644 --- a/kubernetes-maven-plugin/plugin/src/main/java/io/jkube/maven/plugin/mojo/build/AbstractDockerMojo.java +++ b/kubernetes-maven-plugin/plugin/src/main/java/io/jkube/maven/plugin/mojo/build/AbstractDockerMojo.java @@ -20,6 +20,7 @@ import io.jkube.kit.build.service.docker.DockerAccessFactory; import io.jkube.kit.build.service.docker.ImageConfiguration; import io.jkube.kit.build.service.docker.ImagePullManager; +import io.jkube.kit.build.service.docker.JibBuildService; import io.jkube.kit.build.service.docker.RegistryService; import io.jkube.kit.build.service.docker.ServiceHub; import io.jkube.kit.build.service.docker.ServiceHubFactory; @@ -335,6 +336,15 @@ public abstract class AbstractDockerMojo extends AbstractMojo implements ConfigH @Parameter(property = "jkube.useProjectClasspath", defaultValue = "false") protected boolean useProjectClasspath = false; + @Parameter(property = "jkube.build.jib", defaultValue = "false") + protected boolean isJib; + + /** + * Skip building tags + */ + @Parameter(property = "docker.skip.tag", defaultValue = "false") + protected boolean skipTag; + /** * Resource config for getting annotation and labels to be applied to enriched build objects */ @@ -497,6 +507,10 @@ protected Date getReferenceDate() throws IOException { return referenceDate != null ? referenceDate : new Date(); } + private String determinePullPolicy(BuildConfiguration buildConfig) { + return buildConfig != null && buildConfig.getImagePullPolicy() != null ? buildConfig.getImagePullPolicy() : imagePullPolicy; + } + // used for storing a timestamp protected File getBuildTimestampFile() { return new File(project.getBuild().getDirectory(), DOCKER_BUILD_TIMESTAMP); @@ -650,7 +664,7 @@ protected List getResolvedImages() { } protected boolean isDockerAccessRequired() { - return true; // True in case of kubernetes maven plugin + return Boolean.FALSE.equals(isJib); // True in case of kubernetes maven plugin and if jib mode unset } protected void executeBuildGoal(ServiceHub hub) throws IOException, MojoExecutionException { @@ -687,16 +701,22 @@ protected boolean shouldSkipBecauseOfPomPackaging() { } protected void buildAndTag(ServiceHub hub, ImageConfiguration imageConfig) - throws MojoExecutionException, DockerAccessException { + throws MojoExecutionException, IOException { - try { - // TODO need to refactor d-m-p to avoid this call - EnvUtil.storeTimestamp(getBuildTimestampFile(), getBuildTimestamp()); + EnvUtil.storeTimestamp(getBuildTimestampFile(), getBuildTimestamp()); - jkubeServiceHub.getBuildService().build(imageConfig); + BuildService.BuildContext buildContext = getBuildContext(); + ImagePullManager pullManager = getImagePullManager(determinePullPolicy(imageConfig.getBuildConfiguration()), autoPull); - } catch (Exception ex) { - throw new MojoExecutionException("Failed to execute the build", ex); + if (Boolean.TRUE.equals(isJib)) { + log.info("Building Container image with [[B]]JIB(Java Image Builder)[[B]] mode"); + new JibBuildService(buildContext, createMojoParameters(), log).buildImage(imageConfig, true); + } else { + BuildService buildService = hub.getBuildService(); + buildService.buildImage(imageConfig, pullManager, buildContext); + if (!skipTag) { + buildService.tagImage(imageConfig.getName(), imageConfig); + } } } diff --git a/kubernetes-maven-plugin/plugin/src/main/java/io/jkube/maven/plugin/mojo/build/BuildMojo.java b/kubernetes-maven-plugin/plugin/src/main/java/io/jkube/maven/plugin/mojo/build/BuildMojo.java index d1e285f791..6c54be9270 100644 --- a/kubernetes-maven-plugin/plugin/src/main/java/io/jkube/maven/plugin/mojo/build/BuildMojo.java +++ b/kubernetes-maven-plugin/plugin/src/main/java/io/jkube/maven/plugin/mojo/build/BuildMojo.java @@ -17,7 +17,6 @@ import io.jkube.kit.build.service.docker.ServiceHub; import io.jkube.kit.build.service.docker.access.DockerAccess; import io.jkube.kit.build.service.docker.access.log.LogOutputSpecFactory; -import io.jkube.kit.build.service.docker.auth.AuthConfigFactory; import io.jkube.kit.build.service.docker.config.ConfigHelper; import io.jkube.kit.build.service.docker.helper.AnsiLogger; import io.jkube.kit.config.access.ClusterAccess; @@ -27,10 +26,6 @@ import org.apache.maven.plugins.annotations.LifecyclePhase; import org.apache.maven.plugins.annotations.Mojo; import org.apache.maven.plugins.annotations.ResolutionScope; -import org.codehaus.plexus.PlexusConstants; -import org.codehaus.plexus.PlexusContainer; -import org.codehaus.plexus.context.Context; -import org.codehaus.plexus.context.ContextException; import org.codehaus.plexus.personality.plexus.lifecycle.phase.Contextualizable; import java.io.IOException; diff --git a/kubernetes-maven-plugin/plugin/src/main/java/io/jkube/maven/plugin/mojo/build/PushMojo.java b/kubernetes-maven-plugin/plugin/src/main/java/io/jkube/maven/plugin/mojo/build/PushMojo.java index 315798e18d..17c58b5e2c 100644 --- a/kubernetes-maven-plugin/plugin/src/main/java/io/jkube/maven/plugin/mojo/build/PushMojo.java +++ b/kubernetes-maven-plugin/plugin/src/main/java/io/jkube/maven/plugin/mojo/build/PushMojo.java @@ -14,6 +14,8 @@ package io.jkube.maven.plugin.mojo.build; +import io.jkube.kit.build.service.docker.ImageConfiguration; +import io.jkube.kit.build.service.docker.JibBuildService; import io.jkube.kit.build.service.docker.ServiceHub; import io.jkube.kit.build.service.docker.auth.AuthConfigFactory; import io.jkube.kit.config.access.ClusterAccess; @@ -28,6 +30,8 @@ import org.codehaus.plexus.context.Context; import org.codehaus.plexus.context.ContextException; +import java.util.List; + /** * Uploads the built Docker images to a Docker registry * @@ -74,7 +78,17 @@ public void executeInternal(ServiceHub serviceHub) throws MojoExecutionException } try { - serviceHub.getRegistryService().pushImages(getResolvedImages(), retries, getRegistryConfig(pushRegistry), skipTag); + List imageConfigurationList = getResolvedImages(); + if (Boolean.TRUE.equals(isJib)) { + log.info("Building Container image with [[B]]JIB(Java Image Builder)[[B]] mode"); + JibBuildService jibBuildService = new JibBuildService(getBuildContext(), createMojoParameters(), log); + for (ImageConfiguration imageConfig : imageConfigurationList) { + jibBuildService.buildImage(imageConfig, false); + } + } else { + serviceHub.getRegistryService().pushImages(imageConfigurationList, retries, getRegistryConfig(pushRegistry), skipTag); + } + } catch (Exception exp) { throw new MojoExecutionException(exp.getMessage()); }