Skip to content
Closed
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
@@ -1,5 +1,7 @@
package org.cloudfoundry.multiapps.controller.client.lib.domain;

import java.time.Duration;

import com.fasterxml.jackson.databind.annotation.JsonDeserialize;
import com.fasterxml.jackson.databind.annotation.JsonSerialize;
import org.cloudfoundry.multiapps.common.Nullable;
Expand Down Expand Up @@ -53,4 +55,14 @@ public boolean shouldSkipSyslogUrlUpdate() {
@Nullable
public abstract Boolean shouldFailOnTagsUpdateFailure();

@Nullable
public abstract Duration getCreateServiceTimeout();

@Nullable
public abstract Duration getBindServiceTimeout();

@Nullable
public abstract Duration getCreateServiceKeyTimeout();


}
Original file line number Diff line number Diff line change
Expand Up @@ -290,6 +290,7 @@ public final class Messages {
public static final String API_INFO_AUDIT_LOG_CONFIG = "Api info";
public static final String IGNORING_NAMESPACE_PARAMETERS = "Ignoring parameter \"{0}\" , as the MTA is not deployed with namespace!";
public static final String NAMESPACE_PARSING_ERROR_MESSAGE = "Cannot parse \"{0}\" flag - expected a boolean format.";
public static final String PARAMETER_0_MUST_BE_POSITIVE_WITH_MAX_VALUE_1 = "Parameter \"{0}\" must be positive integer value up to {1}!";

private Messages() {
}
Expand Down
Original file line number Diff line number Diff line change
@@ -1,19 +1,22 @@
package org.cloudfoundry.multiapps.controller.core.cf.v2;

import java.text.MessageFormat;
import java.time.Duration;
import java.util.Collections;
import java.util.List;
import java.util.Map;
import java.util.Objects;
import java.util.TreeMap;
import java.util.stream.Collectors;

import org.cloudfoundry.client.v3.serviceinstances.ServiceInstanceType;
import org.cloudfoundry.multiapps.common.ContentException;
import org.cloudfoundry.multiapps.controller.client.lib.domain.CloudServiceInstanceExtended;
import org.cloudfoundry.multiapps.controller.client.lib.domain.ImmutableCloudServiceInstanceExtended;
import org.cloudfoundry.multiapps.controller.core.Messages;
import org.cloudfoundry.multiapps.controller.core.model.SupportedParameters;
import org.cloudfoundry.multiapps.controller.core.util.CloudModelBuilderUtil;
import org.cloudfoundry.multiapps.controller.core.util.DurationUtil;
import org.cloudfoundry.multiapps.controller.core.util.NameUtil;
import org.cloudfoundry.multiapps.controller.core.util.SpecialResourceTypesRequiredParametersUtil;
import org.cloudfoundry.multiapps.mta.model.DeploymentDescriptor;
Expand Down Expand Up @@ -88,6 +91,9 @@
commonServiceParameters.failOnServiceParametersUpdateFailure())
.shouldFailOnPlanUpdateFailure(commonServiceParameters.failOnServicePlanUpdateFailure())
.shouldFailOnTagsUpdateFailure(commonServiceParameters.failOnServiceTagsUpdateFailure())
.createServiceTimeout(extractTimeoutValue(parameters, "create-service-timeout"))

Check failure on line 94 in multiapps-controller-core/src/main/java/org/cloudfoundry/multiapps/controller/core/cf/v2/ServicesCloudModelBuilder.java

View check run for this annotation

SonarQubeCloud / SonarCloud Code Analysis

Define a constant instead of duplicating this literal "create-service-timeout" 3 times.

See more on https://sonarcloud.io/project/issues?id=cloudfoundry_multiapps-controller&issues=AZ46DTeI3bP5d0I4ajoI&open=AZ46DTeI3bP5d0I4ajoI&pullRequest=1833
.bindServiceTimeout(extractTimeoutValue(parameters, "bind-service-timeout"))

Check failure on line 95 in multiapps-controller-core/src/main/java/org/cloudfoundry/multiapps/controller/core/cf/v2/ServicesCloudModelBuilder.java

View check run for this annotation

SonarQubeCloud / SonarCloud Code Analysis

Define a constant instead of duplicating this literal "bind-service-timeout" 3 times.

