Skip to content

Commit

Permalink
Provide support for setting BuildConfig memory/CPU requests and limits
Browse files Browse the repository at this point in the history
  • Loading branch information
rohanKanojia authored and manusa committed Mar 26, 2020
1 parent 3b34640 commit f553aa6
Show file tree
Hide file tree
Showing 10 changed files with 545 additions and 10 deletions.
1 change: 1 addition & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,7 @@ Usage:
* Fix #97: Port of fabric8io/fabric8-maven-plugin#1794 to fix ImageChange triggers not being set in DeploymentConfig when resource fragments are used
* Ported PR fabric8io/fabric8-maven-plugin#1802, Labels are missing for some objects
* Ported PR fabric8io/fabric8-maven-plugin#1805, NullPointerException in ConfigMapEnricher
* Ported PR fabric8io/fabric8-maven-plugin#1772, Support for setting BuildConfig memory/cpu request and limits
* Fix #112: Fix windows specific path error while splitting file path
* Fix #102: HelmMojo works again
* Fix #120: Critical bugs reported by Sonar
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,7 @@
import java.util.Collection;
import java.util.Collections;
import java.util.Date;
import java.util.HashMap;
import java.util.Iterator;
import java.util.LinkedHashMap;
import java.util.LinkedList;
Expand All @@ -37,6 +38,7 @@
import java.util.Set;
import java.util.TreeSet;
import java.util.concurrent.CountDownLatch;
import java.util.regex.Pattern;

import io.fabric8.kubernetes.api.model.Config;
import io.fabric8.kubernetes.api.model.Container;
Expand All @@ -58,6 +60,7 @@
import io.fabric8.kubernetes.api.model.PodList;
import io.fabric8.kubernetes.api.model.PodSpec;
import io.fabric8.kubernetes.api.model.PodStatus;
import io.fabric8.kubernetes.api.model.Quantity;
import io.fabric8.kubernetes.api.model.ReplicationController;
import io.fabric8.kubernetes.api.model.ReplicationControllerSpec;
import io.fabric8.kubernetes.api.model.apps.DaemonSet;
Expand All @@ -84,6 +87,7 @@
import io.fabric8.openshift.api.model.DeploymentConfigSpec;
import io.fabric8.openshift.api.model.Template;
import org.apache.commons.io.FileUtils;
import org.apache.commons.lang3.ArrayUtils;
import org.apache.commons.lang3.StringUtils;
import org.eclipse.jkube.kit.common.KitLogger;
import org.eclipse.jkube.kit.common.ResourceFileType;
Expand All @@ -94,6 +98,10 @@
*/
public class KubernetesHelper {
protected static final String DATE_TIME_FORMAT = "yyyy-MM-dd'T'HH:mm:ssX";
private static final String FILENAME_PATTERN_REGEX = "^(?<name>.*?)(-(?<type>[^-]+))?\\.(?<ext>yaml|yml|json)$";
private static final String PROFILES_PATTERN_REGEX = "^profiles?\\.ya?ml$";
private static final Pattern FILENAME_PATTERN = Pattern.compile(FILENAME_PATTERN_REGEX);
private static final Pattern EXCLUDE_PATTERN = Pattern.compile(PROFILES_PATTERN_REGEX);

/**
* Validates that the given value is valid according to the kubernetes ID parsing rules, throwing an exception if not.
Expand Down Expand Up @@ -828,5 +836,75 @@ public static boolean removeEnvVar(List<EnvVar> envVarList, String name) {
}
return removed;
}


/**
* Get a specific resource fragment ending with some suffix in a specified directory
*
* @param resourceDirFinal resource directory
* @param remotes list remote fragments if provided
* @param resourceNameSuffix resource name suffix
* @param log log object
* @return file if present or null
*/
public static File getResourceFragmentFromSource(File resourceDirFinal, List<String> remotes, String resourceNameSuffix, KitLogger log) {
File[] resourceFiles = listResourceFragments(resourceDirFinal, remotes, log);

if (resourceFiles != null) {
for (File file : resourceFiles) {
if (file.getName().endsWith(resourceNameSuffix)) {
return file;
}
}
}
return null;
}

/**
* Get requests or limit objects from string hashmaps
*
* @param quantity hashmap of strings
* @return hashmap of string to quantity
*/
public static Map<String, Quantity> getQuantityFromString(Map<String, String> quantity) {
Map<String, Quantity> stringQuantityMap = new HashMap<>();
if (quantity != null && !quantity.isEmpty()) {
for (Map.Entry<String, String> entry : quantity.entrySet()) {
stringQuantityMap.put(entry.getKey(), new Quantity(entry.getValue()));
}
}
return stringQuantityMap;
}

protected static File[] listResourceFragments(File localResourceDir, List<String> remotes, KitLogger log) {
File[] resourceFiles = listResourceFragments(localResourceDir);

if(remotes != null) {
File[] remoteResourceFiles = listRemoteResourceFragments(remotes, log);
if (remoteResourceFiles.length > 0) {
resourceFiles = ArrayUtils.addAll(resourceFiles, remoteResourceFiles);
}
}
return resourceFiles;
}

private static File[] listResourceFragments(File resourceDir) {
if (resourceDir == null) {
return new File[0];
}
return resourceDir.listFiles((File dir, String name) -> FILENAME_PATTERN.matcher(name).matches() && !EXCLUDE_PATTERN.matcher(name).matches());
}

private static File[] listRemoteResourceFragments(List<String> remotes, KitLogger log) {
if (!remotes.isEmpty()) {
final File remoteResources = FileUtil.createTempDirectory();
FileUtil.downloadRemotes(remoteResources, remotes, log);

if (remoteResources.isDirectory()) {
return remoteResources.listFiles();
}
}
return new File[0];
}
}

