Skip to content
This repository has been archived by the owner on Jun 19, 2024. It is now read-only.

Commit

Permalink
Fix #1770: Provide support for setting BuildConfig memory/CPU request…
Browse files Browse the repository at this point in the history
…s and limits
  • Loading branch information
rohanKanojia committed Jan 9, 2020
1 parent 818b653 commit d35cc3b
Show file tree
Hide file tree
Showing 9 changed files with 225 additions and 11 deletions.
1 change: 1 addition & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@ After this we will switch probably to real [Semantic Versioning 2.0.0](http://se
* Fix #1695: IllegalArgumentException when Spring Boot application.yaml contains integer keys
* Fix #797: spring-boot generator can not handle multi-profile configuration
* Fix #1751: Build Names are suffixed with -s2i regardless of build strategy
* Fix #1770: Support for setting BuildConfig memory/cpu request and limits
* Fix #1755: Spring boot enricher does not produce a proper heath check and liveness check path when "/" is used.
* Feature: Check maven.compiler.target property for base image detection.
* Fix: Enrichers should resolve relative paths against project directory, not working directory
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,44 @@
/**
* Copyright 2016 Red Hat, Inc.
*
* Red Hat licenses this file to you under the Apache License, version
* 2.0 (the "License"); you may not use this file except in compliance
* with the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
* implied. See the License for the specific language governing
* permissions and limitations under the License.
*/
package io.fabric8.maven.core.config;

import java.util.Map;

public class OpenshiftBuildConfig {
private Map<String, String> limits;
private Map<String, String> requests;

public OpenshiftBuildConfig(Map<String, String> limits, Map<String, String> requests) {
this.limits = limits;
this.requests = requests;
}

public Map<String, String> getRequests() {
return requests;
}

public void setRequests(Map<String, String> requests) {
this.requests = requests;
}

public Map<String, String> getLimits() {
return limits;
}

public void setLimits(Map<String, String> resourceLimits) {
this.limits = resourceLimits;
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,6 @@
*/
package io.fabric8.maven.core.config;

import io.fabric8.kubernetes.api.model.extensions.IngressRule;
import org.apache.maven.plugins.annotations.Parameter;

import java.util.List;
Expand Down Expand Up @@ -97,6 +96,9 @@ public class ResourceConfig {
*/
private String routeDomain;

@Parameter
private OpenshiftBuildConfig openshiftBuildConfig;

public Optional<Map<String, String>> getEnv() {
return Optional.ofNullable(env);
}
Expand Down Expand Up @@ -175,6 +177,9 @@ public List<String> getRemotes() {

public String getRouteDomain() { return routeDomain; }

public OpenshiftBuildConfig getOpenshiftBuildConfig() {
return openshiftBuildConfig;
}
// =============================================================================================

public static class Builder {
Expand Down Expand Up @@ -276,6 +281,11 @@ public Builder withRouteDomain(String routeDomain) {
return this;
}

public Builder withOpenshiftBuildConfig(OpenshiftBuildConfig openshiftBuildConfig) {
config.openshiftBuildConfig = openshiftBuildConfig;
return this;
}

public ResourceConfig build() {
return config;
}
Expand Down
23 changes: 23 additions & 0 deletions core/src/main/java/io/fabric8/maven/core/service/BuildService.java
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,7 @@
import io.fabric8.kubernetes.api.model.KubernetesListBuilder;
import io.fabric8.maven.core.config.BuildRecreateMode;
import io.fabric8.maven.core.config.OpenShiftBuildStrategy;
import io.fabric8.maven.core.config.ResourceConfig;
import io.fabric8.maven.docker.config.ImageConfiguration;
import io.fabric8.maven.docker.service.ImagePullManager;
import io.fabric8.maven.docker.util.MojoParameters;
Expand Down Expand Up @@ -72,6 +73,10 @@ class BuildServiceConfig {

private boolean s2iImageStreamLookupPolicyLocal;

private ResourceConfig resourceConfig;

private File resourceDir;

public BuildServiceConfig() {
}

Expand Down Expand Up @@ -121,6 +126,14 @@ public boolean isForcePullEnabled() {
return forcePull;
}

public ResourceConfig getResourceConfig() {
return resourceConfig;
}

public File getResourceDir() {
return resourceDir;
}

public void attachArtifact(String classifier, File destFile) {
if (attacher != null) {
attacher.attach(classifier, destFile);
Expand Down Expand Up @@ -198,6 +211,16 @@ public Builder imagePullManager(ImagePullManager imagePullManager) {
return this;
}

public Builder resourceConfig(ResourceConfig resourceConfig) {
config.resourceConfig = resourceConfig;
return this;
}

public Builder resourceDir(File resourceDir) {
config.resourceDir = resourceDir;
return this;
}

public BuildServiceConfig build() {
return config;
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -37,6 +37,7 @@
import io.fabric8.kubernetes.api.model.LocalObjectReferenceBuilder;
import io.fabric8.kubernetes.api.model.ObjectReference;
import io.fabric8.kubernetes.api.model.Pod;
import io.fabric8.kubernetes.api.model.Quantity;
import io.fabric8.kubernetes.api.model.Status;
import io.fabric8.kubernetes.client.KubernetesClientException;
import io.fabric8.kubernetes.client.Watch;
Expand Down Expand Up @@ -67,6 +68,7 @@
import io.fabric8.openshift.api.model.Build;
import io.fabric8.openshift.api.model.BuildConfig;
import io.fabric8.openshift.api.model.BuildConfigSpec;
import io.fabric8.openshift.api.model.BuildConfigSpecBuilder;
import io.fabric8.openshift.api.model.BuildOutput;
import io.fabric8.openshift.api.model.BuildOutputBuilder;
import io.fabric8.openshift.api.model.BuildSource;
Expand All @@ -84,6 +86,7 @@
public class OpenshiftBuildService implements BuildService {

private static final String DEFAULT_S2I_BUILD_SUFFIX = "-s2i";
public static final String DEFAULT_S2I_SOURCE_TYPE = "Binary";

private final OpenShiftClient client;
private final Logger log;
Expand Down Expand Up @@ -199,7 +202,7 @@ public void postProcess(BuildServiceConfig config) {
config.attachArtifact("is", getImageStreamFile(config));
}

private String updateOrCreateBuildConfig(BuildServiceConfig config, OpenShiftClient client, KubernetesListBuilder builder, ImageConfiguration imageConfig, String openshiftPullSecret) {
protected String updateOrCreateBuildConfig(BuildServiceConfig config, OpenShiftClient client, KubernetesListBuilder builder, ImageConfiguration imageConfig, String openshiftPullSecret) {
ImageName imageName = new ImageName(imageConfig.getName());
String buildName = getS2IBuildName(config, imageName);
String imageStreamName = getImageStreamName(imageName);
Expand Down Expand Up @@ -236,8 +239,8 @@ private void validateSourceType(String buildName, BuildConfigSpec spec) {
BuildSource source = spec.getSource();
if (source != null) {
String sourceType = source.getType();
if (!Objects.equals("Binary", sourceType)) {
log.warn("BuildServiceConfig %s is not of type: 'Binary' but is '%s' !", buildName, sourceType);
if (!Objects.equals(DEFAULT_S2I_SOURCE_TYPE, sourceType)) {
log.warn("BuildServiceConfig %s is not of type: '" + DEFAULT_S2I_SOURCE_TYPE + "' but is '%s' !", buildName, sourceType);
}
}
}
Expand All @@ -257,17 +260,50 @@ private String createBuildConfig(KubernetesListBuilder builder, String buildName
.withNewMetadata()
.withName(buildName)
.endMetadata()
.withNewSpec()
.withNewSource()
.withType("Binary")
.endSource()
.withStrategy(buildStrategyResource)
.withOutput(buildOutput)
.endSpec()
.withSpec(getBuildConfigSpec(buildStrategyResource, buildOutput))
.endBuildConfigItem();
return buildName;
}

private BuildConfigSpec getBuildConfigSpec(BuildStrategy buildStrategyResource, BuildOutput buildOutput) {
BuildConfigSpecBuilder specBuilder = null;

// Check for BuildConfig resource fragment
File buildConfigResourceFragment = KubernetesResourceUtil.getResourceFragmentFromSource(config.getResourceDir(), config.getResourceConfig(), "buildconfig.yml", log);
if (buildConfigResourceFragment != null) {
BuildConfig buildConfigFragment = client.buildConfigs().load(buildConfigResourceFragment).get();
specBuilder = new BuildConfigSpecBuilder(buildConfigFragment.getSpec());
} else {
specBuilder = new BuildConfigSpecBuilder();
}

if (specBuilder.buildSource() == null) {
specBuilder.withNewSource()
.withType(DEFAULT_S2I_SOURCE_TYPE)
.endSource();
}

if (specBuilder.buildStrategy() == null) {
specBuilder.withStrategy(buildStrategyResource);
}

if (specBuilder.buildOutput() == null) {
specBuilder.withOutput(buildOutput);
}

if (config.getResourceConfig() != null && config.getResourceConfig().getOpenshiftBuildConfig() != null) {
Map<String, Quantity> limits = KubernetesResourceUtil.getQuantityFromString(config.getResourceConfig().getOpenshiftBuildConfig().getLimits());
if (limits != null && !limits.isEmpty()) {
specBuilder.editOrNewResources().addToLimits(limits).endResources();
}
Map<String, Quantity> requests = KubernetesResourceUtil.getQuantityFromString(config.getResourceConfig().getOpenshiftBuildConfig().getRequests());
if (limits != null && !limits.isEmpty()) {
specBuilder.editOrNewResources().addToRequests(requests).endResources() ;
}
}
return specBuilder.build();
}

private String updateBuildConfig(OpenShiftClient client, String buildName, BuildStrategy buildStrategy,
BuildOutput buildOutput, BuildConfigSpec spec) {
// lets check if the strategy or output has changed and if so lets update the BC
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -40,6 +40,7 @@
import io.fabric8.kubernetes.api.model.PodSpecBuilder;
import io.fabric8.kubernetes.api.model.PodStatus;
import io.fabric8.kubernetes.api.model.PodTemplateSpec;
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 @@ -55,6 +56,7 @@
import io.fabric8.kubernetes.api.model.batch.JobSpec;
import io.fabric8.kubernetes.client.KubernetesClientException;
import io.fabric8.kubernetes.internal.HasMetadataComparator;
import io.fabric8.maven.core.config.ResourceConfig;
import io.fabric8.maven.core.util.FileUtil;
import io.fabric8.maven.core.config.PlatformMode;
import io.fabric8.maven.core.model.GroupArtifactVersion;
Expand Down Expand Up @@ -1086,4 +1088,44 @@ private static boolean isLocalCustomisation(PodSpec podSpec) {
}
return true;
}

/**
* Get a specific resource fragment ending with some suffix
*
* @param resourceDirFinal resource directory
* @param resourceConfig resource config in case remote fragments are provided
* @param resourceNameSuffix resource name suffix
* @param log log object
* @return file if present or null
*/
public static File getResourceFragmentFromSource(File resourceDirFinal, ResourceConfig resourceConfig, String resourceNameSuffix, Logger log) {
if (resourceDirFinal != null) {
File[] resourceFiles = KubernetesResourceUtil.listResourceFragments(resourceDirFinal, resourceConfig != null ? resourceConfig.getRemotes() : null, 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;
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,7 @@
import java.io.FileReader;
import java.io.IOException;
import java.util.Collections;
import java.util.HashMap;
import java.util.LinkedList;
import java.util.List;
import java.util.Map;
Expand All @@ -30,6 +31,8 @@
import io.fabric8.kubernetes.api.model.WatchEvent;
import io.fabric8.maven.core.config.BuildRecreateMode;
import io.fabric8.maven.core.config.OpenShiftBuildStrategy;
import io.fabric8.maven.core.config.OpenshiftBuildConfig;
import io.fabric8.maven.core.config.ResourceConfig;
import io.fabric8.maven.core.service.BuildService;
import io.fabric8.maven.core.service.Fabric8ServiceException;
import io.fabric8.maven.core.util.WebServerEventCollector;
Expand Down Expand Up @@ -65,6 +68,7 @@
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import static junit.framework.TestCase.assertNotNull;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertTrue;

Expand Down Expand Up @@ -456,6 +460,39 @@ public void checkTarPackageSecret() throws Exception {
});
}

@Test
public void testBuildConfigResourceConfig() throws Exception {
retryInMockServer(() -> {
Map<String, String> limitsMap = new HashMap<>();
limitsMap.put("cpu", "100m");
limitsMap.put("memory", "256Mi");

BuildService.BuildServiceConfig config = defaultConfig
.resourceConfig(new ResourceConfig.Builder()
.withOpenshiftBuildConfig(new OpenshiftBuildConfig(limitsMap, null)).build()).build();
OpenShiftMockServer mockServer = new OpenShiftMockServer();

OpenShiftClient client = mockServer.createOpenShiftClient();
final OpenshiftBuildService service = new OpenshiftBuildService(client, logger, dockerServiceHub, config);

ImageConfiguration imageWithEnv = new ImageConfiguration.Builder(image)
.buildConfig(new BuildImageConfiguration.Builder(image.getBuildConfiguration())
.env(Collections.singletonMap("FOO", "BAR"))
.build()
).build();

KubernetesListBuilder builder = new KubernetesListBuilder();
service.createBuildArchive(imageWithEnv);
service.updateOrCreateBuildConfig(config, client, builder, imageWithEnv, null);
BuildConfig buildConfig = (BuildConfig) builder.buildFirstItem();
assertNotNull(buildConfig);
assertNotNull(buildConfig.getSpec().getResources());
assertEquals("256Mi", buildConfig.getSpec().getResources().getLimits().get("memory").getAmount());
assertEquals("100m", buildConfig.getSpec().getResources().getLimits().get("cpu").getAmount());
});

}

@FunctionalInterface
private interface MockServerRetryable {
void run() throws Fabric8ServiceException, MojoExecutionException, IOException;
Expand Down
19 changes: 19 additions & 0 deletions doc/src/main/asciidoc/inc/goals/build/_fabric8-build.adoc
Original file line number Diff line number Diff line change
Expand Up @@ -110,6 +110,25 @@ Both build strategies update an https://docs.openshift.com/enterprise/latest/arc

The https://docs.openshift.com/enterprise/latest/dev_guide/builds.html#defining-a-buildconfig[Build Config] and https://docs.openshift.com/enterprise/latest/architecture/core_concepts/builds_and_image_streams.html#image-streams[Image streams] can be managed by this plugin. If they do not exist, they will be automatically created by `fabric8:build`. If they do already exist, they are reused, except when the `buildRecreate` configuration option (property `fabric8.build.recreate`) is set to a value as described in <<build-goal-configuration, Configuration>>. Also if the provided build strategy is different than the one defined in the existing build configuration, the Build Config is edited to reflect the new type (which in turn removes all build associated with the previous build).

If you want to configure memory/cpu requests and limits related to `BuildConfig`, you can either provide them as in plugin configuration or as a resource fragment in `src/main/fabric8` directory. for XML configuration it needs to be done like this:
```
<plugin>
<groupId>io.fabric8</groupId>
<artifactId>fabric8-maven-plugin</artifactId>
<version>${plugin.version}</version>
<configuration>
<resources>
<openshiftBuildConfig>
<limits>
<cpu>100m</cpu>
<memory>256Mi</memory>
</limits>
</openshiftBuildConfig>
</resources>
</configuration>
</plugin>
```

This image stream created can then be directly referenced from https://docs.openshift.com/enterprise/latest/architecture/core_concepts/deployments.html#deployments-and-deployment-configurations[Deployment Configuration] objects created by <<fabric8:resource>>.
By default, image streams are created with a local lookup policy, so that they can be used also by other resources such as Deployments or StatefulSets.
This behavior can be turned off by setting the `fabric8.s2i.imageStreamLookupPolicyLocal` property to `false` when building the project.
Expand Down
Loading

0 comments on commit d35cc3b

Please sign in to comment.