Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

refactor (jkube-kit/build) : Move buildArgs related logic to a new class BuildArgResolverUtil #2994

Merged
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
@@ -0,0 +1,112 @@
/*
* 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.helper;

import org.apache.commons.lang3.StringUtils;
import org.eclipse.jkube.kit.common.JKubeConfiguration;
import org.eclipse.jkube.kit.common.JKubeException;
import org.eclipse.jkube.kit.config.image.ImageConfiguration;

import java.util.Collections;
import java.util.HashMap;
import java.util.Map;
import java.util.Objects;
import java.util.Properties;
import java.util.stream.Stream;

public class BuildArgResolverUtil {

private static final String ARG_PREFIX = "docker.buildArg.";

private BuildArgResolverUtil() { }

/**
* Merges Docker Build Args from the following sources:
* <ul>
* <li>Build Args specified directly in ImageConfiguration</li>
* <li>Build Args specified via System Properties</li>
* <li>Build Args specified via Project Properties</li>
* <li>Build Args specified via Plugin configuration</li>
* <li>Docker Proxy Build Args detected from ~/.docker/config.json</li>
* </ul>
* @param imageConfig ImageConfiguration where to get the Build Args from.
* @param configuration {@link JKubeConfiguration}.
* @return a Map containing merged Build Args from all sources.
*/
public static Map<String, String> mergeBuildArgs(ImageConfiguration imageConfig, JKubeConfiguration configuration) {
final Map<String, String> buildArgs = new HashMap<>();
Stream.of(
imageConfig.getBuild().getArgs(),
buildArgsFromProperties(System.getProperties()),
buildArgsFromProperties(configuration.getProject().getProperties()),
configuration.getBuildArgs(),
buildArgsFromDockerConfig()
)
.filter(Objects::nonNull)
.flatMap(map -> map.entrySet().stream())
.forEach(entry -> {
if (buildArgs.containsKey(entry.getKey())) {
throw new JKubeException(String.format("Multiple Build Args with the same key: %s=%s and %s=%s",
entry.getKey(), buildArgs.get(entry.getKey()), entry.getKey(), entry.getValue()));
}
buildArgs.put(entry.getKey(), entry.getValue());
});
return buildArgs;
}

private static Map<String, String> buildArgsFromProperties(Properties properties) {
Map<String, String> buildArgs = new HashMap<>();
for (Object keyObj : properties.keySet()) {
String key = (String) keyObj;
if (key.startsWith(ARG_PREFIX)) {
String argKey = key.replaceFirst(ARG_PREFIX, "");
String value = properties.getProperty(key);

if (StringUtils.isNotBlank(value)) {
buildArgs.put(argKey, value);
}
}
}
return buildArgs;
}

