Skip to content
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
7 changes: 2 additions & 5 deletions .github/workflows/sonarcloud.yml
Original file line number Diff line number Diff line change
Expand Up @@ -32,16 +32,13 @@ jobs:
key: ${{ runner.os }}-sonar
restore-keys: ${{ runner.os }}-sonar

- name: Build and test (generate JaCoCo)
run: mvn -B -DskipTests=false verify

- name: SonarCloud analyze (push) - non-blocking
if: github.event_name != 'pull_request'
env:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
SONAR_TOKEN: ${{ secrets.SONAR_TOKEN }}
run: >-
mvn -B org.sonarsource.scanner.maven:sonar-maven-plugin:sonar
cd domain && mvn verify && mvn -B org.sonarsource.scanner.maven:sonar-maven-plugin:sonar
-Dsonar.host.url=https://sonarcloud.io
-Dsonar.organization=${{ vars.SONAR_ORG }}
-Dsonar.projectKey=${{ vars.SONAR_PROJECT_KEY }}
Expand All @@ -54,7 +51,7 @@ jobs:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
SONAR_TOKEN: ${{ secrets.SONAR_TOKEN }}
run: >-
mvn -B org.sonarsource.scanner.maven:sonar-maven-plugin:sonar
cd domain && mvn verify && mvn -B org.sonarsource.scanner.maven:sonar-maven-plugin:sonar
-Dsonar.host.url=https://sonarcloud.io
-Dsonar.organization=${{ vars.SONAR_ORG }}
-Dsonar.projectKey=${{ vars.SONAR_PROJECT_KEY }}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -23,11 +23,14 @@
import lombok.Getter;
import lombok.Setter;

import java.time.Duration;
import java.time.LocalDateTime;
import java.util.List;
import java.util.OptionalDouble;
import java.util.UUID;

import static fr.ippon.iroco2.domain.calculator.model.emu.SettingName.MEMORY_IN_MEGA_BYTE;
import static fr.ippon.iroco2.domain.estimator.TimeConstant.MS_IN_ONE_MONTH;
import static java.util.Optional.ofNullable;

@Getter
Expand All @@ -52,6 +55,21 @@ public static Component load(UUID id, UUID infrastructureID, String name, LocalD
return new Component(id, infrastructureID, name, lastModificationDate, regionID, service, values);
}

/**
* Calculates the estimated monthly uptime for a component based on its configuration values.
* If no valid configuration values are set, the default uptime ratio is considered as 1.
*
* @return the computed monthly uptime as a {@link Duration}, representing the duration of uptime for one month.
*/
public Duration computeEstimatedMonthlyUptime() {
double averageUptimeRatio = configurationValues.stream()
.filter(configurationValue -> configurationValue.configurationSettingName().isUptimeParameter())
.map(ConfiguredSetting::computeAverageUptimeRatio)
.flatMapToDouble(OptionalDouble::stream)
.reduce(1., (a, b) -> a * b); // standard product. 1 if no valid configuration is set
return Duration.ofMillis((long) (averageUptimeRatio * MS_IN_ONE_MONTH));
}

