Skip to content

SONARIAC-1312 S6897: Should not raise with LimitRange in the same namespace setting Storage Requests #1389

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

Merged
merged 5 commits into from
Jun 28, 2024
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
Expand Up @@ -40,7 +40,11 @@ public static Predicate<YamlTree> isEqualTo(String parameter) {
}

public static Predicate<YamlTree> isSet() {
return t -> TextUtils.matchesValue(t, value -> !STRINGS_CONSIDERED_AS_EMPTY.contains(value)).isTrue();
return t -> TextUtils.matchesValue(t, isSetString()).isTrue();
}

public static Predicate<String> isSetString() {
return value -> !STRINGS_CONSIDERED_AS_EMPTY.contains(value);
}

public static Predicate<YamlTree> startsWith(List<String> strings) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -19,28 +19,23 @@
*/
package org.sonar.iac.kubernetes.checks;

import java.util.Set;
import org.sonar.iac.kubernetes.model.LimitRange;
import java.util.Map;
import org.sonar.iac.kubernetes.model.LimitRangeItem;

public abstract class AbstractLimitCheck extends AbstractResourceManagementCheck<LimitRange> {
public abstract class AbstractLimitCheck extends AbstractResourceManagementCheck {

private static final String RESOURCE_MANAGEMENT_TYPE = "limits";
private static final Set<String> LIMIT_TYPES = Set.of("Pod", "Container");

String getResourceManagementName() {
return RESOURCE_MANAGEMENT_TYPE;
}

@Override
Class<LimitRange> getGlobalResourceType() {
return LimitRange.class;
}

protected Set<String> getLimitTypes() {
return LIMIT_TYPES;
}

abstract String getResourceName();

abstract String getMessage();

@Override
Map<String, String> retrieveLimitRangeItemMap(LimitRangeItem limitRangeItem) {
return limitRangeItem.defaultMap();
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -19,18 +19,19 @@
*/
package org.sonar.iac.kubernetes.checks;

import org.sonar.iac.kubernetes.model.LimitRange;
import java.util.Map;
import org.sonar.iac.kubernetes.model.LimitRangeItem;

public abstract class AbstractRequestCheck extends AbstractResourceManagementCheck<LimitRange> {
public abstract class AbstractRequestCheck extends AbstractResourceManagementCheck {

private static final String RESOURCE_MANAGEMENT_TYPE = "requests";

@Override
Class<LimitRange> getGlobalResourceType() {
return LimitRange.class;
}

String getResourceManagementName() {
return RESOURCE_MANAGEMENT_TYPE;
}

@Override
Map<String, String> retrieveLimitRangeItemMap(LimitRangeItem limitRangeItem) {
return limitRangeItem.defaultRequestMap();
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -21,21 +21,27 @@

import java.util.Collection;
import java.util.List;
import java.util.Map;
import java.util.Optional;
import java.util.Set;
import java.util.stream.Stream;
import javax.annotation.Nullable;
import org.sonar.iac.common.api.tree.HasTextRange;
import org.sonar.iac.common.yaml.TreePredicates;
import org.sonar.iac.common.yaml.object.BlockObject;
import org.sonar.iac.common.yaml.tree.ScalarTree;
import org.sonar.iac.common.yaml.tree.TupleTree;
import org.sonar.iac.kubernetes.model.ProjectResource;
import org.sonar.iac.kubernetes.model.LimitRange;
import org.sonar.iac.kubernetes.model.LimitRangeItem;
import org.sonar.iac.kubernetes.visitors.KubernetesCheckContext;

import static org.sonar.iac.common.yaml.TreePredicates.isSet;
import static org.sonar.iac.common.yaml.TreePredicates.isSetString;

public abstract class AbstractResourceManagementCheck<T extends ProjectResource> extends AbstractKubernetesObjectCheck {
public abstract class AbstractResourceManagementCheck extends AbstractKubernetesObjectCheck {
protected static final String KIND_POD = "Pod";
protected static final List<String> KIND_WITH_TEMPLATE = List.of("DaemonSet", "Deployment", "Job", "ReplicaSet", "ReplicationController", "StatefulSet", "CronJob");
protected static final List<String> KIND_WITH_TEMPLATE = List.of(
"DaemonSet", "Deployment", "Job", "ReplicaSet", "ReplicationController", "StatefulSet", "CronJob");
protected static final Set<String> LIMIT_RANGE_LIMIT_TYPES = Set.of("Pod", "Container");

@Override
boolean shouldVisitWholeDocument() {
Expand Down Expand Up @@ -66,7 +72,7 @@ protected void reportMissingLimit(BlockObject container) {
container.block("resources").block(getResourceManagementName())
.attribute(getResourceName())
.reportIfAbsent(getFirstChildElement(container), getMessage())
.reportIfValue(isSet().negate(), getMessage());
.reportIfValue(TreePredicates.isSet().negate(), getMessage());
}

@Nullable
Expand All @@ -77,19 +83,29 @@ static HasTextRange getFirstChildElement(BlockObject blockObject) {
return null;
}

private Collection<T> getGlobalResources(BlockObject document, String namespace) {
private static Collection<LimitRange> getGlobalResources(BlockObject document, String namespace) {
var projectContext = ((KubernetesCheckContext) document.ctx).projectContext();
var inputFileContext = ((KubernetesCheckContext) document.ctx).inputFileContext();
return projectContext.getProjectResources(namespace, inputFileContext, getGlobalResourceType());
return projectContext.getProjectResources(namespace, inputFileContext, LimitRange.class);
}

abstract Class<T> getGlobalResourceType();
protected boolean hasLimitDefinedGlobally(Collection<LimitRange> globalResources) {
return globalResources.stream()
.flatMap(limitRange -> limitRange.limits().stream())
.anyMatch(this::hasDefinedLimitForResource);
}

protected boolean hasDefinedLimitForResource(LimitRangeItem limitRangeItem) {
var limit = retrieveLimitRangeItemMap(limitRangeItem).get(getResourceName());
return getLimitRangeLimitTypes().contains(limitRangeItem.type()) && isSet(limit);
}

// TODO: make abstract once its implemented for all subclasses
protected boolean hasLimitDefinedGlobally(Collection<T> globalResources) {
return false;
protected Set<String> getLimitRangeLimitTypes() {
return LIMIT_RANGE_LIMIT_TYPES;
}

abstract Map<String, String> retrieveLimitRangeItemMap(LimitRangeItem limitRangeItem);

abstract String getResourceManagementName();

abstract String getResourceName();
Expand All @@ -110,7 +126,7 @@ private static String getNamespace(BlockObject document) {
.orElse("");
}

static boolean startsWithDigit(@Nullable String value) {
return value != null && !value.isEmpty() && Character.isDigit(value.charAt(0));
static boolean isSet(@Nullable String value) {
return value != null && isSetString().test(value);
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -19,36 +19,20 @@
*/
package org.sonar.iac.kubernetes.checks;

import java.util.Collection;
import java.util.Set;
import org.sonar.check.Rule;
import org.sonar.iac.kubernetes.model.LimitRange;
import org.sonar.iac.kubernetes.model.LimitRangeItem;

@Rule(key = "S6869")
public class CpuLimitCheck extends AbstractLimitCheck {
private static final String MESSAGE = "Specify a CPU limit for this container.";
private static final String KEY = "cpu";
private static final Set<String> LIMIT_TYPES = Set.of("Pod", "Container");

@Override
protected boolean hasLimitDefinedGlobally(Collection<LimitRange> globalResources) {
return globalResources.stream()
.flatMap(limitRange -> limitRange.limits().stream())
.anyMatch(CpuLimitCheck::hasCpuLimit);
}
private static final String RESOURCE_NAME = "cpu";

@Override
String getResourceName() {
return KEY;
return RESOURCE_NAME;
}

@Override
String getMessage() {
return MESSAGE;
}

private static boolean hasCpuLimit(LimitRangeItem limitRangeItem) {
return LIMIT_TYPES.contains(limitRangeItem.type()) && limitRangeItem.defaultMap().containsKey("cpu");
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -19,38 +19,20 @@
*/
package org.sonar.iac.kubernetes.checks;

import java.util.Collection;
import java.util.Set;
import org.sonar.check.Rule;
import org.sonar.iac.kubernetes.model.LimitRange;
import org.sonar.iac.kubernetes.model.LimitRangeItem;

@Rule(key = "S6892")
public class CpuRequestCheck extends AbstractRequestCheck {
private static final String MESSAGE = "Specify a CPU request for this container.";
private static final String KEY = "cpu";

private static final Set<String> LIMIT_TYPES = Set.of("Pod", "Container");

@Override
protected boolean hasLimitDefinedGlobally(Collection<LimitRange> globalResources) {
return globalResources.stream()
.flatMap(limitRange -> limitRange.limits().stream())
.anyMatch(CpuRequestCheck::hasCpuLimit);
}
private static final String RESOURCE_NAME = "cpu";

@Override
String getResourceName() {
return KEY;
return RESOURCE_NAME;
}

@Override
String getMessage() {
return MESSAGE;
}

private static boolean hasCpuLimit(LimitRangeItem limitRangeItem) {
var defaultCpuRequest = limitRangeItem.defaultRequestMap().get(KEY);
return LIMIT_TYPES.contains(limitRangeItem.type()) && startsWithDigit(defaultCpuRequest);
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -19,35 +19,20 @@
*/
package org.sonar.iac.kubernetes.checks;

import java.util.Collection;
import org.sonar.check.Rule;
import org.sonar.iac.kubernetes.model.LimitRange;
import org.sonar.iac.kubernetes.model.LimitRangeItem;

@Rule(key = "S6864")
public class MemoryLimitCheck extends AbstractLimitCheck {
private static final String MESSAGE = "Specify a memory limit for this container.";
private static final String KEY = "memory";

@Override
protected boolean hasLimitDefinedGlobally(Collection<LimitRange> globalResources) {
return globalResources.stream()
.flatMap(limitRange -> limitRange.limits().stream())
.anyMatch(this::hasMemoryLimit);
}
private static final String RESOURCE_NAME = "memory";

@Override
String getResourceName() {
return KEY;
return RESOURCE_NAME;
}

@Override
String getMessage() {
return MESSAGE;
}

private boolean hasMemoryLimit(LimitRangeItem limitRangeItem) {
var defaultMemoryLimit = limitRangeItem.defaultMap().get(KEY);
return getLimitTypes().contains(limitRangeItem.type()) && startsWithDigit(defaultMemoryLimit);
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -19,34 +19,16 @@
*/
package org.sonar.iac.kubernetes.checks;

import java.util.Collection;
import java.util.Set;
import org.sonar.check.Rule;
import org.sonar.iac.kubernetes.model.LimitRange;
import org.sonar.iac.kubernetes.model.LimitRangeItem;

@Rule(key = "S6873")
public class MemoryRequestCheck extends AbstractRequestCheck {
private static final String MESSAGE = "Specify a memory request for this container.";
private static final String KEY = "memory";

private static final Set<String> LIMIT_TYPES = Set.of("Pod", "Container");

@Override
protected boolean hasLimitDefinedGlobally(Collection<LimitRange> globalResources) {
return globalResources.stream()
.flatMap(limitRange -> limitRange.limits().stream())
.anyMatch(MemoryRequestCheck::hasMemoryRequest);
}

private static boolean hasMemoryRequest(LimitRangeItem limitRangeItem) {
var defaultMemoryRequest = limitRangeItem.defaultRequestMap().get(KEY);
return LIMIT_TYPES.contains(limitRangeItem.type()) && startsWithDigit(defaultMemoryRequest);
}
private static final String RESOURCE_NAME = "memory";

@Override
String getResourceName() {
return KEY;
return RESOURCE_NAME;
}

@Override
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -19,35 +19,20 @@
*/
package org.sonar.iac.kubernetes.checks;

import java.util.Collection;
import org.sonar.check.Rule;
import org.sonar.iac.kubernetes.model.LimitRange;
import org.sonar.iac.kubernetes.model.LimitRangeItem;

@Rule(key = "S6870")
public class StorageLimitCheck extends AbstractLimitCheck {
private static final String MESSAGE = "Specify a storage limit for this container.";
private static final String KEY = "ephemeral-storage";
private static final String RESOURCE_NAME = "ephemeral-storage";

@Override
String getResourceName() {
return KEY;
return RESOURCE_NAME;
}

@Override
String getMessage() {
return MESSAGE;
}

@Override
protected boolean hasLimitDefinedGlobally(Collection<LimitRange> globalResources) {
return globalResources.stream()
.flatMap(limitRange -> limitRange.limits().stream())
.anyMatch(this::hasStorageLimit);
}

private boolean hasStorageLimit(LimitRangeItem limitRangeItem) {
var defaultStorageLimit = limitRangeItem.defaultMap().get(KEY);
return getLimitTypes().contains(limitRangeItem.type()) && startsWithDigit(defaultStorageLimit);
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -24,11 +24,11 @@
@Rule(key = "S6897")
public class StorageRequestCheck extends AbstractRequestCheck {
private static final String MESSAGE = "Specify a storage request for this container.";
private static final String KEY = "ephemeral-storage";
private static final String RESOURCE_NAME = "ephemeral-storage";

@Override
String getResourceName() {
return KEY;
return RESOURCE_NAME;
}

@Override
Expand Down
Loading