See more on https://sonarcloud.io/project/issues?id=cloudfoundry_multiapps-controller&issues=AZ46DTeI3bP5d0I4ajoG&open=AZ46DTeI3bP5d0I4ajoG&pullRequest=1833
.createServiceKeyTimeout(extractTimeoutValue(parameters, "create-service-key-timeout"))

Check failure on line 96 in multiapps-controller-core/src/main/java/org/cloudfoundry/multiapps/controller/core/cf/v2/ServicesCloudModelBuilder.java

View check run for this annotation

SonarQubeCloud / SonarCloud Code Analysis

Define a constant instead of duplicating this literal "create-service-key-timeout" 3 times.

See more on https://sonarcloud.io/project/issues?id=cloudfoundry_multiapps-controller&issues=AZ46DTeI3bP5d0I4ajoH&open=AZ46DTeI3bP5d0I4ajoH&pullRequest=1833
.v3Metadata(ServiceMetadataBuilder.build(deploymentDescriptor, namespace, resource))
.build();
}
Expand Down Expand Up @@ -120,11 +126,15 @@
commonServiceParameters.failOnServiceParametersUpdateFailure())
.shouldFailOnPlanUpdateFailure(commonServiceParameters.failOnServicePlanUpdateFailure())
.shouldFailOnTagsUpdateFailure(commonServiceParameters.failOnServiceTagsUpdateFailure())
.createServiceTimeout(extractTimeoutValue(parameters, "create-service-timeout"))
.bindServiceTimeout(extractTimeoutValue(parameters, "bind-service-timeout"))
.createServiceKeyTimeout(extractTimeoutValue(parameters, "create-service-key-timeout"))
.v3Metadata(ServiceMetadataBuilder.build(deploymentDescriptor, namespace, resource))
.build();
}

protected CloudServiceInstanceExtended createExistingService(Resource resource, CommonServiceParameters commonServiceParameters) {
Map<String, Object> parameters = resource.getParameters();
return ImmutableCloudServiceInstanceExtended.builder()
.name(commonServiceParameters.getServiceName())
.resourceName(resource.getName())
Expand All @@ -137,6 +147,9 @@
commonServiceParameters.failOnServiceParametersUpdateFailure())
.shouldFailOnPlanUpdateFailure(commonServiceParameters.failOnServicePlanUpdateFailure())
.shouldFailOnTagsUpdateFailure(commonServiceParameters.failOnServiceTagsUpdateFailure())
.createServiceTimeout(extractTimeoutValue(parameters, "create-service-timeout"))
.bindServiceTimeout(extractTimeoutValue(parameters, "bind-service-timeout"))
.createServiceKeyTimeout(extractTimeoutValue(parameters, "create-service-key-timeout"))
.v3Metadata(ServiceMetadataBuilder.build(deploymentDescriptor, namespace, resource))
.build();
}
Expand Down Expand Up @@ -215,4 +228,12 @@

}