public double getMemoryInMegaByte() {
return ofNullable(getValue(MEMORY_IN_MEGA_BYTE))
.map(Double::parseDouble)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -19,10 +19,40 @@

import fr.ippon.iroco2.domain.calculator.model.emu.SettingName;

import java.util.OptionalDouble;
import java.util.UUID;

import static fr.ippon.iroco2.domain.estimator.TimeConstant.AVERAGE_DAYS_PER_MONTH;
import static fr.ippon.iroco2.domain.estimator.TimeConstant.MS_IN_ONE_DAY;
import static fr.ippon.iroco2.domain.estimator.TimeConstant.MS_IN_ONE_MONTH;
import static java.lang.Double.parseDouble;

public record ConfiguredSetting(
UUID configurationSettingId,
SettingName configurationSettingName,
String value) {

/**
* Computes the average uptime ratio for this setting according to its type and value.
*
* @return an optional containing the uptime ratio corresponding to this setting.
* Empty if the setting is irrelevant to the uptime ration
*/
public OptionalDouble computeAverageUptimeRatio() {
if (!configurationSettingName.isUptimeParameter()) {
return OptionalDouble.empty();
}

final double value = parseDouble(this.value);
final double averageUptimeRatio = switch (configurationSettingName) {
case INSTANCE_NUMBER, VOLUME_NUMBER, MONTHLY_INVOCATION_COUNT -> value;
case DAYS_ON_PER_MONTH -> value / AVERAGE_DAYS_PER_MONTH;
case DAILY_USAGE_COUNT -> value * AVERAGE_DAYS_PER_MONTH;
case AVERAGE_EXEC_TIME_IN_MS -> value / MS_IN_ONE_MONTH;
case DAILY_RUNNING_TIME_IN_MS -> value / MS_IN_ONE_DAY;
default ->
throw new IllegalStateException("Unexpected configuration setting: '%s'".formatted(configurationSettingName));
};
return OptionalDouble.of(averageUptimeRatio);
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,10 @@
*/
package fr.ippon.iroco2.domain.calculator.model.emu;

import java.util.Set;

public enum SettingName {

INSTANCE_NUMBER,
INSTANCE_TYPE,
STORAGE_IN_MEGA_BYTE,
Expand All @@ -28,5 +31,11 @@ public enum SettingName {
MEMORY_IN_MEGA_BYTE,
DAILY_USAGE_COUNT,
DAYS_ON_PER_MONTH,
VOLUME_NUMBER
VOLUME_NUMBER;

private static final Set<SettingName> UPTIME_PARAMETERS = Set.of(INSTANCE_NUMBER, VOLUME_NUMBER, MONTHLY_INVOCATION_COUNT, DAYS_ON_PER_MONTH, DAILY_USAGE_COUNT, AVERAGE_EXEC_TIME_IN_MS, DAILY_RUNNING_TIME_IN_MS);

public boolean isUptimeParameter() {
return UPTIME_PARAMETERS.contains(this);
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,6 @@
package fr.ippon.iroco2.domain.estimator;

import fr.ippon.iroco2.domain.calculator.model.Component;
import fr.ippon.iroco2.domain.calculator.model.ConfiguredSetting;
import fr.ippon.iroco2.domain.calculator.model.emu.SettingName;
import fr.ippon.iroco2.domain.commons.DomainService;
import fr.ippon.iroco2.domain.commons.exception.FunctionalException;
Expand Down Expand Up @@ -46,12 +45,8 @@
import static fr.ippon.iroco2.domain.calculator.model.emu.SettingName.STORAGE_IN_MEGA_BYTE;
import static fr.ippon.iroco2.domain.commons.model.PayloadConfiguration.INSTANCE_TYPE;
import static fr.ippon.iroco2.domain.commons.model.PayloadConfiguration.S3_STORAGE;
import static fr.ippon.iroco2.domain.estimator.TimeConstant.AVERAGE_DAYS_PER_MONTH;
import static fr.ippon.iroco2.domain.estimator.TimeConstant.MS_IN_ONE_DAY;
import static fr.ippon.iroco2.domain.estimator.TimeConstant.MS_IN_ONE_MONTH;
import static fr.ippon.iroco2.domain.estimator.aws.EC2Instance.TDP_TO_POWER_CONSUMPTION_RATIO;
import static fr.ippon.iroco2.domain.estimator.model.MemoryConfig.ZERO_MB;
import static java.lang.Integer.parseInt;
import static java.math.RoundingMode.UP;

@DomainService
Expand All @@ -61,27 +56,6 @@ public class CarbonEstimator {
private final GlobalEnergyMixStorage globalEnergyMixStorage;
private final EC2InstanceStorage ec2InstanceStorage;

private static Duration computeDuration(Component component) {
double percentageUptime = 1;
for (ConfiguredSetting configurationValue : component.getConfigurationValues()) {
switch (configurationValue.configurationSettingName()) {
case INSTANCE_NUMBER, VOLUME_NUMBER, MONTHLY_INVOCATION_COUNT ->
percentageUptime *= parseInt(configurationValue.value());
case DAYS_ON_PER_MONTH ->
percentageUptime *= parseInt(configurationValue.value()) / AVERAGE_DAYS_PER_MONTH;
case DAILY_USAGE_COUNT ->
percentageUptime *= parseInt(configurationValue.value()) * AVERAGE_DAYS_PER_MONTH;
case AVERAGE_EXEC_TIME_IN_MS ->
percentageUptime *= parseInt(configurationValue.value()) / MS_IN_ONE_MONTH;
case DAILY_RUNNING_TIME_IN_MS ->
percentageUptime *= (double) parseInt(configurationValue.value()) / MS_IN_ONE_DAY;
default -> { // not an UpTime parameter
}
}
}
return Duration.ofMillis((long) (percentageUptime * MS_IN_ONE_MONTH));
}

private static MemoryConfig findMemoryConfig(Component component, SettingName settingName) {
return Optional.ofNullable(component.getValue(settingName))
.map(Double::parseDouble)
Expand Down Expand Up @@ -124,8 +98,8 @@ public int estimateComponent(String countryIsoCode, Component component) throws
CPUConfig cpu = findCPU(component);
MemoryConfig ram = findMemoryConfig(component, MEMORY_IN_MEGA_BYTE);
MemoryConfig disk = findMemoryConfig(component, STORAGE_IN_MEGA_BYTE);
Duration duration = computeDuration(component);
var estimatableServer = getEstimatableServer(component.getValue(SettingName.INSTANCE_TYPE), disk, duration, cpu, ram);
Duration monthlyUptime = component.computeEstimatedMonthlyUptime();
var estimatableServer = getEstimatableServer(component.getValue(SettingName.INSTANCE_TYPE), disk, monthlyUptime, cpu, ram);
return estimateServer(countryIsoCode, estimatableServer);
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -17,11 +17,15 @@
*/
package fr.ippon.iroco2.domain.calculator.model;

import fr.ippon.iroco2.domain.calculator.model.emu.SettingName;
import org.junit.jupiter.api.Test;

import java.time.Duration;
import java.util.List;

import static fr.ippon.iroco2.domain.calculator.model.emu.SettingName.INSTANCE_NUMBER;
import static fr.ippon.iroco2.domain.estimator.TimeConstant.AVERAGE_DAYS_PER_MONTH;
import static fr.ippon.iroco2.domain.estimator.TimeConstant.MS_IN_ONE_MONTH;
import static org.assertj.core.api.Assertions.assertThat;
import static org.mockito.Mockito.mock;

Expand Down Expand Up @@ -78,4 +82,29 @@ void getValue_should_return_corresponding_value() {
//then
assertThat(result).isEqualTo("666");
}

@Test
void testComputeEstimatedMonthlyUptime_WithFilteredValues_ProductCalculation() {
// Given

// 0 = ignored, 2, 3, 5, 7 = kept
List<ConfiguredSetting> configurationValues = List.of(
new ConfiguredSetting(null, SettingName.PROCESSOR_ARCHITECTURE, "0"),
new ConfiguredSetting(null, SettingName.MEMORY_IN_MEGA_BYTE, "0"),
new ConfiguredSetting(null, SettingName.INSTANCE_NUMBER, "2"),
new ConfiguredSetting(null, SettingName.VOLUME_NUMBER, "3"),
new ConfiguredSetting(null, SettingName.MONTHLY_INVOCATION_COUNT, "5"),
new ConfiguredSetting(null, SettingName.DAYS_ON_PER_MONTH, "7")
);
Component component = Component.load(
null, null, "Test", null, null, null, configurationValues
);

// When
Duration uptime = component.computeEstimatedMonthlyUptime();

// Then
Duration expectedUptime = Duration.ofMillis((long) (210 * MS_IN_ONE_MONTH / AVERAGE_DAYS_PER_MONTH)); // 2 * 3 * 5 * 7 = 210
assertThat(uptime).isEqualTo(expectedUptime);
}
}
79 changes: 40 additions & 39 deletions pom.xml
Original file line number Diff line number Diff line change
Expand Up @@ -31,15 +31,6 @@
</properties>

<build>
<pluginManagement>
<plugins>
<plugin>
<groupId>org.jacoco</groupId>
<artifactId>jacoco-maven-plugin</artifactId>
<version>0.8.12</version>
</plugin>
</plugins>
</pluginManagement>
<plugins>
<plugin>
<groupId>org.sonarsource.scanner.maven</groupId>
Expand All @@ -49,40 +40,50 @@
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-surefire-plugin</artifactId>
<version>3.5.2</version>
<version>3.5.3</version>
</plugin>
<plugin>
<groupId>org.jacoco</groupId>
<artifactId>jacoco-maven-plugin</artifactId>
<version>0.8.13</version>
<executions>
<execution>
<id>default-prepare-agent</id>
<goals>
<goal>prepare-agent</goal>
</goals>
</execution>
<execution>
<id>default-report</id>
<goals>
<goal>report</goal>
</goals>
</execution>
<execution>
<id>default-check</id>
<goals>
<goal>check</goal>
</goals>
<configuration>
<rules>
<rule>
<element>BUNDLE</element>
<limits>
<limit>
<counter>COMPLEXITY</counter>
<value>COVEREDRATIO</value>
<minimum>0.60</minimum>
</limit>
</limits>
</rule>
</rules>
</configuration>
</execution>
</executions>
</plugin>
</plugins>
</build>

<profiles>
<profile>
<id>coverage</id>
<build>
<plugins>
<plugin>
<groupId>org.jacoco</groupId>
<artifactId>jacoco-maven-plugin</artifactId>
<executions>
<execution>
<id>prepare-agent</id>
<goals>
<goal>prepare-agent</goal>
</goals>
</execution>
<execution>
<id>report</id>
<phase>prepare-package</phase>
<goals>
<goal>report</goal>
</goals>
</execution>
</executions>
</plugin>
</plugins>
</build>
</profile>
</profiles>

<dependencyManagement>
<dependencies>
<dependency>
Expand Down