Skip to content

Commit

Permalink
refactor (jkube-kit) : Move Dockerfile opinionated ImageConfig logic …
Browse files Browse the repository at this point in the history
…from `ConfigHelper.initImageConfiguration` to a dedicated generator

+ ConfigHelper modifies the provided set of ImageConfigurations when it
  detects a `Dockerfile` in project root directory. This is actually a
  feature called Simple Dockerfile Mode. However, this mutation of
  ImageConfiguration from within `initImageConfiguration` does not look
  appropriate. It can be moved to a dedicated generator that will get
  activated when `Dockerfile` is present in project base directory.
+ Add a new generator SimpleDockerfileGenerator, it adds a simple
  ImageConfiguration with Dockerfile configured in build and adds
  it to existing set of ImageConfigurations.

Signed-off-by: Rohan Kumar <rohaan@redhat.com>
  • Loading branch information
rohanKanojia committed Mar 12, 2024
1 parent df35e19 commit ce63991
Show file tree
Hide file tree
Showing 18 changed files with 353 additions and 112 deletions.
6 changes: 6 additions & 0 deletions gradle-plugin/kubernetes/pom.xml
Original file line number Diff line number Diff line change
Expand Up @@ -72,6 +72,12 @@
<artifactId>jkube-kit-generator-java-exec</artifactId>
<version>${jkube.kit.version}</version>
</dependency>

<dependency>
<groupId>org.eclipse.jkube</groupId>
<artifactId>jkube-kit-generator-dockerfile-simple</artifactId>
</dependency>

<dependency>
<groupId>org.eclipse.jkube</groupId>
<artifactId>jkube-kit-generator-karaf</artifactId>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -86,6 +86,7 @@
generator:
# The order given in "includes" is the order in which generators are called
includes:
- dockerfile-simple
- quarkus
- spring-boot
- thorntail-v2
Expand Down Expand Up @@ -118,6 +119,7 @@
generator:
# The order given in "includes" is the order in which generators are called
includes:
- dockerfile-simple
- spring-boot
- thorntail-v2
- wildfly-jar
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -87,6 +87,7 @@
generator:
# The order given in "includes" is the order in which generators are called
includes:
- dockerfile-simple
- quarkus
- spring-boot
- thorntail-v2
Expand Down Expand Up @@ -119,6 +120,7 @@
generator:
# The order given in "includes" is the order in which generators are called
includes:
- dockerfile-simple
- spring-boot
- thorntail-v2
- wildfly-jar
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -23,7 +23,6 @@
import org.eclipse.jkube.kit.common.KitLogger;
import org.eclipse.jkube.kit.common.util.JKubeProjectUtil;

import java.io.File;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Date;
Expand All @@ -33,12 +32,6 @@
import java.util.Set;
import java.util.stream.Collectors;

import static org.eclipse.jkube.kit.build.api.helper.DockerFileUtil.addSimpleDockerfileConfig;
import static org.eclipse.jkube.kit.build.api.helper.DockerFileUtil.createSimpleDockerfileConfig;
import static org.eclipse.jkube.kit.build.api.helper.DockerFileUtil.getTopLevelDockerfile;
import static org.eclipse.jkube.kit.build.api.helper.DockerFileUtil.isSimpleDockerFileMode;
import static org.eclipse.jkube.kit.common.util.PropertiesUtil.getValueFromProperties;

/**
* Utility class which helps in resolving, customizing, initializing and validating
* image configuration.
Expand Down Expand Up @@ -174,16 +167,6 @@ public static List<ImageConfiguration> initImageConfiguration(Date buildTimeStam
filter, // A filter which image to process
generatorManager); // GeneratorManager which will invoke generator

// Check for simple Dockerfile mode
ImageConfiguration dockerFileImageConfig = createImageConfigurationForSimpleDockerfile(resolvedImages, jKubeConfiguration, imageNameFormatter);
if (dockerFileImageConfig != null) {
if (resolvedImages.isEmpty()) {
resolvedImages.add(dockerFileImageConfig);
} else {
resolvedImages.set(0, dockerFileImageConfig);
}
}

// Init and validate Image configurations. After this step, getResolvedImages() contains the valid configuration.
for (ImageConfiguration imageConfiguration : resolvedImages) {
imageConfiguration.setName(imageNameFormatter.format(imageConfiguration.getName()));
Expand All @@ -196,20 +179,6 @@ public static List<ImageConfiguration> initImageConfiguration(Date buildTimeStam
return resolvedImages;
}

private static ImageConfiguration createImageConfigurationForSimpleDockerfile(List<ImageConfiguration> resolvedImages, JKubeConfiguration jKubeConfiguration, ImageNameFormatter imageNameFormatter) {
if (isSimpleDockerFileMode(jKubeConfiguration.getBasedir())) {
File topDockerfile = getTopLevelDockerfile(jKubeConfiguration.getBasedir());
String defaultImageName = imageNameFormatter.format(getValueFromProperties(jKubeConfiguration.getProject().getProperties(),
"jkube.image.name", "jkube.generator.name"));
if (resolvedImages.isEmpty()) {
return createSimpleDockerfileConfig(topDockerfile, defaultImageName);
} else if (resolvedImages.size() == 1 && resolvedImages.get(0).getBuildConfiguration() == null) {
return addSimpleDockerfileConfig(resolvedImages.get(0), topDockerfile);
}
}
return null;
}

// =========================================================================

private static void printDockerfileInfoIfDockerfileMode(ImageConfiguration imageConfiguration, KitLogger log, JKubeConfiguration jKubeConfiguration) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -13,35 +13,36 @@
*/
package org.eclipse.jkube.kit.build.api.helper;