private Duration extractTimeoutValue(Map<String, Object> parameters, String timeoutParameterName) {
if (parameters == null) {
return null;
}
return DurationUtil.parseDurationSafely(parameters.get(timeoutParameterName))
.orElse(null);
}

}
Original file line number Diff line number Diff line change
Expand Up @@ -183,6 +183,13 @@ public class SupportedParameters {
@Deprecated
public static final String DEPRECATED_CONFIG_MTA_PROVIDES_DEPENDENCY = "mta-provides-dependency";

public static final String CREATE_SERVICE_TIMEOUT = "create-service-timeout";
public static final String SERVICES_CREATE_SERVICE_TIMEOUT = "services-create-service-timeout";
public static final String BIND_SERVICE_TIMEOUT = "bind-service-timeout";
public static final String SERVICES_BIND_SERVICE_TIMEOUT = "services-bind-service-timeout";
public static final String CREATE_SERVICE_KEY_TIMEOUT = "create-service-key-timeout";
public static final String SERVICES_CREATE_SERVICE_KEY_TIMEOUT = "services-create-service-key-timeout";

public static final Set<String> MODULE_PARAMETERS = Set.of(APP_NAME, APPLY_NAMESPACE, BUILDPACK, BUILDPACKS, LIFECYCLE, COMMAND,
CREATE_SERVICE_BROKER, DEFAULT_APP_NAME, DEFAULT_HOST, DEFAULT_INSTANCES,
DEFAULT_LIVE_APP_NAME, DEFAULT_LIVE_DOMAIN, DEFAULT_LIVE_HOST,
Expand Down Expand Up @@ -210,10 +217,13 @@ public class SupportedParameters {
SERVICE_KEY_NAME, SERVICE_NAME, SERVICE_PLAN, SERVICE_TAGS, SERVICE_BROKER,
SKIP_SERVICE_UPDATES, TYPE, PROVIDER_ID, PROVIDER_NID, TARGET,
SERVICE_CONFIG_PATH, FILTER, MANAGED, VERSION, PATH, MEMORY,
FAIL_ON_SERVICE_UPDATE, SERVICE_PROVIDER, SERVICE_VERSION);
FAIL_ON_SERVICE_UPDATE, SERVICE_PROVIDER, SERVICE_VERSION,
CREATE_SERVICE_TIMEOUT, BIND_SERVICE_TIMEOUT, CREATE_SERVICE_KEY_TIMEOUT);
public static final Set<String> GLOBAL_PARAMETERS = Set.of(KEEP_EXISTING_ROUTES, APPS_UPLOAD_TIMEOUT, APPS_TASK_EXECUTION_TIMEOUT,
APPS_START_TIMEOUT, APPS_STAGE_TIMEOUT, APPLY_NAMESPACE,
ENABLE_PARALLEL_DEPLOYMENTS, DEPLOY_MODE, BG_DEPENDENCY_AWARE_STOP_ORDER);
ENABLE_PARALLEL_DEPLOYMENTS, DEPLOY_MODE, BG_DEPENDENCY_AWARE_STOP_ORDER,
SERVICES_CREATE_SERVICE_TIMEOUT,
SERVICES_BIND_SERVICE_TIMEOUT, SERVICES_CREATE_SERVICE_KEY_TIMEOUT);

public static final Set<String> DEPENDENCY_PARAMETERS = Set.of(BINDING_NAME, ENV_VAR_NAME, VISIBILITY, USE_LIVE_ROUTES,
SERVICE_BINDING_CONFIG, DELETE_SERVICE_KEY_AFTER_DEPLOYMENT);
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,37 @@
package org.cloudfoundry.multiapps.controller.core.util;

import java.time.Duration;
import java.util.Optional;

import org.cloudfoundry.multiapps.common.ContentException;
import org.cloudfoundry.multiapps.controller.core.Messages;

public final class DurationUtil {

private DurationUtil() {
// utility class
}

public static Duration parseDuration(Object timeout, String parameterName, int maxAllowedValue) {
if (timeout == null) {
return null;
}
if (!(timeout instanceof Number number)) {
throw new ContentException(Messages.PARAMETER_0_MUST_BE_POSITIVE_WITH_MAX_VALUE_1, parameterName, maxAllowedValue);
}
int value = number.intValue();
if (value < 0 || value > maxAllowedValue) {
throw new ContentException(Messages.PARAMETER_0_MUST_BE_POSITIVE_WITH_MAX_VALUE_1, parameterName, maxAllowedValue);
}
return Duration.ofSeconds(value);
}

public static Optional<Duration> parseDurationSafely(Object timeout) {
if (timeout == null || !(timeout instanceof Number number)) {
return Optional.empty();
}
long seconds = number.longValue();
return seconds > 0 ? Optional.of(Duration.ofSeconds(seconds)) : Optional.empty();
}
}

Original file line number Diff line number Diff line change
@@ -0,0 +1,89 @@
package org.cloudfoundry.multiapps.controller.core.util;

import java.time.Duration;
import java.util.Optional;

import org.cloudfoundry.multiapps.common.ContentException;
import org.junit.jupiter.api.Test;

import static org.junit.jupiter.api.Assertions.assertEquals;
import static org.junit.jupiter.api.Assertions.assertNull;
import static org.junit.jupiter.api.Assertions.assertThrows;
import static org.junit.jupiter.api.Assertions.assertTrue;