private static Map<String, String> buildArgsFromDockerConfig() {
final Map<String, Object> dockerConfig = DockerFileUtil.readDockerConfig();
if (dockerConfig == null) {
return Collections.emptyMap();
}

// add proxies
Map<String, String> buildArgs = new HashMap<>();
if (dockerConfig.containsKey("proxies")) {
final Map<String, Object> proxies = (Map<String, Object>) dockerConfig.get("proxies");
if (proxies.containsKey("default")) {
final Map<String, String> defaultProxyObj = (Map<String, String>) proxies.get("default");
String[] proxyMapping = new String[] {
"httpProxy", "http_proxy",
"httpsProxy", "https_proxy",
"noProxy", "no_proxy",
"ftpProxy", "ftp_proxy"
};

for(int index = 0; index < proxyMapping.length; index += 2) {
if (defaultProxyObj.containsKey(proxyMapping[index])) {
buildArgs.put(ARG_PREFIX + proxyMapping[index+1], defaultProxyObj.get(proxyMapping[index]));
}
}
}
}
return buildArgs;
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,189 @@
/*
* 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.helper;

import org.eclipse.jkube.kit.common.JKubeConfiguration;
import org.eclipse.jkube.kit.common.JKubeException;
import org.eclipse.jkube.kit.common.JavaProject;
import org.eclipse.jkube.kit.common.util.EnvUtil;
import org.eclipse.jkube.kit.config.image.ImageConfiguration;
import org.eclipse.jkube.kit.config.image.build.BuildConfiguration;
import org.junit.jupiter.api.AfterEach;
import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.DisplayName;
import org.junit.jupiter.api.Test;
import org.junit.jupiter.api.io.TempDir;

import java.io.File;
import java.io.IOException;
import java.nio.file.Files;
import java.nio.file.Path;
import java.util.Collections;
import java.util.HashMap;
import java.util.Map;
import java.util.Properties;

import static org.assertj.core.api.Assertions.assertThat;
import static org.assertj.core.api.Assertions.assertThatExceptionOfType;

class BuildArgResolverUtilMergeBuildArgsTest {
private ImageConfiguration imageConfiguration;
private JKubeConfiguration jKubeConfiguration;
private Properties projectProperties;
private Map<String, String> buildArgFromPluginConfiguration;

@BeforeEach
void setUp() {
projectProperties = new Properties();
buildArgFromPluginConfiguration = new HashMap<>();
jKubeConfiguration = JKubeConfiguration.builder()
.project(JavaProject.builder()
.properties(projectProperties)
.build())
.buildArgs(buildArgFromPluginConfiguration)
.build();
imageConfiguration = ImageConfiguration.builder()
.name("image-name")
.build(BuildConfiguration.builder()
.build())
.build();
}

@Test
@DisplayName("build args in image config and project properties")
void whenBuildArgsFromImageConfigAndFromProjectProperties_shouldMergeBuildArgs() {
// Given
projectProperties.setProperty("docker.buildArg.VERSION", "latest");
projectProperties.setProperty("docker.buildArg.FULL_IMAGE", "busybox:latest");
Map<String, String> buildArgImageConfiguration = new HashMap<>();
buildArgImageConfiguration.put("REPO_1", "docker.io/library");
buildArgImageConfiguration.put("IMAGE-1", "openjdk");
imageConfiguration = imageConfiguration.toBuilder()
.build(imageConfiguration.getBuild().toBuilder().args(buildArgImageConfiguration).build())
.build();

// When
Map<String, String> mergedBuildArgs = BuildArgResolverUtil.mergeBuildArgs(imageConfiguration, jKubeConfiguration);

// Then
assertThat(mergedBuildArgs)
.containsEntry("VERSION", "latest")
.containsEntry("FULL_IMAGE", "busybox:latest")
.containsEntry("REPO_1", "docker.io/library")
.containsEntry("IMAGE-1", "openjdk");
}

@Test
@DisplayName("build args in image config, project properties, system properties, plugin configuration")
void fromAllSourcesWithDifferentKeys_shouldMergeBuildArgs() {
// Given
givenBuildArgsFromImageConfiguration("VERSION", "latest");
System.setProperty("docker.buildArg.IMAGE-1", "openjdk");
projectProperties.setProperty("docker.buildArg.REPO_1", "docker.io/library");
givenBuildArgsFromJKubeConfiguration("FULL_IMAGE", "busybox:latest");

// When
Map<String, String> mergedBuildArgs = BuildArgResolverUtil.mergeBuildArgs(imageConfiguration, jKubeConfiguration);

// Then
assertThat(mergedBuildArgs)
.containsEntry("VERSION", "latest")
.containsEntry("FULL_IMAGE", "busybox:latest")
.containsEntry("REPO_1", "docker.io/library")
.containsEntry("IMAGE-1", "openjdk");
}

@Test
@DisplayName("build args in image config and system properties with same key, should throw exception")
void fromBuildConfigurationAndSystemPropertiesWithSameKey_shouldNotMergeBuildArgs() {
// Given
givenBuildArgsFromImageConfiguration("VERSION", "latest");
System.setProperty("docker.buildArg.VERSION", "1.0.0");

// When & Then
assertThatExceptionOfType(JKubeException.class)
.isThrownBy(() -> BuildArgResolverUtil.mergeBuildArgs(imageConfiguration, jKubeConfiguration))
.withMessage("Multiple Build Args with the same key: VERSION=latest and VERSION=1.0.0");
}

@Test
@DisplayName("build args in image config and project properties with same key, should throw exception")
void fromBuildConfigurationAndProjectPropertiesWithSameKey_shouldNotMergeBuildArgs() {
// Given
givenBuildArgsFromImageConfiguration("VERSION", "latest");
projectProperties.setProperty("docker.buildArg.VERSION", "1.0.0");

// When & Then
assertThatExceptionOfType(JKubeException.class)
.isThrownBy(() -> BuildArgResolverUtil.mergeBuildArgs(imageConfiguration, jKubeConfiguration))
.withMessage("Multiple Build Args with the same key: VERSION=latest and VERSION=1.0.0");
}

@Test
@DisplayName("build args in image config and plugin config with same key, should throw exception")
void fromBuildConfigurationAndJKubeConfigurationWithSameKey_shouldNotMergeBuildArgs() {
// Given
givenBuildArgsFromImageConfiguration("VERSION", "latest");
givenBuildArgsFromJKubeConfiguration("VERSION", "1.0.0");

// When & Then
assertThatExceptionOfType(JKubeException.class)
.isThrownBy(() -> BuildArgResolverUtil.mergeBuildArgs(imageConfiguration, jKubeConfiguration))
.withMessage("Multiple Build Args with the same key: VERSION=latest and VERSION=1.0.0");
}

@Test
@DisplayName("should add proxy build args from ~/.docker/config.json")
void shouldAddBuildArgsFromDockerConfig(@TempDir File temporaryFolder) throws IOException {
try {
// Given
Path dockerConfig = temporaryFolder.toPath();
final Map<String, String> env = Collections.singletonMap("DOCKER_CONFIG", dockerConfig.toFile().getAbsolutePath());
EnvUtil.overrideEnvGetter(env::get);
Files.write(dockerConfig.resolve("config.json"), ("{\"proxies\": {\"default\": {\n" +
" \"httpProxy\": \"http://proxy.example.com:3128\",\n" +
" \"httpsProxy\": \"https://proxy.example.com:3129\",\n" +
" \"noProxy\": \"*.test.example.com,.example.org,127.0.0.0/8\"\n" +
" }}}").getBytes());
// When
final Map<String, String> mergedBuildArgs = BuildArgResolverUtil.mergeBuildArgs(imageConfiguration, jKubeConfiguration);
// Then
assertThat(mergedBuildArgs)
.containsEntry("docker.buildArg.http_proxy", "http://proxy.example.com:3128")
.containsEntry("docker.buildArg.https_proxy", "https://proxy.example.com:3129")
.containsEntry("docker.buildArg.no_proxy", "*.test.example.com,.example.org,127.0.0.0/8");
} finally {
EnvUtil.overrideEnvGetter(System::getenv);
}
}

private void givenBuildArgsFromImageConfiguration(String key, String value) {
imageConfiguration = imageConfiguration.toBuilder()
.build(BuildConfiguration.builder()
.args(
Collections.singletonMap(key, value))
.build())
.build();
}

private void givenBuildArgsFromJKubeConfiguration(String key, String value) {
buildArgFromPluginConfiguration.put(key, value);
}

@AfterEach
void clearSystemPropertiesUsedInTests() {
System.clearProperty("docker.buildArg.IMAGE-1");
System.clearProperty("docker.buildArg.VERSION");
}
}
Loading