From a5b9bc6d86c80f9ee312c42aeb5eeac2068729fa Mon Sep 17 00:00:00 2001 From: stiv03 Date: Mon, 12 Oct 2026 12:44:08 +0300 Subject: [PATCH 1/5] Discover service-key credential references in service-keys config Extend the v3 DynamicResolvableParametersFactory to scan each resource's service-keys[].config for {ds/...} credential references, in addition to the existing top-level parameter/property scan. Scoped recursion only into service-keys[].config, preserving the existing depth-one contract for all other parameters. --- .../DynamicResolvableParametersFactory.java | 27 +++++++++++++++++++ ...ynamicResolvableParametersFactoryTest.java | 25 +++++++++++++++++ 2 files changed, 52 insertions(+) diff --git a/multiapps-controller-core/src/main/java/org/cloudfoundry/multiapps/controller/core/helpers/v3/DynamicResolvableParametersFactory.java b/multiapps-controller-core/src/main/java/org/cloudfoundry/multiapps/controller/core/helpers/v3/DynamicResolvableParametersFactory.java index f590ef4fb3..b033b8692b 100644 --- a/multiapps-controller-core/src/main/java/org/cloudfoundry/multiapps/controller/core/helpers/v3/DynamicResolvableParametersFactory.java +++ b/multiapps-controller-core/src/main/java/org/cloudfoundry/multiapps/controller/core/helpers/v3/DynamicResolvableParametersFactory.java @@ -8,6 +8,7 @@ import org.cloudfoundry.multiapps.controller.core.model.DynamicResolvableParameter; import org.cloudfoundry.multiapps.controller.core.model.ImmutableDynamicResolvableParameter; +import org.cloudfoundry.multiapps.controller.core.model.SupportedParameters; import org.cloudfoundry.multiapps.mta.model.DeploymentDescriptor; import org.cloudfoundry.multiapps.mta.model.Resource; import org.cloudfoundry.multiapps.mta.util.DynamicParameterUtil; @@ -34,6 +35,7 @@ private void addDynamicResolvableParameter(Set dynam addDynamicResolvableParameter(dynamicResolvableParameters, parametersStringValues); addDynamicResolvableParameter(dynamicResolvableParameters, propertiesStringValues); + addDynamicResolvableParametersFromServiceKeysConfig(dynamicResolvableParameters, resource.getParameters()); } private List getStringValueParameters(Map parameters) { @@ -58,4 +60,29 @@ private void addDynamicResolvableParameter(Set dynam } } + private void addDynamicResolvableParametersFromServiceKeysConfig(Set dynamicResolvableParameters, + Map parameters) { + Object serviceKeys = parameters.get(SupportedParameters.SERVICE_KEYS); + if (!(serviceKeys instanceof List serviceKeysList)) { + return; + } + for (Object serviceKey : serviceKeysList) { + if (serviceKey instanceof Map serviceKeyMap) { + addDynamicResolvableParametersFromConfig(dynamicResolvableParameters, serviceKeyMap.get(SupportedParameters.SERVICE_KEY_CONFIG)); + } + } + } + + private void addDynamicResolvableParametersFromConfig(Set dynamicResolvableParameters, Object config) { + if (!(config instanceof Map configMap)) { + return; + } + List configStringValues = configMap.values() + .stream() + .filter(String.class::isInstance) + .map(String.class::cast) + .collect(Collectors.toList()); + addDynamicResolvableParameter(dynamicResolvableParameters, configStringValues); + } + } diff --git a/multiapps-controller-core/src/test/java/org/cloudfoundry/multiapps/controller/core/helpers/v3/DynamicResolvableParametersFactoryTest.java b/multiapps-controller-core/src/test/java/org/cloudfoundry/multiapps/controller/core/helpers/v3/DynamicResolvableParametersFactoryTest.java index c8b0aa225e..d522fe4ce3 100644 --- a/multiapps-controller-core/src/test/java/org/cloudfoundry/multiapps/controller/core/helpers/v3/DynamicResolvableParametersFactoryTest.java +++ b/multiapps-controller-core/src/test/java/org/cloudfoundry/multiapps/controller/core/helpers/v3/DynamicResolvableParametersFactoryTest.java @@ -60,4 +60,29 @@ void testParametersResolveDepthIsAlwaysOne() { assertTrue(resultParameters.isEmpty(), MessageFormat.format("Expected empty Set but contains \"{0}\"", resultParameters)); } + @Test + void testDetectionOfServiceKeyCredentialReferenceInServiceKeysConfig() { + DynamicResolvableParameter expectedParameter = ImmutableDynamicResolvableParameter.builder() + .parameterName("service-keys.ztis-key.credentials.issuer") + .relationshipEntityName("ztis-config") + .build(); + + DeploymentDescriptor descriptor = DeploymentDescriptor.createV3() + .setId("test-id") + .setResources(List.of(Resource.createV3() + .setName("caas-v2") + .setParameters(Map.of("service-keys", + List.of(Map.of("name", + "caas-v2-key", + "config", + Map.of("issuer", + "{ds/ztis-config/service-keys.ztis-key.credentials.issuer}"))))))); + + DynamicResolvableParametersFactory factory = new DynamicResolvableParametersFactory(descriptor); + Set resultParameters = factory.create(); + + assertTrue(resultParameters.contains(expectedParameter), + MessageFormat.format("Expected parameter \"{0}\" was not found in the Set \"{1}\"", expectedParameter, resultParameters)); + } + } From f7fb78468ccd159c46a141525de8adc3a8bcc0b4 Mon Sep 17 00:00:00 2001 From: stiv03 Date: Mon, 19 Oct 2026 11:52:39 +0300 Subject: [PATCH 2/5] Resolve service-key credential references at deploy time Add ServiceKeyCredentialResolver, which replaces {ds//service-keys..credentials.} references in a service key's credentials by reading the referenced (already created) service key's credentials from the Cloud Controller and extracting the addressed field. Missing fields fail loudly with a clear message. Wire it in via a new idempotent ResolveServiceKeyCredentialReferencesStep, inserted in create-service-key.bpmn between the operation-in-progress check and createServiceKey, so a referenced resource's key (guaranteed to be in an earlier, already-completed batch by processed-after) is read back just before the consuming key is created. --- .../controller/process/Messages.java | 1 + ...lveServiceKeyCredentialReferencesStep.java | 37 +++++++ .../util/ServiceKeyCredentialResolver.java | 80 +++++++++++++++ .../process/create-service-key.bpmn | 4 +- ...erviceKeyCredentialReferencesStepTest.java | 66 +++++++++++++ .../ServiceKeyCredentialResolverTest.java | 97 +++++++++++++++++++ 6 files changed, 284 insertions(+), 1 deletion(-) create mode 100644 multiapps-controller-process/src/main/java/org/cloudfoundry/multiapps/controller/process/steps/ResolveServiceKeyCredentialReferencesStep.java create mode 100644 multiapps-controller-process/src/main/java/org/cloudfoundry/multiapps/controller/process/util/ServiceKeyCredentialResolver.java create mode 100644 multiapps-controller-process/src/test/java/org/cloudfoundry/multiapps/controller/process/steps/ResolveServiceKeyCredentialReferencesStepTest.java create mode 100644 multiapps-controller-process/src/test/java/org/cloudfoundry/multiapps/controller/process/util/ServiceKeyCredentialResolverTest.java diff --git a/multiapps-controller-process/src/main/java/org/cloudfoundry/multiapps/controller/process/Messages.java b/multiapps-controller-process/src/main/java/org/cloudfoundry/multiapps/controller/process/Messages.java index 86ef2d9b56..071e1cfff1 100755 --- a/multiapps-controller-process/src/main/java/org/cloudfoundry/multiapps/controller/process/Messages.java +++ b/multiapps-controller-process/src/main/java/org/cloudfoundry/multiapps/controller/process/Messages.java @@ -834,6 +834,7 @@ public class Messages { public static final String PARSE_NULL_STRING_ERROR = "Cannot parse null string"; 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 UNABLE_TO_RESOLVE_SERVICE_KEY_CREDENTIAL_REFERENCE = "Unable to resolve service key credential reference \"{0}\": field \"{1}\" was not found in the credentials of service key \"{2}\" of service instance \"{3}\""; protected Messages() { } diff --git a/multiapps-controller-process/src/main/java/org/cloudfoundry/multiapps/controller/process/steps/ResolveServiceKeyCredentialReferencesStep.java b/multiapps-controller-process/src/main/java/org/cloudfoundry/multiapps/controller/process/steps/ResolveServiceKeyCredentialReferencesStep.java new file mode 100644 index 0000000000..7287672f92 --- /dev/null +++ b/multiapps-controller-process/src/main/java/org/cloudfoundry/multiapps/controller/process/steps/ResolveServiceKeyCredentialReferencesStep.java @@ -0,0 +1,37 @@ +package org.cloudfoundry.multiapps.controller.process.steps; + +import java.text.MessageFormat; +import java.util.Collections; + +import jakarta.inject.Named; +import org.cloudfoundry.multiapps.controller.client.facade.CloudControllerClient; +import org.cloudfoundry.multiapps.controller.client.facade.domain.CloudServiceKey; +import org.cloudfoundry.multiapps.controller.process.Messages; +import org.cloudfoundry.multiapps.controller.process.util.ServiceKeyCredentialResolver; +import org.cloudfoundry.multiapps.controller.process.variables.Variables; +import org.springframework.beans.factory.config.BeanDefinition; +import org.springframework.context.annotation.Scope; + +@Named("resolveServiceKeyCredentialReferencesStep") +@Scope(BeanDefinition.SCOPE_PROTOTYPE) +public class ResolveServiceKeyCredentialReferencesStep extends SyncFlowableStep { + + @Override + protected StepPhase executeStep(ProcessContext context) { + CloudServiceKey serviceKeyToProcess = context.getVariable(Variables.SERVICE_KEY_TO_PROCESS); + CloudControllerClient client = context.getControllerClient(); + + ServiceKeyCredentialResolver resolver = new ServiceKeyCredentialResolver(client, Collections.emptyMap()); + CloudServiceKey resolvedServiceKey = resolver.resolve(serviceKeyToProcess); + + context.setVariable(Variables.SERVICE_KEY_TO_PROCESS, resolvedServiceKey); + return StepPhase.DONE; + } + + @Override + protected String getStepErrorMessage(ProcessContext context) { + CloudServiceKey serviceKeyToProcess = context.getVariable(Variables.SERVICE_KEY_TO_PROCESS); + return MessageFormat.format(Messages.ERROR_WHILE_CREATING_SERVICE_KEY_0, serviceKeyToProcess.getName()); + } + +} \ No newline at end of file diff --git a/multiapps-controller-process/src/main/java/org/cloudfoundry/multiapps/controller/process/util/ServiceKeyCredentialResolver.java b/multiapps-controller-process/src/main/java/org/cloudfoundry/multiapps/controller/process/util/ServiceKeyCredentialResolver.java new file mode 100644 index 0000000000..21087966ed --- /dev/null +++ b/multiapps-controller-process/src/main/java/org/cloudfoundry/multiapps/controller/process/util/ServiceKeyCredentialResolver.java @@ -0,0 +1,80 @@ +package org.cloudfoundry.multiapps.controller.process.util; + +import java.text.MessageFormat; +import java.util.LinkedHashMap; +import java.util.Map; + +import org.cloudfoundry.multiapps.common.ContentException; +import org.cloudfoundry.multiapps.controller.client.facade.CloudControllerClient; +import org.cloudfoundry.multiapps.controller.client.facade.domain.CloudServiceKey; +import org.cloudfoundry.multiapps.controller.client.facade.domain.ImmutableCloudServiceKey; +import org.cloudfoundry.multiapps.controller.process.Messages; +import org.cloudfoundry.multiapps.mta.util.DynamicParameterUtil; + +/** + * Resolves {@code {ds//service-keys..credentials.}} references found in a service key's credentials by reading the + * referenced (already created) service key's credentials from the Cloud Controller. + */ +public class ServiceKeyCredentialResolver { + + private final CloudControllerClient client; + private final Map serviceInstanceNamesByRelationship; + + public ServiceKeyCredentialResolver(CloudControllerClient client, Map serviceInstanceNamesByRelationship) { + this.client = client; + this.serviceInstanceNamesByRelationship = serviceInstanceNamesByRelationship; + } + + public CloudServiceKey resolve(CloudServiceKey serviceKey) { + Map credentials = serviceKey.getCredentials(); + if (credentials == null || credentials.isEmpty()) { + return serviceKey; + } + Map resolvedCredentials = new LinkedHashMap<>(credentials); + boolean changed = false; + for (Map.Entry entry : credentials.entrySet()) { + if (!isServiceKeyCredentialReference(entry.getValue())) { + continue; + } + resolvedCredentials.put(entry.getKey(), resolveReference((String) entry.getValue())); + changed = true; + } + if (!changed) { + return serviceKey; + } + return ImmutableCloudServiceKey.copyOf(serviceKey) + .withCredentials(resolvedCredentials); + } + + private boolean isServiceKeyCredentialReference(Object value) { + if (!(value instanceof String stringValue) || !stringValue.matches(DynamicParameterUtil.REGEX_PATTERN_FOR_DYNAMIC_PARAMETERS)) { + return false; + } + return DynamicParameterUtil.isServiceKeyCredentialReference(DynamicParameterUtil.getParameterName(stringValue)); + } + + private Object resolveReference(String reference) { + String relationshipName = DynamicParameterUtil.getRelationshipName(reference); + String parameterName = DynamicParameterUtil.getParameterName(reference); + String serviceKeyName = DynamicParameterUtil.getServiceKeyName(parameterName); + String credentialFieldPath = DynamicParameterUtil.getCredentialFieldPath(parameterName); + + String serviceInstanceName = serviceInstanceNamesByRelationship.getOrDefault(relationshipName, relationshipName); + CloudServiceKey referencedKey = client.getServiceKey(serviceInstanceName, serviceKeyName); + return extractCredentialField(reference, referencedKey, serviceInstanceName, serviceKeyName, credentialFieldPath); + } + + private Object extractCredentialField(String reference, CloudServiceKey referencedKey, String serviceInstanceName, + String serviceKeyName, String credentialFieldPath) { + Object current = referencedKey == null ? null : referencedKey.getCredentials(); + for (String field : credentialFieldPath.split("\\.")) { + if (!(current instanceof Map currentMap) || !currentMap.containsKey(field)) { + throw new ContentException(MessageFormat.format(Messages.UNABLE_TO_RESOLVE_SERVICE_KEY_CREDENTIAL_REFERENCE, reference, + credentialFieldPath, serviceKeyName, serviceInstanceName)); + } + current = currentMap.get(field); + } + return current; + } + +} \ No newline at end of file diff --git a/multiapps-controller-process/src/main/resources/org/cloudfoundry/multiapps/controller/process/create-service-key.bpmn b/multiapps-controller-process/src/main/resources/org/cloudfoundry/multiapps/controller/process/create-service-key.bpmn index 47297a3fd3..f45958c2b6 100644 --- a/multiapps-controller-process/src/main/resources/org/cloudfoundry/multiapps/controller/process/create-service-key.bpmn +++ b/multiapps-controller-process/src/main/resources/org/cloudfoundry/multiapps/controller/process/create-service-key.bpmn @@ -16,6 +16,8 @@ + + @@ -34,7 +36,7 @@ - + diff --git a/multiapps-controller-process/src/test/java/org/cloudfoundry/multiapps/controller/process/steps/ResolveServiceKeyCredentialReferencesStepTest.java b/multiapps-controller-process/src/test/java/org/cloudfoundry/multiapps/controller/process/steps/ResolveServiceKeyCredentialReferencesStepTest.java new file mode 100644 index 0000000000..a9e7ede16b --- /dev/null +++ b/multiapps-controller-process/src/test/java/org/cloudfoundry/multiapps/controller/process/steps/ResolveServiceKeyCredentialReferencesStepTest.java @@ -0,0 +1,66 @@ +package org.cloudfoundry.multiapps.controller.process.steps; + +import java.util.Map; + +import org.cloudfoundry.multiapps.controller.client.facade.domain.CloudServiceKey; +import org.cloudfoundry.multiapps.controller.client.facade.domain.ImmutableCloudServiceKey; +import org.cloudfoundry.multiapps.controller.client.lib.domain.ImmutableCloudServiceInstanceExtended; +import org.cloudfoundry.multiapps.controller.process.variables.Variables; +import org.junit.jupiter.api.Test; +import org.mockito.Mockito; + +import static org.junit.jupiter.api.Assertions.assertEquals; + +class ResolveServiceKeyCredentialReferencesStepTest extends SyncFlowableStepTest { + + private static final String REFERENCED_SERVICE_INSTANCE = "ztis-config"; + private static final String REFERENCED_KEY = "ztis-key"; + + @Test + void testResolvesCredentialReferenceInServiceKeyToProcess() { + CloudServiceKey keyToProcess = buildKey("caas-v2-key", "caas-v2", + Map.of("issuer", + "{ds/" + REFERENCED_SERVICE_INSTANCE + "/service-keys." + REFERENCED_KEY + + ".credentials.issuer}")); + context.setVariable(Variables.SERVICE_KEY_TO_PROCESS, keyToProcess); + Mockito.when(client.getServiceKey(REFERENCED_SERVICE_INSTANCE, REFERENCED_KEY)) + .thenReturn(buildKey(REFERENCED_KEY, REFERENCED_SERVICE_INSTANCE, Map.of("issuer", "CN=the-issuer"))); + + step.execute(execution); + + assertStepFinishedSuccessfully(); + CloudServiceKey resolved = context.getVariable(Variables.SERVICE_KEY_TO_PROCESS); + assertEquals("CN=the-issuer", resolved.getCredentials() + .get("issuer")); + } + + @Test + void testNoOpWhenNoCredentialReferences() { + CloudServiceKey keyToProcess = buildKey("caas-v2-key", "caas-v2", Map.of("app-identifier", "caas-coll-client")); + context.setVariable(Variables.SERVICE_KEY_TO_PROCESS, keyToProcess); + + step.execute(execution); + + assertStepFinishedSuccessfully(); + CloudServiceKey resolved = context.getVariable(Variables.SERVICE_KEY_TO_PROCESS); + assertEquals("caas-coll-client", resolved.getCredentials() + .get("app-identifier")); + Mockito.verify(client, Mockito.never()) + .getServiceKey(Mockito.anyString(), Mockito.anyString()); + } + + private CloudServiceKey buildKey(String keyName, String serviceInstanceName, Map credentials) { + return ImmutableCloudServiceKey.builder() + .name(keyName) + .credentials(credentials) + .serviceInstance(ImmutableCloudServiceInstanceExtended.builder() + .name(serviceInstanceName) + .build()) + .build(); + } + + @Override + protected ResolveServiceKeyCredentialReferencesStep createStep() { + return new ResolveServiceKeyCredentialReferencesStep(); + } +} \ No newline at end of file diff --git a/multiapps-controller-process/src/test/java/org/cloudfoundry/multiapps/controller/process/util/ServiceKeyCredentialResolverTest.java b/multiapps-controller-process/src/test/java/org/cloudfoundry/multiapps/controller/process/util/ServiceKeyCredentialResolverTest.java new file mode 100644 index 0000000000..7428358b11 --- /dev/null +++ b/multiapps-controller-process/src/test/java/org/cloudfoundry/multiapps/controller/process/util/ServiceKeyCredentialResolverTest.java @@ -0,0 +1,97 @@ +package org.cloudfoundry.multiapps.controller.process.util; + +import java.util.Map; + +import org.cloudfoundry.multiapps.common.ContentException; +import org.cloudfoundry.multiapps.controller.client.facade.CloudControllerClient; +import org.cloudfoundry.multiapps.controller.client.facade.domain.CloudServiceKey; +import org.cloudfoundry.multiapps.controller.client.facade.domain.ImmutableCloudServiceKey; +import org.cloudfoundry.multiapps.controller.client.facade.domain.ImmutableServiceCredentialBindingOperation; +import org.cloudfoundry.multiapps.controller.client.facade.domain.ServiceCredentialBindingOperation; +import org.cloudfoundry.multiapps.controller.client.lib.domain.ImmutableCloudServiceInstanceExtended; +import org.junit.jupiter.api.Assertions; +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Test; +import org.mockito.Mock; +import org.mockito.Mockito; +import org.mockito.MockitoAnnotations; + +class ServiceKeyCredentialResolverTest { + + private static final String REFERENCED_RESOURCE = "ztis-config"; + private static final String REFERENCED_SERVICE_INSTANCE = "ztis-config-instance"; + private static final String REFERENCED_KEY = "ztis-key"; + + @Mock + private CloudControllerClient client; + + private ServiceKeyCredentialResolver resolver; + + @BeforeEach + void setUp() throws Exception { + MockitoAnnotations.openMocks(this) + .close(); + resolver = new ServiceKeyCredentialResolver(client, Map.of(REFERENCED_RESOURCE, REFERENCED_SERVICE_INSTANCE)); + } + + @Test + void testResolvesServiceKeyCredentialReference() { + Mockito.when(client.getServiceKey(REFERENCED_SERVICE_INSTANCE, REFERENCED_KEY)) + .thenReturn(buildReferencedKey(Map.of("issuer", "CN=the-issuer"))); + CloudServiceKey keyToResolve = buildKeyWithCredentials(Map.of("issuer", + "{ds/" + REFERENCED_RESOURCE + "/service-keys." + REFERENCED_KEY + + ".credentials.issuer}")); + + CloudServiceKey resolved = resolver.resolve(keyToResolve); + + Assertions.assertEquals("CN=the-issuer", resolved.getCredentials() + .get("issuer")); + } + + @Test + void testLeavesPlainCredentialsUntouched() { + CloudServiceKey keyToResolve = buildKeyWithCredentials(Map.of("app-identifier", "caas-coll-client")); + + CloudServiceKey resolved = resolver.resolve(keyToResolve); + + Assertions.assertEquals("caas-coll-client", resolved.getCredentials() + .get("app-identifier")); + Mockito.verifyNoInteractions(client); + } + + @Test + void testFailsLoudlyWhenReferencedFieldMissing() { + Mockito.when(client.getServiceKey(REFERENCED_SERVICE_INSTANCE, REFERENCED_KEY)) + .thenReturn(buildReferencedKey(Map.of("subject", "CN=only-subject"))); + CloudServiceKey keyToResolve = buildKeyWithCredentials(Map.of("issuer", + "{ds/" + REFERENCED_RESOURCE + "/service-keys." + REFERENCED_KEY + + ".credentials.issuer}")); + + Assertions.assertThrows(ContentException.class, () -> resolver.resolve(keyToResolve)); + } + + private CloudServiceKey buildKeyWithCredentials(Map credentials) { + return ImmutableCloudServiceKey.builder() + .name("caas-v2-key") + .credentials(credentials) + .serviceInstance(ImmutableCloudServiceInstanceExtended.builder() + .name("caas-v2") + .build()) + .build(); + } + + private CloudServiceKey buildReferencedKey(Map credentials) { + return ImmutableCloudServiceKey.builder() + .name(REFERENCED_KEY) + .credentials(credentials) + .serviceInstance(ImmutableCloudServiceInstanceExtended.builder() + .name(REFERENCED_SERVICE_INSTANCE) + .build()) + .serviceKeyOperation(ImmutableServiceCredentialBindingOperation.builder() + .state(ServiceCredentialBindingOperation.State.SUCCEEDED) + .type(ServiceCredentialBindingOperation.Type.CREATE) + .build()) + .build(); + } + +} \ No newline at end of file From 12700f7fc1d6aa4306ec044324889ce1b13b5ffb Mon Sep 17 00:00:00 2001 From: stiv03 Date: Tue, 13 Oct 2026 09:33:42 +0300 Subject: [PATCH 3/5] Map referenced resource to its CF service instance name for read-back ResolveServiceKeyCredentialReferencesStep now builds the relationship->service-instance-name map from the deployment descriptor, using each resource's service-name parameter (falling back to the resource name). Previously it passed an empty map, so the credential read-back used the resource name as the service instance name and failed for resources whose service-name differs from their resource name. --- ...lveServiceKeyCredentialReferencesStep.java | 24 ++++++++++++-- ...erviceKeyCredentialReferencesStepTest.java | 32 +++++++++++++++++++ 2 files changed, 54 insertions(+), 2 deletions(-) diff --git a/multiapps-controller-process/src/main/java/org/cloudfoundry/multiapps/controller/process/steps/ResolveServiceKeyCredentialReferencesStep.java b/multiapps-controller-process/src/main/java/org/cloudfoundry/multiapps/controller/process/steps/ResolveServiceKeyCredentialReferencesStep.java index 7287672f92..8198453c5b 100644 --- a/multiapps-controller-process/src/main/java/org/cloudfoundry/multiapps/controller/process/steps/ResolveServiceKeyCredentialReferencesStep.java +++ b/multiapps-controller-process/src/main/java/org/cloudfoundry/multiapps/controller/process/steps/ResolveServiceKeyCredentialReferencesStep.java @@ -1,14 +1,18 @@ package org.cloudfoundry.multiapps.controller.process.steps; import java.text.MessageFormat; -import java.util.Collections; +import java.util.HashMap; +import java.util.Map; import jakarta.inject.Named; import org.cloudfoundry.multiapps.controller.client.facade.CloudControllerClient; import org.cloudfoundry.multiapps.controller.client.facade.domain.CloudServiceKey; +import org.cloudfoundry.multiapps.controller.core.model.SupportedParameters; import org.cloudfoundry.multiapps.controller.process.Messages; import org.cloudfoundry.multiapps.controller.process.util.ServiceKeyCredentialResolver; import org.cloudfoundry.multiapps.controller.process.variables.Variables; +import org.cloudfoundry.multiapps.mta.model.DeploymentDescriptor; +import org.cloudfoundry.multiapps.mta.model.Resource; import org.springframework.beans.factory.config.BeanDefinition; import org.springframework.context.annotation.Scope; @@ -21,13 +25,29 @@ protected StepPhase executeStep(ProcessContext context) { CloudServiceKey serviceKeyToProcess = context.getVariable(Variables.SERVICE_KEY_TO_PROCESS); CloudControllerClient client = context.getControllerClient(); - ServiceKeyCredentialResolver resolver = new ServiceKeyCredentialResolver(client, Collections.emptyMap()); + Map serviceInstanceNamesByRelationship = buildServiceInstanceNamesByRelationship(context); + ServiceKeyCredentialResolver resolver = new ServiceKeyCredentialResolver(client, serviceInstanceNamesByRelationship); CloudServiceKey resolvedServiceKey = resolver.resolve(serviceKeyToProcess); context.setVariable(Variables.SERVICE_KEY_TO_PROCESS, resolvedServiceKey); return StepPhase.DONE; } + private Map buildServiceInstanceNamesByRelationship(ProcessContext context) { + DeploymentDescriptor descriptor = context.getVariable(Variables.COMPLETE_DEPLOYMENT_DESCRIPTOR); + Map serviceInstanceNamesByRelationship = new HashMap<>(); + if (descriptor == null) { + return serviceInstanceNamesByRelationship; + } + for (Resource resource : descriptor.getResources()) { + Object serviceName = resource.getParameters() + .get(SupportedParameters.SERVICE_NAME); + serviceInstanceNamesByRelationship.put(resource.getName(), + serviceName instanceof String ? (String) serviceName : resource.getName()); + } + return serviceInstanceNamesByRelationship; + } + @Override protected String getStepErrorMessage(ProcessContext context) { CloudServiceKey serviceKeyToProcess = context.getVariable(Variables.SERVICE_KEY_TO_PROCESS); diff --git a/multiapps-controller-process/src/test/java/org/cloudfoundry/multiapps/controller/process/steps/ResolveServiceKeyCredentialReferencesStepTest.java b/multiapps-controller-process/src/test/java/org/cloudfoundry/multiapps/controller/process/steps/ResolveServiceKeyCredentialReferencesStepTest.java index a9e7ede16b..18c8042463 100644 --- a/multiapps-controller-process/src/test/java/org/cloudfoundry/multiapps/controller/process/steps/ResolveServiceKeyCredentialReferencesStepTest.java +++ b/multiapps-controller-process/src/test/java/org/cloudfoundry/multiapps/controller/process/steps/ResolveServiceKeyCredentialReferencesStepTest.java @@ -1,11 +1,15 @@ package org.cloudfoundry.multiapps.controller.process.steps; +import java.util.List; import java.util.Map; import org.cloudfoundry.multiapps.controller.client.facade.domain.CloudServiceKey; import org.cloudfoundry.multiapps.controller.client.facade.domain.ImmutableCloudServiceKey; import org.cloudfoundry.multiapps.controller.client.lib.domain.ImmutableCloudServiceInstanceExtended; +import org.cloudfoundry.multiapps.controller.core.model.SupportedParameters; import org.cloudfoundry.multiapps.controller.process.variables.Variables; +import org.cloudfoundry.multiapps.mta.model.DeploymentDescriptor; +import org.cloudfoundry.multiapps.mta.model.Resource; import org.junit.jupiter.api.Test; import org.mockito.Mockito; @@ -13,6 +17,7 @@ class ResolveServiceKeyCredentialReferencesStepTest extends SyncFlowableStepTest { + private static final String REFERENCED_RESOURCE = "ztis-config"; private static final String REFERENCED_SERVICE_INSTANCE = "ztis-config"; private static final String REFERENCED_KEY = "ztis-key"; @@ -49,6 +54,33 @@ void testNoOpWhenNoCredentialReferences() { .getServiceKey(Mockito.anyString(), Mockito.anyString()); } + @Test + void testUsesReferencedResourceServiceNameForReadBack() { + String referencedResourceName = "ztis-resource"; + String referencedServiceInstanceName = "custom-ztis-service-name"; + DeploymentDescriptor descriptor = DeploymentDescriptor.createV3() + .setId("test-id") + .setResources(List.of(Resource.createV3() + .setName(referencedResourceName) + .setParameters(Map.of(SupportedParameters.SERVICE_NAME, + referencedServiceInstanceName)))); + context.setVariable(Variables.COMPLETE_DEPLOYMENT_DESCRIPTOR, descriptor); + CloudServiceKey keyToProcess = buildKey("caas-v2-key", "caas-v2", + Map.of("issuer", + "{ds/" + referencedResourceName + "/service-keys." + REFERENCED_KEY + + ".credentials.issuer}")); + context.setVariable(Variables.SERVICE_KEY_TO_PROCESS, keyToProcess); + Mockito.when(client.getServiceKey(referencedServiceInstanceName, REFERENCED_KEY)) + .thenReturn(buildKey(REFERENCED_KEY, referencedServiceInstanceName, Map.of("issuer", "CN=the-issuer"))); + + step.execute(execution); + + assertStepFinishedSuccessfully(); + CloudServiceKey resolved = context.getVariable(Variables.SERVICE_KEY_TO_PROCESS); + assertEquals("CN=the-issuer", resolved.getCredentials() + .get("issuer")); + } + private CloudServiceKey buildKey(String keyName, String serviceInstanceName, Map credentials) { return ImmutableCloudServiceKey.builder() .name(keyName) From 5fe8070fd4b35bfb722c780ab4895d7ec1990920 Mon Sep 17 00:00:00 2001 From: stiv03 Date: Tue, 20 Oct 2026 10:22:57 +0300 Subject: [PATCH 4/5] Resolve service-key credential references in service-binding config Extend the dynamic service-key credential feature to a module's service-binding parameters (requires[].parameters.config), not only service-keys[].config. Factor the map-level resolution out of ServiceKeyCredentialResolver into resolveReferencesInMap, and add an idempotent ResolveBindingCredentialReferencesStep that rewrites SERVICE_BINDING_PARAMETERS just before BindServiceToApplicationStep, wired into manage-app-service-bindings.bpmn on the bind path (the POLL re-entry edge still targets the bind task directly; resolution is a no-op once no references remain). This supports the client scenario of injecting a produced service key's credential (e.g. ZTIS issuer/subject) into another service's binding config. The reference must name a service key; a bare managed-service reference has no retrievable credential output in CF. --- .../controller/process/Messages.java | 1 + ...esolveBindingCredentialReferencesStep.java | 57 ++++++++++++ .../util/ServiceKeyCredentialResolver.java | 28 ++++-- .../process/manage-app-service-bindings.bpmn | 4 +- ...veBindingCredentialReferencesStepTest.java | 92 +++++++++++++++++++ .../ServiceKeyCredentialResolverTest.java | 15 +++ 6 files changed, 188 insertions(+), 9 deletions(-) create mode 100644 multiapps-controller-process/src/main/java/org/cloudfoundry/multiapps/controller/process/steps/ResolveBindingCredentialReferencesStep.java create mode 100644 multiapps-controller-process/src/test/java/org/cloudfoundry/multiapps/controller/process/steps/ResolveBindingCredentialReferencesStepTest.java diff --git a/multiapps-controller-process/src/main/java/org/cloudfoundry/multiapps/controller/process/Messages.java b/multiapps-controller-process/src/main/java/org/cloudfoundry/multiapps/controller/process/Messages.java index 071e1cfff1..8ee26944e7 100755 --- a/multiapps-controller-process/src/main/java/org/cloudfoundry/multiapps/controller/process/Messages.java +++ b/multiapps-controller-process/src/main/java/org/cloudfoundry/multiapps/controller/process/Messages.java @@ -835,6 +835,7 @@ 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 UNABLE_TO_RESOLVE_SERVICE_KEY_CREDENTIAL_REFERENCE = "Unable to resolve service key credential reference \"{0}\": field \"{1}\" was not found in the credentials of service key \"{2}\" of service instance \"{3}\""; + public static final String ERROR_RESOLVING_BINDING_CREDENTIAL_REFERENCES = "Error while resolving service key credential references in service binding parameters"; protected Messages() { } diff --git a/multiapps-controller-process/src/main/java/org/cloudfoundry/multiapps/controller/process/steps/ResolveBindingCredentialReferencesStep.java b/multiapps-controller-process/src/main/java/org/cloudfoundry/multiapps/controller/process/steps/ResolveBindingCredentialReferencesStep.java new file mode 100644 index 0000000000..64b38c9f9f --- /dev/null +++ b/multiapps-controller-process/src/main/java/org/cloudfoundry/multiapps/controller/process/steps/ResolveBindingCredentialReferencesStep.java @@ -0,0 +1,57 @@ +package org.cloudfoundry.multiapps.controller.process.steps; + +import java.util.HashMap; +import java.util.Map; + +import jakarta.inject.Named; +import org.cloudfoundry.multiapps.controller.client.facade.CloudControllerClient; +import org.cloudfoundry.multiapps.controller.core.model.SupportedParameters; +import org.cloudfoundry.multiapps.controller.process.Messages; +import org.cloudfoundry.multiapps.controller.process.util.ServiceKeyCredentialResolver; +import org.cloudfoundry.multiapps.controller.process.variables.Variables; +import org.cloudfoundry.multiapps.mta.model.DeploymentDescriptor; +import org.cloudfoundry.multiapps.mta.model.Resource; +import org.springframework.beans.factory.config.BeanDefinition; +import org.springframework.context.annotation.Scope; + +@Named("resolveBindingCredentialReferencesStep") +@Scope(BeanDefinition.SCOPE_PROTOTYPE) +public class ResolveBindingCredentialReferencesStep extends SyncFlowableStep { + + @Override + protected StepPhase executeStep(ProcessContext context) { + Map bindingParameters = context.getVariable(Variables.SERVICE_BINDING_PARAMETERS); + if (bindingParameters == null || bindingParameters.isEmpty()) { + return StepPhase.DONE; + } + CloudControllerClient client = context.getControllerClient(); + Map serviceInstanceNamesByRelationship = buildServiceInstanceNamesByRelationship(context); + ServiceKeyCredentialResolver resolver = new ServiceKeyCredentialResolver(client, serviceInstanceNamesByRelationship); + + Map resolvedBindingParameters = resolver.resolveReferencesInMap(bindingParameters); + + context.setVariable(Variables.SERVICE_BINDING_PARAMETERS, resolvedBindingParameters); + return StepPhase.DONE; + } + + private Map buildServiceInstanceNamesByRelationship(ProcessContext context) { + DeploymentDescriptor descriptor = context.getVariable(Variables.COMPLETE_DEPLOYMENT_DESCRIPTOR); + Map serviceInstanceNamesByRelationship = new HashMap<>(); + if (descriptor == null) { + return serviceInstanceNamesByRelationship; + } + for (Resource resource : descriptor.getResources()) { + Object serviceName = resource.getParameters() + .get(SupportedParameters.SERVICE_NAME); + serviceInstanceNamesByRelationship.put(resource.getName(), + serviceName instanceof String ? (String) serviceName : resource.getName()); + } + return serviceInstanceNamesByRelationship; + } + + @Override + protected String getStepErrorMessage(ProcessContext context) { + return Messages.ERROR_RESOLVING_BINDING_CREDENTIAL_REFERENCES; + } + +} \ No newline at end of file diff --git a/multiapps-controller-process/src/main/java/org/cloudfoundry/multiapps/controller/process/util/ServiceKeyCredentialResolver.java b/multiapps-controller-process/src/main/java/org/cloudfoundry/multiapps/controller/process/util/ServiceKeyCredentialResolver.java index 21087966ed..3489119580 100644 --- a/multiapps-controller-process/src/main/java/org/cloudfoundry/multiapps/controller/process/util/ServiceKeyCredentialResolver.java +++ b/multiapps-controller-process/src/main/java/org/cloudfoundry/multiapps/controller/process/util/ServiceKeyCredentialResolver.java @@ -30,20 +30,32 @@ public CloudServiceKey resolve(CloudServiceKey serviceKey) { if (credentials == null || credentials.isEmpty()) { return serviceKey; } - Map resolvedCredentials = new LinkedHashMap<>(credentials); + Map resolvedCredentials = resolveReferencesInMap(credentials); + if (resolvedCredentials == credentials) { + return serviceKey; + } + return ImmutableCloudServiceKey.copyOf(serviceKey) + .withCredentials(resolvedCredentials); + } + + /** + * Resolves {@code {ds/...}} service-key credential references in an arbitrary map (e.g. a module's service-binding config), returning a + * new map with the resolved values. Returns the same map instance when there is nothing to resolve. + */ + public Map resolveReferencesInMap(Map values) { + if (values == null || values.isEmpty()) { + return values; + } + Map resolved = new LinkedHashMap<>(values); boolean changed = false; - for (Map.Entry entry : credentials.entrySet()) { + for (Map.Entry entry : values.entrySet()) { if (!isServiceKeyCredentialReference(entry.getValue())) { continue; } - resolvedCredentials.put(entry.getKey(), resolveReference((String) entry.getValue())); + resolved.put(entry.getKey(), resolveReference((String) entry.getValue())); changed = true; } - if (!changed) { - return serviceKey; - } - return ImmutableCloudServiceKey.copyOf(serviceKey) - .withCredentials(resolvedCredentials); + return changed ? resolved : values; } private boolean isServiceKeyCredentialReference(Object value) { diff --git a/multiapps-controller-process/src/main/resources/org/cloudfoundry/multiapps/controller/process/manage-app-service-bindings.bpmn b/multiapps-controller-process/src/main/resources/org/cloudfoundry/multiapps/controller/process/manage-app-service-bindings.bpmn index 1d37925af6..b0b0ad3c11 100644 --- a/multiapps-controller-process/src/main/resources/org/cloudfoundry/multiapps/controller/process/manage-app-service-bindings.bpmn +++ b/multiapps-controller-process/src/main/resources/org/cloudfoundry/multiapps/controller/process/manage-app-service-bindings.bpmn @@ -9,6 +9,8 @@ + + @@ -23,7 +25,7 @@ - + diff --git a/multiapps-controller-process/src/test/java/org/cloudfoundry/multiapps/controller/process/steps/ResolveBindingCredentialReferencesStepTest.java b/multiapps-controller-process/src/test/java/org/cloudfoundry/multiapps/controller/process/steps/ResolveBindingCredentialReferencesStepTest.java new file mode 100644 index 0000000000..e579fcad5a --- /dev/null +++ b/multiapps-controller-process/src/test/java/org/cloudfoundry/multiapps/controller/process/steps/ResolveBindingCredentialReferencesStepTest.java @@ -0,0 +1,92 @@ +package org.cloudfoundry.multiapps.controller.process.steps; + +import java.util.List; +import java.util.Map; + +import org.cloudfoundry.multiapps.controller.client.facade.domain.CloudServiceKey; +import org.cloudfoundry.multiapps.controller.client.facade.domain.ImmutableCloudServiceKey; +import org.cloudfoundry.multiapps.controller.client.lib.domain.ImmutableCloudServiceInstanceExtended; +import org.cloudfoundry.multiapps.controller.core.model.SupportedParameters; +import org.cloudfoundry.multiapps.controller.process.variables.Variables; +import org.cloudfoundry.multiapps.mta.model.DeploymentDescriptor; +import org.cloudfoundry.multiapps.mta.model.Resource; +import org.junit.jupiter.api.Test; +import org.mockito.Mockito; + +import static org.junit.jupiter.api.Assertions.assertEquals; + +class ResolveBindingCredentialReferencesStepTest extends SyncFlowableStepTest { + + private static final String REFERENCED_RESOURCE = "ztis-config"; + private static final String REFERENCED_SERVICE_INSTANCE = "ztis-config"; + private static final String REFERENCED_KEY = "ztis-key"; + + @Test + void testResolvesCredentialReferenceInBindingParameters() { + context.setVariable(Variables.SERVICE_BINDING_PARAMETERS, + Map.of("issuer", + "{ds/" + REFERENCED_RESOURCE + "/service-keys." + REFERENCED_KEY + ".credentials.issuer}", + "app-identifier", "my-app-identifier")); + Mockito.when(client.getServiceKey(REFERENCED_SERVICE_INSTANCE, REFERENCED_KEY)) + .thenReturn(buildKey(REFERENCED_KEY, REFERENCED_SERVICE_INSTANCE, Map.of("issuer", "CN=the-issuer"))); + + step.execute(execution); + + assertStepFinishedSuccessfully(); + Map resolved = context.getVariable(Variables.SERVICE_BINDING_PARAMETERS); + assertEquals("CN=the-issuer", resolved.get("issuer")); + assertEquals("my-app-identifier", resolved.get("app-identifier")); + } + + @Test + void testNoOpWhenNoReferences() { + context.setVariable(Variables.SERVICE_BINDING_PARAMETERS, Map.of("app-identifier", "my-app-identifier")); + + step.execute(execution); + + assertStepFinishedSuccessfully(); + Map resolved = context.getVariable(Variables.SERVICE_BINDING_PARAMETERS); + assertEquals("my-app-identifier", resolved.get("app-identifier")); + Mockito.verify(client, Mockito.never()) + .getServiceKey(Mockito.anyString(), Mockito.anyString()); + } + + @Test + void testUsesReferencedResourceServiceNameForReadBack() { + String referencedResourceName = "ztis-resource"; + String referencedServiceInstanceName = "custom-ztis-service-name"; + DeploymentDescriptor descriptor = DeploymentDescriptor.createV3() + .setId("test-id") + .setResources(List.of(Resource.createV3() + .setName(referencedResourceName) + .setParameters(Map.of(SupportedParameters.SERVICE_NAME, + referencedServiceInstanceName)))); + context.setVariable(Variables.COMPLETE_DEPLOYMENT_DESCRIPTOR, descriptor); + context.setVariable(Variables.SERVICE_BINDING_PARAMETERS, + Map.of("issuer", + "{ds/" + referencedResourceName + "/service-keys." + REFERENCED_KEY + ".credentials.issuer}")); + Mockito.when(client.getServiceKey(referencedServiceInstanceName, REFERENCED_KEY)) + .thenReturn(buildKey(REFERENCED_KEY, referencedServiceInstanceName, Map.of("issuer", "CN=the-issuer"))); + + step.execute(execution); + + assertStepFinishedSuccessfully(); + Map resolved = context.getVariable(Variables.SERVICE_BINDING_PARAMETERS); + assertEquals("CN=the-issuer", resolved.get("issuer")); + } + + private CloudServiceKey buildKey(String keyName, String serviceInstanceName, Map credentials) { + return ImmutableCloudServiceKey.builder() + .name(keyName) + .credentials(credentials) + .serviceInstance(ImmutableCloudServiceInstanceExtended.builder() + .name(serviceInstanceName) + .build()) + .build(); + } + + @Override + protected ResolveBindingCredentialReferencesStep createStep() { + return new ResolveBindingCredentialReferencesStep(); + } +} \ No newline at end of file diff --git a/multiapps-controller-process/src/test/java/org/cloudfoundry/multiapps/controller/process/util/ServiceKeyCredentialResolverTest.java b/multiapps-controller-process/src/test/java/org/cloudfoundry/multiapps/controller/process/util/ServiceKeyCredentialResolverTest.java index 7428358b11..e0b19874cb 100644 --- a/multiapps-controller-process/src/test/java/org/cloudfoundry/multiapps/controller/process/util/ServiceKeyCredentialResolverTest.java +++ b/multiapps-controller-process/src/test/java/org/cloudfoundry/multiapps/controller/process/util/ServiceKeyCredentialResolverTest.java @@ -48,6 +48,21 @@ void testResolvesServiceKeyCredentialReference() { .get("issuer")); } + @Test + void testResolvesReferencesInMap() { + Mockito.when(client.getServiceKey(REFERENCED_SERVICE_INSTANCE, REFERENCED_KEY)) + .thenReturn(buildReferencedKey(Map.of("issuer", "CN=the-issuer"))); + Map bindingConfig = Map.of("issuer", + "{ds/" + REFERENCED_RESOURCE + "/service-keys." + REFERENCED_KEY + + ".credentials.issuer}", + "static", "unchanged"); + + Map resolved = resolver.resolveReferencesInMap(bindingConfig); + + Assertions.assertEquals("CN=the-issuer", resolved.get("issuer")); + Assertions.assertEquals("unchanged", resolved.get("static")); + } + @Test void testLeavesPlainCredentialsUntouched() { CloudServiceKey keyToResolve = buildKeyWithCredentials(Map.of("app-identifier", "caas-coll-client")); From 0a3d9936fbcdc8e5a3d7aaea7a800531d0f1869b Mon Sep 17 00:00:00 2001 From: stiv03 Date: Tue, 20 Oct 2026 11:15:22 +0300 Subject: [PATCH 5/5] Map binding-config references via SERVICES_TO_BIND, not the descriptor The binding-resolution step runs inside the manageAppServiceBindingSubProcess Flowable sub-process, which does not inherit COMPLETE_DEPLOYMENT_DESCRIPTOR. Build the relationship->service-instance-name map from Variables.SERVICES_TO_BIND (each CloudServiceInstanceExtended carries getResourceName() and getName()), which is passed into the sub-process and already precomputes the resolved CF service instance names. The referenced resource must be among the module's required dependencies so it appears in SERVICES_TO_BIND. --- .../ResolveBindingCredentialReferencesStep.java | 17 ++++++----------- ...olveBindingCredentialReferencesStepTest.java | 15 +++++---------- 2 files changed, 11 insertions(+), 21 deletions(-) diff --git a/multiapps-controller-process/src/main/java/org/cloudfoundry/multiapps/controller/process/steps/ResolveBindingCredentialReferencesStep.java b/multiapps-controller-process/src/main/java/org/cloudfoundry/multiapps/controller/process/steps/ResolveBindingCredentialReferencesStep.java index 64b38c9f9f..e334d3626f 100644 --- a/multiapps-controller-process/src/main/java/org/cloudfoundry/multiapps/controller/process/steps/ResolveBindingCredentialReferencesStep.java +++ b/multiapps-controller-process/src/main/java/org/cloudfoundry/multiapps/controller/process/steps/ResolveBindingCredentialReferencesStep.java @@ -1,16 +1,15 @@ package org.cloudfoundry.multiapps.controller.process.steps; import java.util.HashMap; +import java.util.List; import java.util.Map; import jakarta.inject.Named; import org.cloudfoundry.multiapps.controller.client.facade.CloudControllerClient; -import org.cloudfoundry.multiapps.controller.core.model.SupportedParameters; +import org.cloudfoundry.multiapps.controller.client.lib.domain.CloudServiceInstanceExtended; import org.cloudfoundry.multiapps.controller.process.Messages; import org.cloudfoundry.multiapps.controller.process.util.ServiceKeyCredentialResolver; import org.cloudfoundry.multiapps.controller.process.variables.Variables; -import org.cloudfoundry.multiapps.mta.model.DeploymentDescriptor; -import org.cloudfoundry.multiapps.mta.model.Resource; import org.springframework.beans.factory.config.BeanDefinition; import org.springframework.context.annotation.Scope; @@ -27,7 +26,6 @@ protected StepPhase executeStep(ProcessContext context) { CloudControllerClient client = context.getControllerClient(); Map serviceInstanceNamesByRelationship = buildServiceInstanceNamesByRelationship(context); ServiceKeyCredentialResolver resolver = new ServiceKeyCredentialResolver(client, serviceInstanceNamesByRelationship); - Map resolvedBindingParameters = resolver.resolveReferencesInMap(bindingParameters); context.setVariable(Variables.SERVICE_BINDING_PARAMETERS, resolvedBindingParameters); @@ -35,16 +33,13 @@ protected StepPhase executeStep(ProcessContext context) { } private Map buildServiceInstanceNamesByRelationship(ProcessContext context) { - DeploymentDescriptor descriptor = context.getVariable(Variables.COMPLETE_DEPLOYMENT_DESCRIPTOR); Map serviceInstanceNamesByRelationship = new HashMap<>(); - if (descriptor == null) { + List servicesToBind = context.getVariable(Variables.SERVICES_TO_BIND); + if (servicesToBind == null) { return serviceInstanceNamesByRelationship; } - for (Resource resource : descriptor.getResources()) { - Object serviceName = resource.getParameters() - .get(SupportedParameters.SERVICE_NAME); - serviceInstanceNamesByRelationship.put(resource.getName(), - serviceName instanceof String ? (String) serviceName : resource.getName()); + for (CloudServiceInstanceExtended service : servicesToBind) { + serviceInstanceNamesByRelationship.put(service.getResourceName(), service.getName()); } return serviceInstanceNamesByRelationship; } diff --git a/multiapps-controller-process/src/test/java/org/cloudfoundry/multiapps/controller/process/steps/ResolveBindingCredentialReferencesStepTest.java b/multiapps-controller-process/src/test/java/org/cloudfoundry/multiapps/controller/process/steps/ResolveBindingCredentialReferencesStepTest.java index e579fcad5a..4c1a616bcd 100644 --- a/multiapps-controller-process/src/test/java/org/cloudfoundry/multiapps/controller/process/steps/ResolveBindingCredentialReferencesStepTest.java +++ b/multiapps-controller-process/src/test/java/org/cloudfoundry/multiapps/controller/process/steps/ResolveBindingCredentialReferencesStepTest.java @@ -6,10 +6,7 @@ import org.cloudfoundry.multiapps.controller.client.facade.domain.CloudServiceKey; import org.cloudfoundry.multiapps.controller.client.facade.domain.ImmutableCloudServiceKey; import org.cloudfoundry.multiapps.controller.client.lib.domain.ImmutableCloudServiceInstanceExtended; -import org.cloudfoundry.multiapps.controller.core.model.SupportedParameters; import org.cloudfoundry.multiapps.controller.process.variables.Variables; -import org.cloudfoundry.multiapps.mta.model.DeploymentDescriptor; -import org.cloudfoundry.multiapps.mta.model.Resource; import org.junit.jupiter.api.Test; import org.mockito.Mockito; @@ -55,13 +52,11 @@ void testNoOpWhenNoReferences() { void testUsesReferencedResourceServiceNameForReadBack() { String referencedResourceName = "ztis-resource"; String referencedServiceInstanceName = "custom-ztis-service-name"; - DeploymentDescriptor descriptor = DeploymentDescriptor.createV3() - .setId("test-id") - .setResources(List.of(Resource.createV3() - .setName(referencedResourceName) - .setParameters(Map.of(SupportedParameters.SERVICE_NAME, - referencedServiceInstanceName)))); - context.setVariable(Variables.COMPLETE_DEPLOYMENT_DESCRIPTOR, descriptor); + context.setVariable(Variables.SERVICES_TO_BIND, + List.of(ImmutableCloudServiceInstanceExtended.builder() + .resourceName(referencedResourceName) + .name(referencedServiceInstanceName) + .build())); context.setVariable(Variables.SERVICE_BINDING_PARAMETERS, Map.of("issuer", "{ds/" + referencedResourceName + "/service-keys." + REFERENCED_KEY + ".credentials.issuer}"));