Skip to content

Commit

Permalink
refactor (jkube-kit) : Move ConfigHelper.initImageConfiguration to Ge…
Browse files Browse the repository at this point in the history
…neratorManager

+ This code modifying and filtering ImageConfiguration lists is better
  suited to be placed inside GeneratorManager rather than some static
  utility method.
+ Add new fields `buildTimestamp`, `sourceDirectory`, `filter` to
  GeneratorContext

Signed-off-by: Rohan Kumar <rohaan@redhat.com>
  • Loading branch information
rohanKanojia committed Apr 22, 2024
1 parent f9e53c1 commit daec100
Show file tree
Hide file tree
Showing 11 changed files with 258 additions and 280 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -27,7 +27,6 @@
import org.eclipse.jkube.gradle.plugin.GradleLogger;
import org.eclipse.jkube.gradle.plugin.GradleUtil;
import org.eclipse.jkube.gradle.plugin.KubernetesExtension;
import org.eclipse.jkube.kit.build.api.helper.ImageConfigResolver;
import org.eclipse.jkube.kit.common.JKubeConfiguration;
import org.eclipse.jkube.kit.common.KitLogger;
import org.eclipse.jkube.kit.common.RegistryConfig;
Expand All @@ -49,7 +48,6 @@
import org.gradle.api.tasks.Internal;
import org.gradle.api.tasks.TaskAction;

import static org.eclipse.jkube.kit.build.api.helper.ConfigHelper.initImageConfiguration;
import static org.eclipse.jkube.kit.common.JKubeFileInterpolator.interpolate;
import static org.eclipse.jkube.kit.common.util.BuildReferenceDateUtil.getBuildTimestamp;
import static org.eclipse.jkube.kit.config.service.kubernetes.KubernetesClientUtil.updateResourceConfigNamespace;
Expand Down Expand Up @@ -84,26 +82,21 @@ private void init() {
clusterAccess = new ClusterAccess(initClusterConfiguration());
jKubeServiceHub = initJKubeServiceHubBuilder().build();
kubernetesExtension.resources = updateResourceConfigNamespace(kubernetesExtension.getNamespaceOrNull(), kubernetesExtension.resources);
ImageConfigResolver imageConfigResolver = new ImageConfigResolver();
try {
resolvedImages = resolveImages(imageConfigResolver);
final JKubeEnricherContext context = JKubeEnricherContext.builder()
.project(kubernetesExtension.javaProject)
.processorConfig(ProfileUtil.blendProfileWithConfiguration(ProfileUtil.ENRICHER_CONFIG,
kubernetesExtension.getProfileOrNull(),
resolveResourceSourceDirectory(),
kubernetesExtension.enricher))
.images(resolvedImages)
.resources(kubernetesExtension.resources)
.log(kitLogger)
.jKubeBuildStrategy(kubernetesExtension.getBuildStrategyOrDefault())
.build();
final List<String> extraClasspathElements = kubernetesExtension.getUseProjectClassPathOrDefault() ?
kubernetesExtension.javaProject.getCompileClassPathElements() : Collections.emptyList();
enricherManager = new DefaultEnricherManager(context, extraClasspathElements);
} catch (IOException exception) {
kitLogger.error("Error in fetching Build timestamps: " + exception.getMessage());
}
resolvedImages = resolveImages();
final JKubeEnricherContext context = JKubeEnricherContext.builder()
.project(kubernetesExtension.javaProject)
.processorConfig(ProfileUtil.blendProfileWithConfiguration(ProfileUtil.ENRICHER_CONFIG,
kubernetesExtension.getProfileOrNull(),
resolveResourceSourceDirectory(),
kubernetesExtension.enricher))
.images(resolvedImages)
.resources(kubernetesExtension.resources)
.log(kitLogger)
.jKubeBuildStrategy(kubernetesExtension.getBuildStrategyOrDefault())
.build();
final List<String> extraClasspathElements = kubernetesExtension.getUseProjectClassPathOrDefault() ?
kubernetesExtension.javaProject.getCompileClassPathElements() : Collections.emptyList();
enricherManager = new DefaultEnricherManager(context, extraClasspathElements);
}