class DurationUtilTest {

private static final String PARAM_NAME = "test-timeout";
private static final int MAX_VALUE = 3600;

@Test
void testParseDurationWithValidValue() {
Duration result = DurationUtil.parseDuration(300, PARAM_NAME, MAX_VALUE);
assertEquals(Duration.ofSeconds(300), result);
}

@Test
void testParseDurationWithZero() {
Duration result = DurationUtil.parseDuration(0, PARAM_NAME, MAX_VALUE);
assertEquals(Duration.ZERO, result);
}

@Test
void testParseDurationWithMaxValue() {
Duration result = DurationUtil.parseDuration(MAX_VALUE, PARAM_NAME, MAX_VALUE);
assertEquals(Duration.ofSeconds(MAX_VALUE), result);
}

@Test
void testParseDurationWithNull() {
Duration result = DurationUtil.parseDuration(null, PARAM_NAME, MAX_VALUE);
assertNull(result);
}

@Test
void testParseDurationWithNegativeValue() {
assertThrows(ContentException.class, () -> DurationUtil.parseDuration(-1, PARAM_NAME, MAX_VALUE));
}

@Test
void testParseDurationWithValueExceedingMax() {
assertThrows(ContentException.class, () -> DurationUtil.parseDuration(MAX_VALUE + 1, PARAM_NAME, MAX_VALUE));
}

@Test
void testParseDurationWithNonNumber() {
assertThrows(ContentException.class, () -> DurationUtil.parseDuration("invalid", PARAM_NAME, MAX_VALUE));
}

@Test
void testParseDurationSafelyWithValidValue() {
Optional<Duration> result = DurationUtil.parseDurationSafely(300);
assertTrue(result.isPresent());
assertEquals(Duration.ofSeconds(300), result.get());
}

@Test
void testParseDurationSafelyWithNull() {
Optional<Duration> result = DurationUtil.parseDurationSafely(null);
assertTrue(result.isEmpty());
}

@Test
void testParseDurationSafelyWithZero() {
Optional<Duration> result = DurationUtil.parseDurationSafely(0);
assertTrue(result.isEmpty());
}

@Test
void testParseDurationSafelyWithNegative() {
Optional<Duration> result = DurationUtil.parseDurationSafely(-5);
assertTrue(result.isEmpty());
}

@Test
void testParseDurationSafelyWithNonNumber() {
Optional<Duration> result = DurationUtil.parseDurationSafely("invalid");
assertTrue(result.isEmpty());
}
}

Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
package org.cloudfoundry.multiapps.controller.process;

import java.time.Duration;
import java.util.regex.Pattern;