import java.io.File;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collections;
import java.util.Date;
import java.util.List;
import java.util.Properties;

import org.eclipse.jkube.kit.common.JKubeConfiguration;
import org.eclipse.jkube.kit.common.JavaProject;
import org.eclipse.jkube.kit.common.KitLogger;
import org.eclipse.jkube.kit.config.image.GeneratorManager;
import org.eclipse.jkube.kit.config.image.ImageConfiguration;
import org.eclipse.jkube.kit.config.image.build.BuildConfiguration;

import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.Test;

import java.io.File;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collections;
import java.util.Date;
import java.util.List;
import java.util.Objects;
import java.util.Properties;
import org.junit.jupiter.api.io.TempDir;

import static org.assertj.core.api.Assertions.assertThatIllegalArgumentException;
import static org.assertj.core.api.Assertions.assertThatIllegalStateException;
import static org.assertj.core.api.AssertionsForInterfaceTypes.assertThat;
import static org.mockito.ArgumentMatchers.any;
import static org.mockito.ArgumentMatchers.anyString;
import static org.mockito.ArgumentMatchers.eq;
import static org.mockito.Mockito.RETURNS_DEEP_STUBS;
import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.spy;
import static org.mockito.Mockito.verify;
import static org.mockito.Mockito.when;
import static org.mockito.Mockito.eq;

class ConfigHelperTest {
private KitLogger logger;
Expand Down Expand Up @@ -85,56 +86,6 @@ void initImageConfiguration_withSimpleImageConfiguration_shouldReturnImageConfig
.isEqualTo(dummyImageConfiguration);
}

@Test
void initImageConfiguration_withSimpleDockerFileInProjectBaseDir_shouldCreateImageConfiguration() {
List<ImageConfiguration> images = new ArrayList<>();
File dockerFile = new File(Objects.requireNonNull(getClass().getResource("/dummy-javaproject/Dockerfile")).getFile());
jKubeConfiguration = jKubeConfiguration.toBuilder()
.project(javaProject.toBuilder()
.baseDirectory(dockerFile.getParentFile())
.build())
.build();

// When
List<ImageConfiguration> resolvedImages = ConfigHelper.initImageConfiguration(new Date(), images, imageConfigResolver, logger, null, generatorManager, jKubeConfiguration);

// Then
assertThat(resolvedImages).isNotNull()
.singleElement()
.hasFieldOrPropertyWithValue("name", "jkube/test-java-project:latest")
.hasFieldOrPropertyWithValue("build.dockerFile", dockerFile)
.hasFieldOrPropertyWithValue("build.ports", Collections.singletonList("8080"));
}

