Skip to content

Commit

Permalink
fix: images can be configured with properties
Browse files Browse the repository at this point in the history
Signed-off-by: Marc Nuri <marc@marcnuri.com>
  • Loading branch information
manusa committed May 13, 2024
1 parent c8e5218 commit 2c65b16
Show file tree
Hide file tree
Showing 22 changed files with 848 additions and 1,556 deletions.

This file was deleted.

Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@
* Contributors:
* Red Hat, Inc. - initial API and implementation
*/
package org.eclipse.jkube.kit.build.api.config.handler.property;
package org.eclipse.jkube.kit.build.api.config.property;


/**
Expand All @@ -29,34 +29,19 @@ public enum ConfigKey {
ASSEMBLY_USER("assembly.user"),
ASSEMBLY_MODE("assembly.mode"),
ASSEMBLY_TARLONGFILEMODE("assembly.tarLongFileMode"),
AUTO_REMOVE,
BIND,
BUILD_OPTIONS,
CAP_ADD,
CAP_DROP,
CLEANUP,
CONTAINER_NAME_PATTERN,
CPUSHARES,
CPUS,
CPUSET,
NOCACHE,
CACHEFROM,
OPTIMISE,
CMD,
CONTEXT_DIR,
DEPENDS_ON,
DOMAINNAME,
DNS,
DNS_SEARCH,
DOCKER_ARCHIVE,
DOCKER_FILE,
ENTRYPOINT,
ENV,
ENV_PROPERTY_FILE,
ENV_BUILD("envBuild", ValueCombinePolicy.Merge),
ENV_RUN("envRun", ValueCombinePolicy.Merge),
EXPOSED_PROPERTY_KEY,
EXTRA_HOSTS,
FILTER,
FROM,
FROM_EXT,
Expand All @@ -67,67 +52,23 @@ public enum ConfigKey {
HEALTHCHECK_START_PERIOD("healthcheck.startPeriod"),
HEALTHCHECK_RETRIES("healthcheck.retries"),
HEALTHCHECK_CMD("healthcheck.cmd"),
HOSTNAME,
IMAGE_PULL_POLICY_BUILD("imagePullPolicy.build"),
IMAGE_PULL_POLICY_RUN("imagePullPolicy.run"),
IMAGE_PULL_POLICY,
LABELS(ValueCombinePolicy.Merge),
LINKS,
LOG_ENABLED("log.enabled"),
LOG_PREFIX("log.prefix"),
LOG_DATE("log.date"),
LOG_FILE("log.file"),
LOG_COLOR("log.color"),
LOG_DRIVER_NAME("log.driver.name"),
LOG_DRIVER_OPTS("log.driver.opts"),
MAINTAINER,
MEMORY,
MEMORY_SWAP,
NAME,
NET,
NETWORK_MODE("network.mode"),
NETWORK_NAME("network.name"),
NETWORK_ALIAS("network.alias"),
PORT_PROPERTY_FILE,
PORTS(ValueCombinePolicy.Merge),
PRIVILEGED,
READ_ONLY,
REGISTRY,
RESTART_POLICY_NAME("restartPolicy.name"),
RESTART_POLICY_RETRY("restartPolicy.retry"),
SHELL,
RUN,
SECURITY_OPTS,
SHMSIZE,
SKIP_BUILD("skip.build"),
SKIP_RUN("skip.run"),
SKIP,
TAGS(ValueCombinePolicy.Merge),
TMPFS,
ULIMITS,
USER,
VOLUMES,
VOLUMES_FROM,
WAIT_LOG("wait.log"),
WAIT_TIME("wait.time"),
WAIT_HEALTHY("wait.healthy"),
WAIT_URL("wait.url"),
WAIT_HTTP_URL("wait.http.url"),
WAIT_HTTP_METHOD("wait.http.method"),
WAIT_HTTP_STATUS("wait.http.status"),
WAIT_KILL("wait.kill"),
WAIT_EXEC_POST_START("wait.exec.postStart"),
WAIT_EXEC_PRE_STOP("wait.exec.preStop"),
WAIT_EXEC_BREAK_ON_ERROR("wait.exec.breakOnError"),
WAIT_EXIT("wait.exit"),
WAIT_SHUTDOWN("wait.shutdown"),
WAIT_TCP_MODE("wait.tcp.mode"),
WAIT_TCP_HOST("wait.tcp.host"),
WAIT_TCP_PORT("wait.tcp.port"),
WATCH_INTERVAL("watch.interval"),
WATCH_MODE("watch.mode"),
WATCH_POSTGOAL("watch.postGoal"),
WATCH_POSTEXEC("watch.postExec"),
WORKDIR,
WORKING_DIR;
WORKDIR;

ConfigKey() {
this(ValueCombinePolicy.Replace);
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,162 @@
/*
* 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.kit.build.api.config.property;

import org.apache.commons.lang3.StringUtils;
import org.eclipse.jkube.kit.config.image.ImageConfiguration;
import org.eclipse.jkube.kit.config.image.WatchImageConfiguration;
import org.eclipse.jkube.kit.common.JavaProject;
import org.eclipse.jkube.kit.common.util.EnvUtil;
import org.eclipse.jkube.kit.common.util.JKubeProjectUtil;
import org.eclipse.jkube.kit.common.util.MapUtil;
import org.eclipse.jkube.kit.common.Arguments;
import org.eclipse.jkube.kit.common.AssemblyConfiguration;
import org.eclipse.jkube.kit.config.image.build.BuildConfiguration;
import org.eclipse.jkube.kit.config.image.build.HealthCheckConfiguration;

import java.util.Collections;
import java.util.LinkedHashSet;
import java.util.List;
import java.util.Map;
import java.util.Optional;
import java.util.Properties;
import java.util.Set;
import java.util.function.Function;

public class PropertyConfigResolver {

private static final String DEFAULT_PREFIX = "jkube.container-image";
// The ValueProvider class has more functionality than the one used by the PropertyConfigResolver class.
// Let's add some default values so that we can conserve the ValueProvider as is and still leverage its functionality.
private static final PropertyMode DEFAULT_MODE = PropertyMode.OVERRIDE;

public final ImageConfiguration resolve(ImageConfiguration fromConfig, JavaProject project) {
final Properties properties = JKubeProjectUtil.getPropertiesWithSystemOverrides(project);
final String prefix = StringUtils.isBlank(fromConfig.getPropertyResolverPrefix()) ?
DEFAULT_PREFIX : fromConfig.getPropertyResolverPrefix();
final ValueProvider valueProvider = new ValueProvider(prefix, properties, DEFAULT_MODE);
return ImageConfiguration.builder()
.name(valueProvider.getString(ConfigKey.NAME, fromConfig.getName()))
.alias(valueProvider.getString(ConfigKey.ALIAS, fromConfig.getAlias()))
.build(extractBuildConfiguration(fromConfig, valueProvider))
.watch(extractWatchConfig(fromConfig, valueProvider))
.build();
}

private static <T, R> R valueOr(T input, Function<T,R> function, R defaultValue) {
return Optional.ofNullable(input).map(function).orElse(defaultValue);
}

private static <T, R> R valueOrNull(T input, Function<T,R> function) {
return valueOr(input, function, null);
}

private BuildConfiguration extractBuildConfiguration(ImageConfiguration fromConfig, ValueProvider valueProvider) {
final BuildConfiguration config = fromConfig.getBuild() != null ?
fromConfig.getBuild() : new BuildConfiguration();
return config.toBuilder()
.cmd(extractArguments(valueProvider, ConfigKey.CMD, valueOrNull(config, BuildConfiguration::getCmd)))
.cleanup(valueProvider.getString(ConfigKey.CLEANUP, valueOrNull(config, BuildConfiguration::getCleanup)))
.nocache(valueProvider.getBoolean(ConfigKey.NOCACHE, valueOrNull(config, BuildConfiguration::getNocache)))
.clearCacheFrom().cacheFrom(valueProvider.getList(ConfigKey.CACHEFROM, valueOr(config, BuildConfiguration::getCacheFrom, Collections.emptyList())))
.optimise(valueProvider.getBoolean(ConfigKey.OPTIMISE, valueOrNull(config, BuildConfiguration::getOptimise)))
.entryPoint(extractArguments(valueProvider, ConfigKey.ENTRYPOINT, valueOrNull(config, BuildConfiguration::getEntryPoint)))
.assembly(extractAssembly(valueOrNull(config, BuildConfiguration::getAssembly), valueProvider))
.env(MapUtil.mergeMaps(
valueProvider.getMap(ConfigKey.ENV_BUILD, Collections.emptyMap()),
valueProvider.getMap(ConfigKey.ENV, valueOrNull(config, BuildConfiguration::getEnv))
))
.args(valueProvider.getMap(ConfigKey.ARGS, valueOr(config, BuildConfiguration::getArgs, Collections.emptyMap())))
.labels(valueProvider.getMap(ConfigKey.LABELS, valueOr(config, BuildConfiguration::getLabels, Collections.emptyMap())))
.clearPorts().ports(extractPortValues(valueOr(config, BuildConfiguration::getPorts, Collections.emptyList()), valueProvider))
.shell(extractArguments(valueProvider, ConfigKey.SHELL, valueOrNull(config, BuildConfiguration::getShell)))
.clearRunCmds().runCmds(valueProvider.getList(ConfigKey.RUN, valueOr(config, BuildConfiguration::getRunCmds, Collections.emptyList())))
.from(valueProvider.getString(ConfigKey.FROM, valueOrNull(config, BuildConfiguration::getFrom)))
.fromExt(valueProvider.getMap(ConfigKey.FROM_EXT, valueOrNull(config, BuildConfiguration::getFromExt)))
.clearVolumes().volumes(valueProvider.getList(ConfigKey.VOLUMES, valueOr(config, BuildConfiguration::getVolumes, Collections.emptyList())))
.clearTags().tags(valueProvider.getList(ConfigKey.TAGS, valueOr(config, BuildConfiguration::getTags, Collections.emptyList())))
.maintainer(valueProvider.getString(ConfigKey.MAINTAINER, valueOrNull(config, BuildConfiguration::getMaintainer)))
.workdir(valueProvider.getString(ConfigKey.WORKDIR, valueOrNull(config, BuildConfiguration::getWorkdir)))
.skip(valueProvider.getBoolean(ConfigKey.SKIP, valueOrNull(config, BuildConfiguration::getSkip)))
.imagePullPolicy(valueProvider.getString(ConfigKey.IMAGE_PULL_POLICY, valueOrNull(config, BuildConfiguration::getImagePullPolicy)))
.contextDir(valueProvider.getString(ConfigKey.CONTEXT_DIR, valueOrNull(config, BuildConfiguration::getContextDirRaw)))
.dockerArchive(valueProvider.getString(ConfigKey.DOCKER_ARCHIVE, valueOrNull(config, BuildConfiguration::getDockerArchiveRaw)))
.dockerFile(valueProvider.getString(ConfigKey.DOCKER_FILE, valueOrNull(config, BuildConfiguration::getDockerFileRaw)))
.buildOptions(valueProvider.getMap(ConfigKey.BUILD_OPTIONS, valueOrNull(config, BuildConfiguration::getBuildOptions)))
.filter(valueProvider.getString(ConfigKey.FILTER, valueOrNull(config, BuildConfiguration::getFilter)))
.user(valueProvider.getString(ConfigKey.USER, valueOrNull(config, BuildConfiguration::getUser)))
.healthCheck(extractHealthCheck(valueOrNull(config, BuildConfiguration::getHealthCheck), valueProvider))
.build();
}

private AssemblyConfiguration extractAssembly(AssemblyConfiguration originalConfig, ValueProvider valueProvider) {
final AssemblyConfiguration config = originalConfig != null ? originalConfig : new AssemblyConfiguration();
return config.toBuilder()
.targetDir(valueProvider.getString(ConfigKey.ASSEMBLY_BASEDIR, valueOrNull(originalConfig, AssemblyConfiguration::getTargetDir)))
.exportTargetDir(valueProvider.getBoolean(ConfigKey.ASSEMBLY_EXPORT_TARGET_DIR, valueOrNull(originalConfig, AssemblyConfiguration::getExportTargetDir)))
.permissionsString(valueProvider.getString(ConfigKey.ASSEMBLY_PERMISSIONS, valueOrNull(originalConfig, AssemblyConfiguration::getPermissionsRaw)))
.user(valueProvider.getString(ConfigKey.ASSEMBLY_USER, valueOrNull(originalConfig, AssemblyConfiguration::getUser)))
.modeString(valueProvider.getString(ConfigKey.ASSEMBLY_MODE, valueOrNull(originalConfig, AssemblyConfiguration::getModeRaw)))
.tarLongFileMode(valueProvider.getString(ConfigKey.ASSEMBLY_TARLONGFILEMODE, valueOrNull(originalConfig, AssemblyConfiguration::getTarLongFileMode)))
.build();
}

private HealthCheckConfiguration extractHealthCheck(HealthCheckConfiguration originalConfig, ValueProvider valueProvider) {
final Map<String, String> healthCheckProperties = valueProvider.getMap(ConfigKey.HEALTHCHECK, Collections.emptyMap());
if (healthCheckProperties != null && !healthCheckProperties.isEmpty()) {
final HealthCheckConfiguration config = originalConfig != null ? originalConfig : new HealthCheckConfiguration();
return config.toBuilder()
.interval(valueProvider.getString(ConfigKey.HEALTHCHECK_INTERVAL, valueOrNull(originalConfig, HealthCheckConfiguration::getInterval)))
.timeout(valueProvider.getString(ConfigKey.HEALTHCHECK_TIMEOUT, valueOrNull(originalConfig, HealthCheckConfiguration::getTimeout)))
.startPeriod(valueProvider.getString(ConfigKey.HEALTHCHECK_START_PERIOD, valueOrNull(originalConfig, HealthCheckConfiguration::getStartPeriod)))
.retries(valueProvider.getInteger(ConfigKey.HEALTHCHECK_RETRIES, valueOrNull(originalConfig, HealthCheckConfiguration::getRetries)))
.modeString(valueProvider.getString(ConfigKey.HEALTHCHECK_MODE, originalConfig == null || originalConfig.getMode() == null ? null : originalConfig.getMode().name()))
.cmd(extractArguments(valueProvider, ConfigKey.HEALTHCHECK_CMD, valueOrNull(originalConfig, HealthCheckConfiguration::getCmd)))
.build();
} else {
return originalConfig;
}
}

// Extract only the values of the port mapping

private Set<String> extractPortValues(List<String> config, ValueProvider valueProvider) {
final Set<String> ret = new LinkedHashSet<>();
final List<String> ports = valueProvider.getList(ConfigKey.PORTS, config);
if (ports == null) {
return null;
}
final List<String[]> parsedPorts = EnvUtil.splitOnLastColon(ports);
for (String[] port : parsedPorts) {
ret.add(port[1]);
}
return ret;
}

private Arguments extractArguments(ValueProvider valueProvider, ConfigKey configKey, Arguments alternative) {
return valueProvider.getObject(configKey, alternative, raw -> raw != null ? Arguments.builder().shell(raw).build() : null);
}

private WatchImageConfiguration extractWatchConfig(ImageConfiguration fromConfig, ValueProvider valueProvider) {
final WatchImageConfiguration config = fromConfig.getWatchConfiguration() != null ?
fromConfig.getWatchConfiguration() : new WatchImageConfiguration();
return config.toBuilder()
.interval(valueProvider.getInteger(ConfigKey.WATCH_INTERVAL, config.getIntervalRaw()))
.postGoal(valueProvider.getString(ConfigKey.WATCH_POSTGOAL, config.getPostGoal()))
.postExec(valueProvider.getString(ConfigKey.WATCH_POSTEXEC, config.getPostExec()))
.modeString(valueProvider.getString(ConfigKey.WATCH_POSTGOAL, config.getMode() == null ? null : config.getMode().name()))
.build();
}

}
Original file line number Diff line number Diff line change
Expand Up @@ -11,20 +11,20 @@
* Contributors:
* Red Hat, Inc. - initial API and implementation
*/
package org.eclipse.jkube.kit.build.api.config.handler.property;
package org.eclipse.jkube.kit.build.api.config.property;