public class Constants {
Expand Down Expand Up @@ -45,6 +46,10 @@ public class Constants {

public static final Pattern STANDARD_INT_PATTERN = Pattern.compile("[+-]?[0-9]+");

public static final Duration DEFAULT_DELETE_SERVICE_TIMEOUT = Duration.ofMinutes(60);
public static final Duration DEFAULT_DELETE_SERVICE_KEY_TIMEOUT = Duration.ofMinutes(60);
public static final Duration DEFAULT_UNBIND_SERVICE_TIMEOUT = Duration.ofMinutes(60);

protected Constants() {
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -101,6 +101,7 @@ public class Messages {
public static final String ERROR_WHILE_DELETING_SERVICE_INSTANCE_METADATA_0 = "Error while deleting service instance metadata \"{0}\"";
public static final String ERROR_WHILE_DELETING_SERVICE_KEY_0 = "Error while deleting service key \"{0}\"";
public static final String ERROR_WHILE_CALCULATING_SERVICE_KEYS_FOR_WAITING = "Error while calculating service keys for waiting";
public static final String SERVICE_KEY_NOT_FOUND = "Service key \"{0}\" for service \"{1}\" was not found and will be skipped";
public static final String COULD_NOT_GET_APP_LOGS = "Could not get application recent logs: {0}";
public static final String ERROR_DURING_INCREMENTAL_INSTANCE_UPDATE_OF_MODULE_0 = "Error during incremental instance update of module \"{0}\"";
public static final String ERROR_DURING_POLL_OF_INCREMENTAL_INSTANCE_UPDATE_OF_MODULE_0 = "Error during poll of incremental instance update of module \"{0}\"";
Expand Down Expand Up @@ -552,6 +553,8 @@ public class Messages {
public static final String DELETING_ERROR_TYPE_O_FOR_PROCESS_1 = "Deleting error type \"{0}\" for process \"{1}\"";
public static final String TIMEOUT_MESSAGE = "Application {0} timeout is set by \"{1}\" parameter to {2} seconds";
public static final String TIMEOUT_DEFAULT_VALUE_MESSAGE = "Application {0} timeout is default one: {1} seconds";
public static final String OPERATION_TIMEOUT_MESSAGE = "{0} timeout is set by \"{1}\" parameter to {2} seconds";
public static final String OPERATION_TIMEOUT_DEFAULT_VALUE_MESSAGE = "{0} timeout is default one: {1} seconds";
public static final String PROVIDED_EXTENSION_DESCRIPTORS = "Provided extension descriptors: {0}";
public static final String CREATED_SUBSCRIPTION = "Created subscription with ID: {0}";
public static final String UPDATING_SUBSCRIBER_0 = "Updating subscriber: {0} ";
Expand Down Expand Up @@ -735,6 +738,7 @@ public class Messages {
public static final String WILL_ONLY_REMOVE_SERVICE_INSTANCE_METADATA_BECAUSE_THE_SERVICE_TYPE_IS_EXISTING = "Will only remove service instance metadata, because the service type is \"existing\"";
public static final String DETERMINING_DELETE_ACTIONS_FOR_SERVICE_INSTANCE_0 = "Determining delete actions for service instance \"{0}\"";
public static final String CANNOT_RETRIEVE_OPTIONAL_SERVICE_BINDING_FOR_SERVICE_INSTANCE_0 = "Cannot retrieve optional service binding for service instance \"{0}\"";
public static final String CANNOT_RETRIEVE_SERVICE_BINDING_FOR_SERVICE_INSTANCE_0_NOT_FOUND = "Service instance \"{0}\" was not found; skipping service binding check.";
public static final String SERVICE_KEYS_SCHEDULED_FOR_RECREATION_MODIFICATION_0 = "Service keys scheduled for recreation due to modification: \"{0}\"";
public static final String SERVICE_KEYS_SCHEDULED_FOR_RECREATION_STATE_0 = "Service keys scheduled for recreation due to state: \"{0}\"";
public static final String SERVICE_KEYS_SCHEDULED_FOR_CREATION_0 = "Service keys scheduled for creation: \"{0}\"";
Expand Down Expand Up @@ -825,6 +829,18 @@ public class Messages {
public static final String INVALID_BOOLEAN_VALUE = "Invalid boolean value: must be 'true' or 'false'";
public static final String DISABLE_AUTOSCALER_LABEL_CONTENT = "Disabled_by_MTA_operation_{0}";

public static final String COULD_NOT_RESOLVE_SERVICE_RESOURCE_NAME_FOR_TIMEOUT_TYPE_0 = "Could not resolve service resource name for timeout type {0}";
public static final String COULD_NOT_FIND_RESOURCE_0_IN_DESCRIPTOR_FOR_TIMEOUT_TYPE_1 = "Could not find resource {0} in deployment descriptor for timeout type {1}";
public static final String EXTRACTING_ALL_TIMEOUT_PARAMETERS_FROM_DESCRIPTOR = "Extracting all timeout parameters from descriptor";
public static final String NO_DESCRIPTOR_FOUND_USING_DEFAULT_TIMEOUTS = "No descriptor found; sub-processes will use default timeouts";
public static final String SUCCESSFULLY_EXTRACTED_0_TIMEOUT_PARAMETERS = "Successfully extracted {0} timeout parameters from descriptor";
public static final String TIMEOUT_0_EQUALS_1_SECONDS_FROM_2 = "Timeout {0} = {1}s (from {2})";
public static final String FAILED_TO_RESOLVE_TIMEOUT_FOR_0_1 = "Failed to resolve timeout for {0}: {1}";
public static final String ERROR_EXTRACTING_GLOBAL_TIMEOUTS_FROM_DESCRIPTOR = "Error while extracting global timeouts from deployment descriptor";
public static final String COULD_NOT_RESOLVE_DESCRIPTOR_RESOURCE_FOR_TIMEOUT_TYPE_0_PARAMETER_1 = "Could not resolve descriptor resource for timeout type {0}; parameter {1} cannot be applied";
public static final String DEPLOYMENT_DESCRIPTOR_MISSING_GLOBAL_PARAMETER_0_NOT_APPLIED_FOR_1 = "Deployment descriptor is missing; global parameter {0} cannot be applied for {1}";
public static final String NO_DEPLOYMENT_DESCRIPTOR_FOUND_IN_CONTEXT = "No deployment descriptor found in context";

protected Messages() {
}
}
Loading
Loading