Original file line number Diff line number Diff line change
@@ -0,0 +1,215 @@
/**
* 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.common.util;

import io.fabric8.kubernetes.api.model.EnvVar;
import io.fabric8.kubernetes.api.model.EnvVarBuilder;
import io.fabric8.kubernetes.api.model.Quantity;
import mockit.Mocked;
import org.eclipse.jkube.kit.common.KitLogger;
import org.junit.Test;

import java.io.File;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collections;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertNotNull;
import static org.junit.Assert.assertNull;
import static org.junit.Assert.assertTrue;

public class KubernetesHelperTest {
@Mocked
KitLogger logger;

@Test
public void testListResourceFragments() {
// Given
File localResourceDir = new File(getClass().getResource("/util/fragments").getPath());

// When & Then
assertLocalFragments(KubernetesHelper.listResourceFragments(localResourceDir, null, logger), 2);
}

@Test
public void testResourceFragmentsWithRemotes() {
// Given
List<String> remoteStrList = getRemoteFragments();
File localResourceDir = new File(getClass().getResource("/util/fragments").getPath());

// When
File[] fragments = KubernetesHelper.listResourceFragments(localResourceDir, remoteStrList, logger);

// Then
assertLocalFragments(fragments, 4);
assertTrue(Arrays.stream(fragments).anyMatch( f -> f.getName().equals("deployment.yaml")));
assertTrue(Arrays.stream(fragments).anyMatch( f -> f.getName().equals("sa.yml")));
}

@Test
public void testGetResourceFragmentFromSourceWithSomeResourceDirAndNullRemotes() {
// Given
File localResourceDir = new File(getClass().getResource("/util/fragments").getPath());

// When
File fragmentFile = KubernetesHelper.getResourceFragmentFromSource(localResourceDir, Collections.emptyList(), "service.yml", logger);

// Then
assertTrue(fragmentFile.exists());
assertEquals("service.yml", fragmentFile.getName());
}

@Test
public void testGetResourceFragmentWithNullResourceDirAndNullRemotes() {
assertNull(KubernetesHelper.getResourceFragmentFromSource(null, null, "service.yml", logger));
}

@Test
public void testGetResourceFragmentFromSourceWithNullResourceDirAndSomeRemotes() {
// Given
List<String> remotes = getRemoteFragments();

// When
File fragmentFile = KubernetesHelper.getResourceFragmentFromSource(null, remotes, "deployment.yaml", logger);

// Then
assertTrue(fragmentFile.exists());
assertEquals("deployment.yaml", fragmentFile.getName());
}

@Test
public void testGetResourceFragmentFromSourceWithSomeResourceDirAndSomeRemotes() {
// Given
File localResourceDir = new File(getClass().getResource("/util/fragments").getPath());
List<String> remotes = getRemoteFragments();

// When
File fragmentFile = KubernetesHelper.getResourceFragmentFromSource(localResourceDir, remotes, "sa.yml", logger);

// Then
assertTrue(fragmentFile.exists());
assertEquals("sa.yml", fragmentFile.getName());
}

@Test
public void testGetQuantityFromString() {
// Given
Map<String, String> limitsAsStr = new HashMap<>();
limitsAsStr.put("cpu", "200m");
limitsAsStr.put("memory", "1Gi");

// When
Map<String, Quantity> limitAsQuantity = KubernetesHelper.getQuantityFromString(limitsAsStr);

// Then
assertNotNull(limitAsQuantity);
assertEquals(2, limitAsQuantity.size());
assertEquals(new Quantity("200m"), limitAsQuantity.get("cpu"));
assertEquals(new Quantity("1Gi"), limitAsQuantity.get("memory"));
}

@Test
public void testGetEnvVar() {
// Given
List<EnvVar> envVarList = prepareEnvVarList();

// When
String value1 = KubernetesHelper.getEnvVar(envVarList, "env1", "defaultValue");
String value2 = KubernetesHelper.getEnvVar(envVarList, "JAVA_OPTIONS", "defaultValue");
String value3 = KubernetesHelper.getEnvVar(envVarList, "FOO", "defaultValue");
String value4 = KubernetesHelper.getEnvVar(envVarList, "UNKNOWN", "defaultValue");

// Then
assertEquals("value1", value1);
assertEquals("-Dfoo=bar -Dxyz=abc", value2);
assertEquals("BAR", value3);
assertEquals("defaultValue", value4);
}

@Test
public void testSetEnvVar() {
// Given
List<EnvVar> envVarList = prepareEnvVarList();

// When
boolean statusCode1 = KubernetesHelper.setEnvVar(envVarList, "FOO", "NEW_BAR");
boolean statusCode2 = KubernetesHelper.setEnvVar(envVarList, "UNKNOWN_KEY", "UNKNOWN_VALUE");

// Then
assertTrue(statusCode1);
assertEquals("NEW_BAR", KubernetesHelper.getEnvVar(envVarList, "FOO", "defaultValue"));
assertTrue(statusCode2);
assertEquals("UNKNOWN_VALUE", KubernetesHelper.getEnvVar(envVarList, "UNKNOWN_KEY", "defaultValue"));
}

@Test
public void testRemoveEnvVar() {
// Given
List<EnvVar> envVarList = prepareEnvVarList();

// When
boolean statusCode1 = KubernetesHelper.removeEnvVar(envVarList, "FOO");

// Then
assertTrue(statusCode1);
assertEquals("defaultValue", KubernetesHelper.getEnvVar(envVarList, "FOO", "defaultValue"));
}

@Test
public void testConvertToEnvVarList() {
// Given
Map<String, String> envVarAsStringMap = new HashMap<>();
envVarAsStringMap.put("env1", "value1");
envVarAsStringMap.put("JAVA_OPTIONS", "-Dfoo=bar -Dxyz=abc");
envVarAsStringMap.put("FOO", "BAR");

// When
List<EnvVar> envVarList = KubernetesHelper.convertToEnvVarList(envVarAsStringMap);

// Then
assertNotNull(envVarList);
assertEquals(3, envVarList.size());
assertEquals("value1", KubernetesHelper.getEnvVar(envVarList, "env1", "defaultValue"));
assertEquals("-Dfoo=bar -Dxyz=abc", KubernetesHelper.getEnvVar(envVarList, "JAVA_OPTIONS", "defaultValue"));
assertEquals("BAR", KubernetesHelper.getEnvVar(envVarList, "FOO", "defaultValue"));

}

private void assertLocalFragments(File[] fragments, int expectedSize) {
assertEquals(expectedSize, fragments.length);
assertTrue(Arrays.stream(fragments).anyMatch( f -> f.getName().equals("deployment.yml")));
assertTrue(Arrays.stream(fragments).anyMatch( f -> f.getName().equals("service.yml")));
}

private List<EnvVar> prepareEnvVarList() {
List<EnvVar> envVarList = new ArrayList<>();
envVarList.add(new EnvVarBuilder().withName("env1").withValue("value1").build());
envVarList.add(new EnvVarBuilder().withName("JAVA_OPTIONS").withValue("-Dfoo=bar -Dxyz=abc").build());
envVarList.add(new EnvVarBuilder().withName("FOO").withValue("BAR").build());

return envVarList;
}

private List<String> getRemoteFragments() {
List<String> remoteStrList = new ArrayList<>();

remoteStrList.add("https://gist.githubusercontent.com/lordofthejars/ac2823cec7831697d09444bbaa76cd50/raw/e4b43f1b6494766dfc635b5959af7730c1a58a93/deployment.yaml");
remoteStrList.add("https://gist.githubusercontent.com/rohanKanojia/c4ac4ae5533f0bf0dd77d13c905face7/raw/8a7de1e27c1f437c1ccbd186ed247efd967953ee/sa.yml");
return remoteStrList;
}
}
35 changes: 35 additions & 0 deletions jkube-kit/common/src/test/resources/util/fragments/deployment.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,35 @@
#
# 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
#

spec:
replicas: 1
template:
spec:
volumes:
- name: config
gitRepo:
repository: 'https://github.com/jstrachan/sample-springboot-config.git'
revision: 667ee4db6bc842b127825351e5c9bae5a4fb2147
directory: .
containers:
- volumeMounts:
- name: config
mountPath: /app/config
env:
- name: KUBERNETES_NAMESPACE
valueFrom:
fieldRef:
apiVersion: v1
fieldPath: metadata.namespace
serviceAccount: ribbon
19 changes: 19 additions & 0 deletions jkube-kit/common/src/test/resources/util/fragments/service.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
#
# 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
#

metadata:
annotations:
api.service.kubernetes.io/path: /hello
spec:
type: LoadBalancer
Loading

0 comments on commit f553aa6

Please sign in to comment.