/**
* Identifies how the {@link PropertyConfigHandler} should treat properties vs configuration
* Identifies how the {@link PropertyConfigResolver} should treat properties vs configuration
* from POM file in the {@link ValueProvider}.
*
* @author Johan Ström
*/
public enum PropertyMode {
Only,
Override,
Fallback,
Skip;
enum PropertyMode {
ONLY,
OVERRIDE,
FALLBACK,
SKIP;

/**
* Given String, parse to a valid property mode.
Expand All @@ -36,7 +36,7 @@ public enum PropertyMode {
*/
static PropertyMode parse(String name) {
if(name == null) {
return PropertyMode.Only;
return PropertyMode.ONLY;
}

name = name.toLowerCase();
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -11,14 +11,14 @@
* Contributors:
* Red Hat, Inc. - initial API and implementation
*/
package org.eclipse.jkube.kit.build.api.config.handler.property;
package org.eclipse.jkube.kit.build.api.config.property;

import org.apache.commons.lang3.StringUtils;

/**
* Dictates how to combine values from different sources. See {@link PropertyConfigHandler} for details.
* Dictates how to combine values from different sources. See {@link PropertyConfigResolver} for details.
*/
public enum ValueCombinePolicy {
enum ValueCombinePolicy {
/**
* The prioritized value fully replaces any other values.
*/
Expand Down
Loading

0 comments on commit 2c65b16

Please sign in to comment.