protected boolean shouldSkip() {
Expand Down Expand Up @@ -172,7 +165,11 @@ protected GeneratorContext.GeneratorContextBuilder initGeneratorContextBuilder()
.runtimeMode(kubernetesExtension.getRuntimeMode())
.strategy(kubernetesExtension.getBuildStrategyOrDefault())
.prePackagePhase(false)
.useProjectClasspath(kubernetesExtension.getUseProjectClassPathOrDefault());
.useProjectClasspath(kubernetesExtension.getUseProjectClassPathOrDefault())
.sourceDirectory(kubernetesExtension.getBuildSourceDirectoryOrDefault())
.buildTimestamp(getBuildTimestamp(null, null, kubernetesExtension.javaProject.getBuildDirectory().getAbsolutePath(),
DOCKER_BUILD_TIMESTAMP))
.filter(kubernetesExtension.getFilterOrNull());
}

protected ClusterConfiguration initClusterConfiguration() {
Expand All @@ -186,14 +183,8 @@ protected final List<File> resolveResourceSourceDirectory() {
}


protected List<ImageConfiguration> resolveImages(ImageConfigResolver imageConfigResolver) throws IOException {
return initImageConfiguration(
getBuildTimestamp(null, null, kubernetesExtension.javaProject.getBuildDirectory().getAbsolutePath(),
DOCKER_BUILD_TIMESTAMP),
kubernetesExtension.images, imageConfigResolver, kitLogger,
kubernetesExtension.getFilter().getOrNull(),
new DefaultGeneratorManager(initGeneratorContextBuilder().build()),
jKubeServiceHub.getConfiguration());
protected List<ImageConfiguration> resolveImages() {
return new DefaultGeneratorManager(initGeneratorContextBuilder().build()).generateAndMerge(kubernetesExtension.images);
}

protected File getManifest(KubernetesClient kc) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -13,15 +13,13 @@
*/
package org.eclipse.jkube.gradle.plugin.task;

import java.io.IOException;
import java.util.List;
import java.util.Optional;
import java.util.Properties;

import javax.inject.Inject;

import org.eclipse.jkube.gradle.plugin.OpenShiftExtension;
import org.eclipse.jkube.kit.build.api.helper.ImageConfigResolver;
import org.eclipse.jkube.kit.config.image.ImageConfiguration;
import org.eclipse.jkube.kit.config.resource.RuntimeMode;

Expand All @@ -37,7 +35,7 @@ public OpenShiftResourceTask(Class<? extends OpenShiftExtension> extensionClass)
}

@Override
public List<ImageConfiguration> resolveImages(ImageConfigResolver imageConfigResolver) throws IOException {
public List<ImageConfiguration> resolveImages() {
RuntimeMode runtimeMode = kubernetesExtension.getRuntimeMode();
final Properties properties = kubernetesExtension.javaProject.getProperties();
if (!properties.contains(DOCKER_IMAGE_USER)) {
Expand All @@ -48,6 +46,6 @@ public List<ImageConfiguration> resolveImages(ImageConfigResolver imageConfigRes
if (!properties.contains(RuntimeMode.JKUBE_EFFECTIVE_PLATFORM_MODE)) {
properties.setProperty(RuntimeMode.JKUBE_EFFECTIVE_PLATFORM_MODE, runtimeMode.toString());
}
return super.resolveImages(imageConfigResolver);
return super.resolveImages();
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -15,22 +15,12 @@

import org.eclipse.jkube.kit.build.api.config.handler.property.PropertyConfigHandler;
import org.eclipse.jkube.kit.build.api.config.handler.property.PropertyMode;
import org.eclipse.jkube.kit.common.JKubeConfiguration;
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.eclipse.jkube.kit.common.JavaProject;
import org.eclipse.jkube.kit.common.KitLogger;
import org.eclipse.jkube.kit.common.util.JKubeProjectUtil;

import java.util.ArrayList;
import java.util.Arrays;
import java.util.Date;
import java.util.HashSet;
import java.util.List;
import java.util.Properties;
import java.util.Set;
import java.util.stream.Collectors;

/**
* Utility class which helps in resolving, customizing, initializing and validating
Expand All @@ -45,32 +35,6 @@ public class ConfigHelper {

private ConfigHelper() {}

/**
* Resolve image with an external image resolver
*
* @param logger Kit Logger
* @param images the original image config list (can be null)
* @param imageResolver the resolver used to extend on an image configuration
* @param imageNameFilter filter to select only certain image configurations with the given name
* @param generatorManager for applying generators on image configurations
* @return a list of resolved and customized image configuration.
*/
private static List<ImageConfiguration> resolveImages(KitLogger logger,
List<ImageConfiguration> images,
Resolver imageResolver,
String imageNameFilter,
GeneratorManager generatorManager) {
List<ImageConfiguration> ret = resolveConfiguration(imageResolver, images);
ret = generatorManager.generate(ret);
final List<ImageConfiguration> filtered = filterImages(imageNameFilter,ret);
if (!ret.isEmpty() && filtered.isEmpty() && imageNameFilter != null) {
final List<String> imageNames = ret.stream().map(ImageConfiguration::getName).collect(Collectors.toList());
logger.warn("None of the resolved images [%s] match the configured filter '%s'",
String.join(",", imageNames), imageNameFilter);
}
return filtered;
}

public static void validateExternalPropertyActivation(JavaProject project, List<ImageConfiguration> images) {
String prop = getExternalConfigActivationProperty(project);
if(prop == null) {
Expand Down Expand Up @@ -111,92 +75,10 @@ public static String getExternalConfigActivationProperty(JavaProject project) {
return value;
}

// Check if the provided image configuration matches the given
public static boolean matchesConfiguredImages(String imageList, ImageConfiguration imageConfig) {
if (imageList == null) {
return true;
}
Set<String> imagesAllowed = new HashSet<>(Arrays.asList(imageList.split("\\s*,\\s*")));
return imagesAllowed.contains(imageConfig.getName()) || imagesAllowed.contains(imageConfig.getAlias());
}

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

// Filter image configuration on name. Given filter should be either null (no filter) or a comma separated
// list of image names which should be used
private static List<ImageConfiguration> filterImages(String nameFilter, List<ImageConfiguration> imagesToFilter) {
List<ImageConfiguration> ret = new ArrayList<>();
for (ImageConfiguration imageConfig : imagesToFilter) {
if (matchesConfiguredImages(nameFilter, imageConfig)) {
ret.add(imageConfig);
}
}
return ret;
}

// Resolve and initialize external configuration
private static List<ImageConfiguration> resolveConfiguration(Resolver imageResolver,
List<ImageConfiguration> unresolvedImages) {
List<ImageConfiguration> ret = new ArrayList<>();
if (unresolvedImages != null) {
for (ImageConfiguration image : unresolvedImages) {
ret.addAll(imageResolver.resolve(image));
}
verifyImageNames(ret);
}
return ret;
}


// Extract authentication information
private static void verifyImageNames(List<ImageConfiguration> ret) {
for (ImageConfiguration config : ret) {
if (config.getName() == null) {
throw new IllegalArgumentException("Configuration error: <image> must have a non-null <name>");
}
}
}

public static List<ImageConfiguration> initImageConfiguration(Date buildTimeStamp, List<ImageConfiguration> unresolvedImages, ImageConfigResolver imageConfigResolver, KitLogger log, String filter, GeneratorManager generatorManager, JKubeConfiguration jKubeConfiguration) {
final ImageNameFormatter imageNameFormatter = new ImageNameFormatter(jKubeConfiguration.getProject(), buildTimeStamp);
// Resolve images
final List<ImageConfiguration> resolvedImages = ConfigHelper.resolveImages(
log,
unresolvedImages,
(ImageConfiguration image) -> imageConfigResolver.resolve(image, jKubeConfiguration.getProject()),
filter, // A filter which image to process
generatorManager); // GeneratorManager which will invoke generator

// Init and validate Image configurations. After this step, getResolvedImages() contains the valid configuration.
for (ImageConfiguration imageConfiguration : resolvedImages) {
imageConfiguration.setName(imageNameFormatter.format(imageConfiguration.getName()));
if (imageConfiguration.getBuild() != null) {
imageConfiguration.getBuild().initAndValidate();
}
printDockerfileInfoIfDockerfileMode(imageConfiguration, log, jKubeConfiguration);
}

return resolvedImages;
}

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

private static void printDockerfileInfoIfDockerfileMode(ImageConfiguration imageConfiguration, KitLogger log, JKubeConfiguration jKubeConfiguration) {
BuildConfiguration buildConfiguration = imageConfiguration.getBuildConfiguration();
if (buildConfiguration != null && buildConfiguration.isDockerFileMode()) {
log.info("Using Dockerfile: %s", buildConfiguration.getDockerFile().getAbsolutePath());
log.info("Using Docker Context Directory: %s", buildConfiguration.getAbsoluteContextDirPath(jKubeConfiguration.getSourceDirectory(), jKubeConfiguration.getBasedir().getAbsolutePath()));
}
}

/**
* A resolver can map one given image configuration to one or more image configurations
* This is e.g. used for resolving properties
*/
private interface Resolver {
List<ImageConfiguration> resolve(ImageConfiguration image);
}

/**
* Format an image name by replacing certain placeholders
*/
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -14,126 +14,32 @@
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 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;

class ConfigHelperTest {
private KitLogger logger;
private ImageConfigResolver imageConfigResolver;
private JavaProject javaProject;
private JKubeConfiguration jKubeConfiguration;
private GeneratorManager generatorManager;

@BeforeEach
void setUp() {
logger = spy(new KitLogger.SilentLogger());
imageConfigResolver = mock(ImageConfigResolver.class, RETURNS_DEEP_STUBS);
generatorManager = mock(GeneratorManager.class);
javaProject = JavaProject.builder()
.groupId("org.eclipse.jkube")
.artifactId("test-java-project")
.version("0.0.1-SNAPSHOT")
.properties(new Properties())
.baseDirectory(new File("dummy-dir"))
.build();
jKubeConfiguration = JKubeConfiguration.builder()
.project(javaProject)
.build();
}

@Test
void initImageConfiguration_withSimpleImageConfiguration_shouldReturnImageConfiguration() {
// Given
ImageConfiguration dummyImageConfiguration = createNewDummyImageConfiguration();
List<ImageConfiguration> images = new ArrayList<>();
images.add(dummyImageConfiguration);
when(imageConfigResolver.resolve(dummyImageConfiguration, javaProject)).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()
.isEqualTo(dummyImageConfiguration);
}

@Test
void initImageConfiguration_whenImageConfigurationNameBlank_thenThrowException() {
// Given
ImageConfiguration imageConfiguration = ImageConfiguration.builder().build();
List<ImageConfiguration> images = Collections.singletonList(imageConfiguration);
when(imageConfigResolver.resolve(imageConfiguration, javaProject)).thenReturn(images);

// When + Then
assertThatIllegalArgumentException()
.isThrownBy(() -> ConfigHelper.initImageConfiguration(new Date(), images, imageConfigResolver, logger, null, generatorManager, jKubeConfiguration))
.withMessage("Configuration error: <image> must have a non-null <name>");
}

@Test
void initImageConfiguration_whenNoMatchForImageFilter_thenLogWarning() {
// Given
ImageConfiguration dummyImageConfiguration = createNewDummyImageConfiguration();
List<ImageConfiguration> images = Collections.singletonList(createNewDummyImageConfiguration());
when(imageConfigResolver.resolve(dummyImageConfiguration, javaProject)).thenReturn(images);
when(generatorManager.generate(images)).thenReturn(images);

// When
ConfigHelper.initImageConfiguration(new Date(), images, imageConfigResolver, logger, "i-dont-exist", generatorManager, jKubeConfiguration);

// Then
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
Expand Down
Loading

0 comments on commit daec100

Please sign in to comment.