@Test
void initImageConfiguration_withSimpleDockerFileModeEnabledAndImageConfigurationWithNoBuild_shouldModifyExistingImageConfiguration() {
ImageConfiguration dummyImageConfiguration = ImageConfiguration.builder()
.name("imageconfiguration-no-build:latest")
.build();
List<ImageConfiguration> images = new ArrayList<>();
images.add(dummyImageConfiguration);
File dockerFile = new File(getClass().getResource("/dummy-javaproject/Dockerfile").getFile());
jKubeConfiguration = jKubeConfiguration.toBuilder()
.project(javaProject.toBuilder()
.baseDirectory(dockerFile.getParentFile())
.build())
.build();
when(imageConfigResolver.resolve(dummyImageConfiguration, jKubeConfiguration.getProject())).thenReturn(images);
when(generatorManager.generate(images)).thenReturn(images);

// When
List<ImageConfiguration> resolvedImages = ConfigHelper.initImageConfiguration(new Date(), images, imageConfigResolver, logger, null, generatorManager, jKubeConfiguration);

// Then
assertThat(resolvedImages).isNotNull()
.singleElement()
.hasFieldOrPropertyWithValue("name", "imageconfiguration-no-build:latest")
.hasFieldOrPropertyWithValue("build.dockerFile", dockerFile)
.hasFieldOrPropertyWithValue("build.ports", Collections.singletonList("8080"));
verify(logger).info(eq("Using Dockerfile: %s"), anyString());
verify(logger).info(eq("Using Docker Context Directory: %s"), any(File.class));
}

@Test
void initImageConfiguration_whenImageConfigurationNameBlank_thenThrowException() {
// Given
Expand Down Expand Up @@ -163,6 +114,28 @@ void initImageConfiguration_whenNoMatchForImageFilter_thenLogWarning() {
verify(logger).warn("None of the resolved images [%s] match the configured filter '%s'", "foo/bar:latest", "i-dont-exist");
}

@Test
void initImageConfiguration_whenDockerfileUsed_thenLogDockerfilePathAndContextDir(@TempDir File temporaryFolder) {
File dockerFile = temporaryFolder.toPath().resolve("Dockerfile").toFile();
ImageConfiguration dummyImageConfiguration = ImageConfiguration.builder()
.name("imageconfiguration-no-build:latest")
.build(BuildConfiguration.builder()
.dockerFile(dockerFile.getAbsolutePath())
.build())
.build();
List<ImageConfiguration> images = new ArrayList<>();
images.add(dummyImageConfiguration);
when(imageConfigResolver.resolve(dummyImageConfiguration, jKubeConfiguration.getProject())).thenReturn(images);
when(generatorManager.generate(images)).thenReturn(images);

// When
ConfigHelper.initImageConfiguration(new Date(), images, imageConfigResolver, logger, null, generatorManager, jKubeConfiguration);

// Then
verify(logger).info(eq("Using Dockerfile: %s"), anyString());
verify(logger).info(eq("Using Docker Context Directory: %s"), any(File.class));
}

@Test
void validateExternalPropertyActivation_withMultipleImagesWithoutExplicitExternalConfig_shouldThrowException() {
// Given
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -215,10 +215,6 @@ protected String getAlias() {
}

protected boolean shouldAddGeneratedImageConfiguration(List<ImageConfiguration> configs) {
if (getProject() != null && getProject().getBaseDirectory() != null && getProject().getBaseDirectory().exists()
&& DockerFileUtil.isSimpleDockerFileMode(getContext().getProject().getBaseDirectory())) {
return false;
}
if (containsBuildConfiguration(configs)) {
return Boolean.parseBoolean(getConfigWithFallback(Config.ADD, "jkube.generator.add", "false"));
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -327,23 +327,6 @@ void shouldAddDefaultImage() {
.returns(false, g -> g.shouldAddGeneratedImageConfiguration(Collections.singletonList(ic1)));
}

@Test
@DisplayName("should not add default image in case of simple dockerfile")
void shouldNotAddDefaultImageInCaseOfSimpleDockerfile(@TempDir Path folder) throws IOException {
// Given
File projectBaseDir = Files.createDirectory(folder.resolve("test-project-dir")).toFile();
File dockerFile = new File(projectBaseDir, "Dockerfile");
boolean isTestDockerfileCreated = dockerFile.createNewFile();
when(ctx.getProject()).thenReturn(project);
when(project.getBaseDirectory()).thenReturn(projectBaseDir);
// When
BaseGenerator generator = createGenerator(null);

// Then
assertThat(isTestDockerfileCreated).isTrue();
assertThat(generator.shouldAddGeneratedImageConfiguration(Collections.emptyList())).isFalse();
}

@Test
@DisplayName("should add generated image configuration when add enabled via config, should return true")
void shouldAddGeneratedImageConfiguration_whenAddEnabledViaConfig_shouldReturnTrue() {
Expand Down
47 changes: 47 additions & 0 deletions jkube-kit/generator/dockerfile-simple/pom.xml
Original file line number Diff line number Diff line change
@@ -0,0 +1,47 @@
<?xml version="1.0" encoding="UTF-8"?>
<!--
Copyright (c) 2019 Red Hat, Inc.
This program and the accompanying materials are made
available under the terms of the Eclipse Public License 2.0
which is available at:
https://www.eclipse.org/legal/epl-2.0/
SPDX-License-Identifier: EPL-2.0
Contributors:
Red Hat, Inc. - initial API and implementation
-->
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/maven-v4_0_0.xsd">
<modelVersion>4.0.0</modelVersion>

<parent>
<groupId>org.eclipse.jkube</groupId>
<artifactId>jkube-kit-parent</artifactId>
<version>1.17-SNAPSHOT</version>
<relativePath>../../parent/pom.xml</relativePath>
</parent>

<artifactId>jkube-kit-generator-dockerfile-simple</artifactId>

<name>JKube Kit :: Generator :: Dockerfile Simple</name>

<dependencies>
<dependency>
<groupId>org.eclipse.jkube</groupId>
<artifactId>jkube-kit-generator-api</artifactId>
</dependency>

<dependency>
<groupId>org.junit.jupiter</groupId>
<artifactId>junit-jupiter-engine</artifactId>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.assertj</groupId>
<artifactId>assertj-core</artifactId>
</dependency>
</dependencies>
</project>
Original file line number Diff line number Diff line change
@@ -0,0 +1,67 @@
/*
* Copyright (c) 2019 Red Hat, Inc.
* This program and the accompanying materials are made
* available under the terms of the Eclipse Public License 2.0
* which is available at:
*
* https://www.eclipse.org/legal/epl-2.0/
*
* SPDX-License-Identifier: EPL-2.0
*
* Contributors:
* Red Hat, Inc. - initial API and implementation
*/
package org.eclipse.jkube.generator.dockerfile.simple;

import org.eclipse.jkube.generator.api.GeneratorContext;
import org.eclipse.jkube.generator.api.support.BaseGenerator;
import org.eclipse.jkube.kit.build.api.helper.ImageNameFormatter;
import org.eclipse.jkube.kit.common.JKubeException;
import org.eclipse.jkube.kit.config.image.ImageConfiguration;

import java.io.File;
import java.io.IOException;
import java.util.Date;
import java.util.List;

import static org.eclipse.jkube.kit.build.api.helper.DockerFileUtil.addSimpleDockerfileConfig;
import static org.eclipse.jkube.kit.build.api.helper.DockerFileUtil.createSimpleDockerfileConfig;
import static org.eclipse.jkube.kit.build.api.helper.DockerFileUtil.getTopLevelDockerfile;
import static org.eclipse.jkube.kit.build.api.helper.DockerFileUtil.isSimpleDockerFileMode;
import static org.eclipse.jkube.kit.common.util.BuildReferenceDateUtil.getBuildTimestamp;
import static org.eclipse.jkube.kit.common.util.PropertiesUtil.getValueFromProperties;

public class SimpleDockerfileGenerator extends BaseGenerator {
public SimpleDockerfileGenerator(GeneratorContext context) {
super(context, "dockerfile-simple");
}

@Override
public boolean isApplicable(List<ImageConfiguration> configs) {
return isSimpleDockerFileMode(getContext().getProject().getBaseDirectory()) &&
(configs.isEmpty() || configs.get(0).getBuild() == null);
}

@Override
public List<ImageConfiguration> customize(List<ImageConfiguration> configs, boolean prePackagePhase) {
ImageNameFormatter imageNameFormatter = new ImageNameFormatter(getContext().getProject(), extractBuildTimestampFromProject());
File topDockerfile = getTopLevelDockerfile(getContext().getProject().getBaseDirectory());
String defaultImageName = imageNameFormatter.format(getValueFromProperties(getContext().getProject().getProperties(),
"jkube.image.name", "jkube.generator.name"));
if (configs.isEmpty()) {
configs.add(createSimpleDockerfileConfig(topDockerfile, defaultImageName));
} else if (configs.size() == 1 && configs.get(0).getBuildConfiguration() == null) {
configs.set(0, addSimpleDockerfileConfig(configs.get(0), topDockerfile));
}
return configs;
}

private Date extractBuildTimestampFromProject() {
try {
return getBuildTimestamp(null, null, getContext().getProject().getBuildDirectory().getAbsolutePath(),
"docker/build.timestamp");
} catch (IOException ioException) {
throw JKubeException.launderThrowable("failure in extract project build timestamp", ioException);
}
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
# The order of the generators used is defined in the active profile
# (which is the profile "default" by default)
# You can find the default profiles in "profiles-default.yml"

org.eclipse.jkube.generator.dockerfile.simple.SimpleDockerfileGenerator
Loading

0 comments on commit ce63991

Please sign in to comment.