Skip to content
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

Service binding doesn't work as SERVICE_BINDING_ROOT expression is not expanded #37461

Closed
michalvavrik opened this issue Dec 1, 2023 · 17 comments · Fixed by #37794
Closed

Service binding doesn't work as SERVICE_BINDING_ROOT expression is not expanded #37461

michalvavrik opened this issue Dec 1, 2023 · 17 comments · Fixed by #37794
Assignees
Labels
Milestone

Comments

@michalvavrik
Copy link
Member

Describe the bug

Service binding doesn't work as mounted secret is never found as file because config expression is not expanded.
Problem was brought in when trying to fix other problem by this PR: #34792

Expected behavior

Service binding works.

Actual behavior

Service binding in OCP 4.14 and 4.11 binds secret in following fashion:

...
Environment:
  SERVICE_BINDING_ROOT:        /bindings
Mounts:
  /bindings/app-postgresql from app-postgresql (rw)
...

and application never starts with:

15:13:04,436 Service Binding root '/deployments/${SERVICE_BINDING_ROOT:}' does not exist

I put some logging to io.quarkus.kubernetes.service.binding.runtime.KubernetesServiceBindingConfigSourceFactory#getConfigSources(io.smallrye.config.ConfigSourceContext) and got:

...
17:29:36,283 INFO  [app] source io.smallrye.config.ConfigSourceContext$ConfigSourceContextConfigSource key SERVICE_BINDING_ROOT val /bindings
...

How to Reproduce?

Steps to reproduce:

  1. be logged in OpenShift, have installed Service Binding and Crunchy PG operator and your user must have rights to list csv etc. (in short, for reproducer only you can use cluster admin)
  2. git clone [email protected]:michalvavrik/quarkus-test-suite.git
  3. cd quarkus-test-suite/service-binding/postgresql-crunchy-classic
  4. git checkout feature/fix-and-enable-crunchy-sb-tests
  5. mvn clean verify -Dopenshift

Output of uname -a or ver

Fedora 38

Output of java -version

OpenJDK Runtime Environment Temurin-17.0.7+9

Quarkus version or git rev

999-SNAPSHOT,

Build tool (ie. output of mvnw --version or gradlew --version)

Apache Maven 3.9.3

Additional information

I have a fix.

@michalvavrik michalvavrik added the kind/bug Something isn't working label Dec 1, 2023
Copy link

quarkus-bot bot commented Dec 1, 2023

/cc @geoand (kubernetes,openshift), @iocanel (kubernetes,openshift)

@michalvavrik michalvavrik changed the title Service binding doesn't work as SERVICE_BINDING_ROOT expresion is not expanded Service binding doesn't work as SERVICE_BINDING_ROOT expression is not expanded Dec 2, 2023
@geoand
Copy link
Contributor

geoand commented Dec 4, 2023

cc @radcortez

@michalvavrik
Copy link
Member Author

cc @radcortez

I just opened this issue to make it clear how long service binding didn't work, as it is IMHO showcase of how much is service binding used by users in non-LTS releases considering this wasn't reported between 3.3 and 3.6. I wonder if anyone but Quarkus QE use it.

@radcortez can just look at #37462.

@radcortez
Copy link
Member

The ConfigSourceContext used to build source factories should evaluate all the interceptors, because the config chain is constructed with all the interceptors plus non-factory sources to create factory sources. Adding the expression interceptor to the factory could work, but it may hide another issue.

I've tried to replicate the issue here: smallrye/smallrye-config@56f6e27, but it seems to work, so maybe I'm missing something.

Can you confirm on your debug that you can see the Expression interceptor in the context (without adding it manually)?

@michalvavrik
Copy link
Member Author

The ConfigSourceContext used to build source factories should evaluate all the interceptors, because the config chain is constructed with all the interceptors plus non-factory sources to create factory sources. Adding the expression interceptor to the factory could work, but it may hide another issue.

I've tried to replicate the issue here: smallrye/smallrye-config@56f6e27, but it seems to work, so maybe I'm missing something.

ok

Can you confirm on your debug that you can see the Expression interceptor in the context (without adding it manually)?

I can't debug JVM app inside OpenShift, added some reflection

diff --git a/extensions/kubernetes-service-binding/runtime/src/main/java/io/quarkus/kubernetes/service/binding/runtime/KubernetesServiceBindingConfigSourceFactory.java b/extensions/kubernetes-service-binding/runtime/src/main/java/io/quarkus/kubernetes/service/binding/runtime/KubernetesServiceBindingConfigSourceFactory.java
index df605dc3633..fff962b97f8 100644
--- a/extensions/kubernetes-service-binding/runtime/src/main/java/io/quarkus/kubernetes/service/binding/runtime/KubernetesServiceBindingConfigSourceFactory.java
+++ b/extensions/kubernetes-service-binding/runtime/src/main/java/io/quarkus/kubernetes/service/binding/runtime/KubernetesServiceBindingConfigSourceFactory.java
@@ -3,12 +3,16 @@
 import static java.util.Collections.emptyList;
 
 import java.io.File;
+import java.lang.reflect.Field;
+import java.lang.reflect.InvocationTargetException;
+import java.lang.reflect.Method;
 import java.nio.file.Files;
 import java.nio.file.Path;
 import java.nio.file.Paths;
 import java.util.ArrayList;
 import java.util.Comparator;
 import java.util.HashMap;
+import java.util.Iterator;
 import java.util.List;
 import java.util.Map;
 import java.util.Optional;
@@ -19,6 +23,7 @@
 
 import io.smallrye.config.ConfigSourceContext;
 import io.smallrye.config.ConfigSourceFactory;
+import io.smallrye.config.ConfigSourceInterceptor;
 import io.smallrye.config.SmallRyeConfig;
 import io.smallrye.config.SmallRyeConfigBuilder;
 
@@ -34,6 +39,77 @@ public Iterable<ConfigSource> getConfigSources(final ConfigSourceContext context
                 .withMappingIgnore("quarkus.**")
                 .build();
 
+        System.out.println("reflection for " + context);
+        for (Field declaredField : context.getClass().getDeclaredFields()) {
+            System.out.println("field is " + declaredField.getName());
+            if (declaredField.getName().equals("context")) {
+                declaredField.setAccessible(true);
+                try {
+                    var ctx = declaredField.get(context);
+                    ctx: while (ctx != null) {
+                        System.out.println("ctx is " + ctx);
+                        for (Field field : ctx.getClass().getDeclaredFields()) {
+                            System.out.println("ctx field " + field.getName());
+                            if (field.getName().equals("interceptor")) {
+                                field.setAccessible(true);
+                                ConfigSourceInterceptor interceptor = (ConfigSourceInterceptor) field.get(ctx);
+                                System.out.println("found interceptor " + interceptor);
+                            }
+                            if (field.getName().equals("next")) {
+                                field.setAccessible(true);
+                                var ctx1 = field.get(ctx);
+                                if (ctx1 == ctx) {
+                                    break ctx;
+                                }
+                                ctx = ctx1;
+                                continue ctx;
+                            }
+                        }
+                        ctx = null;
+                    }
+                } catch (IllegalAccessException e) {
+                    throw new RuntimeException(e);
+                }
+                break;
+            }
+        }
+
+        config.getConfigSources().forEach(cs -> System.out.println("config source: " + cs.getName()));
+
+        for (Field field : config.getClass().getDeclaredFields()) {
+            System.out.println("field is " + field.getName());
+            if (field.getName().equals("configSources")) {
+                field.setAccessible(true);
+                try {
+                    var configSources = field.get(config);
+                    for (Field declaredField : configSources.getClass().getDeclaredFields()) {
+                        System.out.println("config source field is " + declaredField.getName());
+                        if (declaredField.getName().equals("interceptorChain")) {
+                            declaredField.setAccessible(true);
+                            var interceptorChain = declaredField.get(configSources);
+                            for (Method declaredMethod : interceptorChain.getClass().getDeclaredMethods()) {
+                                System.out.println("interceptor chain declared method is " + declaredMethod.getName());
+                                if (declaredMethod.getName().equals("iterateNames")) {
+                                    declaredMethod.setAccessible(true);
+                                    Iterator<String> intNames = (Iterator<String>) declaredMethod.invoke(interceptorChain);
+                                    intNames.forEachRemaining(name -> {
+                                        System.out.println("prop name is: " + name);
+                                    });
+                                    break;
+                                }
+                            }
+                            break;
+                        }
+                    }
+                } catch (IllegalAccessException e) {
+                    throw new RuntimeException(e);
+                } catch (InvocationTargetException e) {
+                    throw new RuntimeException(e);
+                }
+                break;
+            }
+        }
+
         KubernetesServiceBindingConfig kubernetesServiceBindingConfig = config
                 .getConfigMapping(KubernetesServiceBindingConfig.class);

and printed out following:

19:48:19,529 INFO  [app] reflection for io.smallrye.config.SmallRyeConfigSourceContext@84487f4
19:48:19,529 INFO  [app] field is context
19:48:19,529 INFO  [app] ctx is io.smallrye.config.SmallRyeConfigSourceInterceptorContext@2dfe5525
19:48:19,529 INFO  [app] ctx field serialVersionUID
19:48:19,529 INFO  [app] ctx field interceptor
19:48:19,529 INFO  [app] found interceptor io.quarkus.kubernetes.runtime.config.KubernetesConfigFallback@1290c49
19:48:19,529 INFO  [app] ctx field next
19:48:19,529 INFO  [app] ctx is io.smallrye.config.SmallRyeConfigSourceInterceptorContext@6a9b9909
19:48:19,529 INFO  [app] ctx field serialVersionUID
19:48:19,529 INFO  [app] ctx field interceptor
19:48:19,529 INFO  [app] found interceptor io.smallrye.config.FallbackConfigSourceInterceptor@55d9b8f0
19:48:19,529 INFO  [app] ctx field next
19:48:19,530 INFO  [app] ctx is io.smallrye.config.SmallRyeConfigSourceInterceptorContext@a518813
19:48:19,530 INFO  [app] ctx field serialVersionUID
19:48:19,530 INFO  [app] ctx field interceptor
19:48:19,530 INFO  [app] found interceptor io.smallrye.config.SecretKeysHandlerConfigSourceInterceptor@43d38654
19:48:19,530 INFO  [app] ctx field next
19:48:19,530 INFO  [app] ctx is io.smallrye.config.SmallRyeConfigSourceInterceptorContext@75361cf6
19:48:19,530 INFO  [app] ctx field serialVersionUID
19:48:19,530 INFO  [app] ctx field interceptor
19:48:19,530 INFO  [app] found interceptor io.smallrye.config.ExpressionConfigSourceInterceptor@6d303498
19:48:19,530 INFO  [app] ctx field next
19:48:19,530 INFO  [app] ctx is io.smallrye.config.SmallRyeConfigSourceInterceptorContext@6ba7383d
19:48:19,530 INFO  [app] ctx field serialVersionUID
19:48:19,530 INFO  [app] ctx field interceptor
19:48:19,530 INFO  [app] found interceptor io.smallrye.config.LoggingConfigSourceInterceptor@3419e23b
19:48:19,530 INFO  [app] ctx field next
19:48:19,530 INFO  [app] ctx is io.smallrye.config.SmallRyeConfigSourceInterceptorContext@710d89e2
19:48:19,530 INFO  [app] ctx field serialVersionUID
19:48:19,530 INFO  [app] ctx field interceptor
19:48:19,530 INFO  [app] found interceptor io.smallrye.config.ProfileConfigSourceInterceptor@1d75e7af
19:48:19,530 INFO  [app] ctx field next
19:48:19,530 INFO  [app] ctx is io.smallrye.config.SmallRyeConfigSourceInterceptorContext@4fc142ec
19:48:19,531 INFO  [app] ctx field serialVersionUID
19:48:19,531 INFO  [app] ctx field interceptor
19:48:19,531 INFO  [app] found interceptor io.smallrye.config.RelocateConfigSourceInterceptor@34b27915
19:48:19,531 INFO  [app] ctx field next
19:48:19,531 INFO  [app] ctx is io.smallrye.config.SmallRyeConfigSourceInterceptorContext@29eda4f8
19:48:19,531 INFO  [app] ctx field serialVersionUID
19:48:19,531 INFO  [app] ctx field interceptor
19:48:19,531 INFO  [app] found interceptor io.smallrye.config.RelocateConfigSourceInterceptor@1b9776f5
19:48:19,531 INFO  [app] ctx field next
19:48:19,531 INFO  [app] ctx is io.smallrye.config.SmallRyeConfigSourceInterceptorContext@5e048149
19:48:19,531 INFO  [app] ctx field serialVersionUID
19:48:19,531 INFO  [app] ctx field interceptor
19:48:19,531 INFO  [app] found interceptor io.smallrye.config.RelocateConfigSourceInterceptor@79d9214d
19:48:19,531 INFO  [app] ctx field next
19:48:19,531 INFO  [app] ctx is io.smallrye.config.SmallRyeConfigSourceInterceptorContext@3d5790ea
19:48:19,531 INFO  [app] ctx field serialVersionUID
19:48:19,531 INFO  [app] ctx field interceptor
19:48:19,531 INFO  [app] found interceptor io.smallrye.config.SecretKeysConfigSourceInterceptor@1dd7796b
19:48:19,531 INFO  [app] ctx field next
19:48:19,531 INFO  [app] ctx is io.smallrye.config.SmallRyeConfigSourceInterceptorContext@67a3bd51
19:48:19,531 INFO  [app] ctx field serialVersionUID
19:48:19,531 INFO  [app] ctx field interceptor
19:48:19,532 INFO  [app] found interceptor io.smallrye.config.SmallRyeConfigSources@57402ba1
19:48:19,532 INFO  [app] ctx field next
19:48:19,532 INFO  [app] ctx is io.smallrye.config.SmallRyeConfigSourceInterceptorContext@56913163
19:48:19,532 INFO  [app] ctx field serialVersionUID
19:48:19,532 INFO  [app] ctx field interceptor
19:48:19,532 INFO  [app] found interceptor io.smallrye.config.ConfigSourceInterceptor$1@702b06fb
19:48:19,532 INFO  [app] ctx field next
19:48:19,532 INFO  [app] config source: io.smallrye.config.ConfigSourceContext$ConfigSourceContextConfigSource
19:48:19,532 INFO  [app] config source: DefaultValuesConfigSource
.....

Do you need more? I'd expect no service binding to work, so if you can run some hello world you should be able to debug it yourself. If I can provide more info that you need, write me here or elsewhere, I'll try.

@radcortez
Copy link
Member

Thanks.

If you notice on your debug:

19:48:19,530 INFO  [app] ctx field interceptor
19:48:19,530 INFO  [app] found interceptor io.smallrye.config.ExpressionConfigSourceInterceptor@6d303498
19:48:19,530 INFO  [app] ctx field next

The interceptor is there, but apparently, the expression is not being evaluated. From the other log, it seems that the value set is /deployments/${SERVICE_BINDING_ROOT:}?

List<ServiceBinding> serviceBindings = getServiceBindings(kubernetesServiceBindingConfig.root().get());

static List<ServiceBinding> getServiceBindings(String bindingRoot) {
Path p = Paths.get(bindingRoot);
if (!Files.exists(p)) {
log.warn("Service Binding root '" + p.toAbsolutePath() + "' does not exist");
return emptyList();
}

It seems that something is setting the quarkus.kubernetes-service-binding.root to /deployments/${SERVICE_BINDING_ROOT:}? Is that correct?

@radcortez
Copy link
Member

One option that I see is if the value is being set with some sort of escaping to not perform the expression evaluation. Can you confirm the exact value being set?

@michalvavrik
Copy link
Member Author

One option that I see is if the value is being set with some sort of escaping to not perform the expression evaluation. Can you confirm the exact value being set?

I'll provide you with information you asked in above comments. And I absolutely understand why you don't want to fix it the way I suggested, but I'm not sure from your questions whether you understand the situation - my PR fixed the problem for me, hence if ExpressionConfigSourceInterceptor was truly applied, it should also fix the issue, right? Anyway, I'm not trying to fix it anymore, I'll try collect information for you later @radcortez

@michalvavrik
Copy link
Member Author

michalvavrik commented Dec 5, 2023

It seems that something is setting the quarkus.kubernetes-service-binding.root to /deployments/${SERVICE_BINDING_ROOT:}? Is that correct?

/deployments/ is not from property, but from Path

@michalvavrik
Copy link
Member Author

@radcortez I sent you full log by email as I don't want to spend long time removing sensitive information, but here is the gist:

10:55:19,753 INFO  [app] reflection for io.smallrye.config.SmallRyeConfigSourceContext@84487f4
10:55:19,753 INFO  [app] field is context
10:55:19,753 INFO  [app] ctx is io.smallrye.config.SmallRyeConfigSourceInterceptorContext@2dfe5525
10:55:19,753 INFO  [app] ctx field serialVersionUID
10:55:19,753 INFO  [app] ctx field interceptor
10:55:19,753 INFO  [app] found interceptor io.quarkus.kubernetes.runtime.config.KubernetesConfigFallback@1290c49
10:55:19,753 INFO  [app] ctx field next
10:55:19,753 INFO  [app] ctx is io.smallrye.config.SmallRyeConfigSourceInterceptorContext@6a9b9909
10:55:19,753 INFO  [app] ctx field serialVersionUID
10:55:19,753 INFO  [app] ctx field interceptor
10:55:19,753 INFO  [app] found interceptor io.smallrye.config.FallbackConfigSourceInterceptor@55d9b8f0
10:55:19,753 INFO  [app] ctx field next
10:55:19,754 INFO  [app] ctx is io.smallrye.config.SmallRyeConfigSourceInterceptorContext@a518813
10:55:19,754 INFO  [app] ctx field serialVersionUID
10:55:19,754 INFO  [app] ctx field interceptor
10:55:19,754 INFO  [app] found interceptor io.smallrye.config.SecretKeysHandlerConfigSourceInterceptor@43d38654
10:55:19,754 INFO  [app] ctx field next
10:55:19,754 INFO  [app] ctx is io.smallrye.config.SmallRyeConfigSourceInterceptorContext@75361cf6
10:55:19,754 INFO  [app] ctx field serialVersionUID
10:55:19,754 INFO  [app] ctx field interceptor
10:55:19,754 INFO  [app] found interceptor io.smallrye.config.ExpressionConfigSourceInterceptor@6d303498
10:55:19,754 INFO  [app] ctx field next
10:55:19,754 INFO  [app] ctx is io.smallrye.config.SmallRyeConfigSourceInterceptorContext@6ba7383d
10:55:19,754 INFO  [app] ctx field serialVersionUID
10:55:19,754 INFO  [app] ctx field interceptor
10:55:19,754 INFO  [app] found interceptor io.smallrye.config.LoggingConfigSourceInterceptor@3419e23b
10:55:19,754 INFO  [app] ctx field next
10:55:19,754 INFO  [app] ctx is io.smallrye.config.SmallRyeConfigSourceInterceptorContext@710d89e2
10:55:19,754 INFO  [app] ctx field serialVersionUID
10:55:19,754 INFO  [app] ctx field interceptor
10:55:19,754 INFO  [app] found interceptor io.smallrye.config.ProfileConfigSourceInterceptor@1d75e7af
10:55:19,754 INFO  [app] ctx field next
10:55:19,754 INFO  [app] ctx is io.smallrye.config.SmallRyeConfigSourceInterceptorContext@4fc142ec
10:55:19,754 INFO  [app] ctx field serialVersionUID
10:55:19,754 INFO  [app] ctx field interceptor
10:55:19,754 INFO  [app] found interceptor io.smallrye.config.RelocateConfigSourceInterceptor@34b27915
10:55:19,754 INFO  [app] ctx field next
10:55:19,755 INFO  [app] ctx is io.smallrye.config.SmallRyeConfigSourceInterceptorContext@29eda4f8
10:55:19,755 INFO  [app] ctx field serialVersionUID
10:55:19,755 INFO  [app] ctx field interceptor
10:55:19,755 INFO  [app] found interceptor io.smallrye.config.RelocateConfigSourceInterceptor@1b9776f5
10:55:19,755 INFO  [app] ctx field next
10:55:19,755 INFO  [app] ctx is io.smallrye.config.SmallRyeConfigSourceInterceptorContext@5e048149
10:55:19,755 INFO  [app] ctx field serialVersionUID
10:55:19,755 INFO  [app] ctx field interceptor
10:55:19,755 INFO  [app] found interceptor io.smallrye.config.RelocateConfigSourceInterceptor@79d9214d
10:55:19,755 INFO  [app] ctx field next
10:55:19,755 INFO  [app] ctx is io.smallrye.config.SmallRyeConfigSourceInterceptorContext@3d5790ea
10:55:19,755 INFO  [app] ctx field serialVersionUID
10:55:19,755 INFO  [app] ctx field interceptor
10:55:19,755 INFO  [app] found interceptor io.smallrye.config.SecretKeysConfigSourceInterceptor@1dd7796b
10:55:19,755 INFO  [app] ctx field next
10:55:19,755 INFO  [app] ctx is io.smallrye.config.SmallRyeConfigSourceInterceptorContext@67a3bd51
10:55:19,755 INFO  [app] ctx field serialVersionUID
10:55:19,755 INFO  [app] ctx field interceptor
10:55:19,755 INFO  [app] found interceptor io.smallrye.config.SmallRyeConfigSources@57402ba1
10:55:19,755 INFO  [app] ctx field next
10:55:19,755 INFO  [app] ctx is io.smallrye.config.SmallRyeConfigSourceInterceptorContext@56913163
10:55:19,755 INFO  [app] ctx field serialVersionUID
10:55:19,755 INFO  [app] ctx field interceptor
10:55:19,755 INFO  [app] found interceptor io.smallrye.config.ConfigSourceInterceptor$1@702b06fb
10:55:19,755 INFO  [app] ctx field next
10:55:19,755 INFO  [app] config source: io.smallrye.config.ConfigSourceContext$ConfigSourceContextConfigSource
10:55:19,755 INFO  [app] config source: DefaultValuesConfigSource
10:55:19,755 INFO  [app] quarkus.kubernetes-service-binding.root got directly (not mapping) from SR Config is ${SERVICE_BINDING_ROOT:}
10:55:19,755 INFO  [app] bindingRoot is Optional[${SERVICE_BINDING_ROOT:}]
10:55:19,755 INFO  [app] /////////// property keys and values
...
10:55:19,769 INFO  [app] Property name SERVICE_BINDING_ROOT property value /bindings
...
10:55:19,769 INFO  [app] Property name KUBERNETES_PORT_443_TCP_PORT property value 443
10:55:19,770 INFO  [app] Property name quarkus.management.domain-socket property value /var/run/io.quarkus.management.socket
10:55:19,770 INFO  [app] Property name quarkus.http.port property value 8080
10:55:19,770 INFO  [app] //////////// config sources property keys and values 
....
10:55:19,776 INFO  [app] ctx property key service.binding.root value ConfigValue{name='service.binding.root', value='/bindings', rawValue='/bindings', profile='null', configSourceName='EnvConfigSource', configSourceOrdinal=300, configSourcePosition=2, lineNumber=-1}
...
10:55:19,786 INFO  [app] ctx property key SERVICE_BINDING_ROOT value ConfigValue{name='SERVICE_BINDING_ROOT', value='/bindings', rawValue='/bindings', profile='null', configSourceName='EnvConfigSource', configSourceOrdinal=300, configSourcePosition=2, lineNumber=-1}
...
10:55:19,786 INFO  [app]  --/ __ \/ / / / _ | / _ \/ //_/ / / / __/ 
10:55:19,787 INFO  [app]  -/ /_/ / /_/ / __ |/ , _/ ,< / /_/ /\ \   
10:55:19,787 INFO  [app] --\___\_\____/_/ |_/_/|_/_/|_|\____/___/   
10:55:19,787 INFO  [app] 09:55:15,112 Service Binding root '/deployments/${SERVICE_BINDING_ROOT:}' does not exist
10:55:19,787 INFO  [app] Model classes are defined for the default persistence unit <default> but configured datasource <default> not found: the default EntityManagerFactory will not be created. To solve this, configure the default datasource. Refer to https://quarkus.io/guides/datasource for guidance.

for code

diff --git a/extensions/kubernetes-service-binding/runtime/src/main/java/io/quarkus/kubernetes/service/binding/runtime/KubernetesServiceBindingConfigSourceFactory.java b/extensions/kubernetes-service-binding/runtime/src/main/java/io/quarkus/kubernetes/service/binding/runtime/KubernetesServiceBindingConfigSourceFactory.java
index df605dc3633..e2742933840 100644
--- a/extensions/kubernetes-service-binding/runtime/src/main/java/io/quarkus/kubernetes/service/binding/runtime/KubernetesServiceBindingConfigSourceFactory.java
+++ b/extensions/kubernetes-service-binding/runtime/src/main/java/io/quarkus/kubernetes/service/binding/runtime/KubernetesServiceBindingConfigSourceFactory.java
@@ -3,6 +3,7 @@
 import static java.util.Collections.emptyList;
 
 import java.io.File;
+import java.lang.reflect.Field;
 import java.nio.file.Files;
 import java.nio.file.Path;
 import java.nio.file.Paths;
@@ -19,6 +20,7 @@
 
 import io.smallrye.config.ConfigSourceContext;
 import io.smallrye.config.ConfigSourceFactory;
+import io.smallrye.config.ConfigSourceInterceptor;
 import io.smallrye.config.SmallRyeConfig;
 import io.smallrye.config.SmallRyeConfigBuilder;
 
@@ -34,9 +36,60 @@ public Iterable<ConfigSource> getConfigSources(final ConfigSourceContext context
                 .withMappingIgnore("quarkus.**")
                 .build();
 
+        System.out.println("reflection for " + context);
+        for (Field declaredField : context.getClass().getDeclaredFields()) {
+            System.out.println("field is " + declaredField.getName());
+            if (declaredField.getName().equals("context")) {
+                declaredField.setAccessible(true);
+                try {
+                    var ctx = declaredField.get(context);
+                    ctx: while (ctx != null) {
+                        System.out.println("ctx is " + ctx);
+                        for (Field field : ctx.getClass().getDeclaredFields()) {
+                            System.out.println("ctx field " + field.getName());
+                            if (field.getName().equals("interceptor")) {
+                                field.setAccessible(true);
+                                ConfigSourceInterceptor interceptor = (ConfigSourceInterceptor) field.get(ctx);
+                                System.out.println("found interceptor " + interceptor);
+                            }
+                            if (field.getName().equals("next")) {
+                                field.setAccessible(true);
+                                var ctx1 = field.get(ctx);
+                                if (ctx1 == ctx) {
+                                    break ctx;
+                                }
+                                ctx = ctx1;
+                                continue ctx;
+                            }
+                        }
+                        ctx = null;
+                    }
+                } catch (IllegalAccessException e) {
+                    throw new RuntimeException(e);
+                }
+                break;
+            }
+        }
+
+        config.getConfigSources().forEach(cs -> System.out.println("config source: " + cs.getName()));
+
+        String val = config.getConfigValue("quarkus.kubernetes-service-binding.root").getValue();
+        System.out.println("quarkus.kubernetes-service-binding.root got directly (not mapping) from SR Config is " + val);
+
         KubernetesServiceBindingConfig kubernetesServiceBindingConfig = config
                 .getConfigMapping(KubernetesServiceBindingConfig.class);
 
+        System.out.println("bindingRoot is " + kubernetesServiceBindingConfig.root());
+
+        System.out.println("/////////// property keys and values");
+        config.getPropertyNames().forEach(
+                key -> System.out.println("Property name " + key + " property value "
+                        + config.getOptionalValue(key, String.class).orElse("<<no-value>>")));
+
+        System.out.println("//////////// config sources property keys and values ");
+        context.iterateNames()
+                .forEachRemaining(key -> System.out.println("ctx property key " + key + " value " + context.getValue(key)));
+
         if (!kubernetesServiceBindingConfig.enabled()) {
             log.debug(
                     "No attempt will be made to bind configuration based on Kubernetes ServiceBinding because the feature was not enabled.");

@michalvavrik
Copy link
Member Author

10:53:37,801 INFO [app] Property name quarkus.kubernetes-service-binding.root property value ${SERVICE_BINDING_ROOT:}

@michalvavrik
Copy link
Member Author

michalvavrik commented Dec 5, 2023

quarkus.kubernetes-service-binding.root is not in the context directly, which explains why it is not evaluated, no?
I think that is different to https://github.com/smallrye/smallrye-config/blob/56f6e276bddacade18fa46e57b7440c1c73ee0ee/implementation/src/test/java/io/smallrye/config/ConfigSourceFactoryTest.java#L51

@radcortez
Copy link
Member

I'll provide you with information you asked in above comments. And I absolutely understand why you don't want to fix it the way I suggested, but I'm not sure from your questions whether you understand the situation - my PR fixed the problem for me, hence if ExpressionConfigSourceInterceptor was truly applied, it should also fix the issue, right?

Maybe we would need to apply your fix, but my point was that there is already an ExpressionConfigSourceInterceptor in the context which I would expect to expand the expression. If there is a case where the context is not expanding an expression, then we need to fix it at the context level.

@michalvavrik
Copy link
Member Author

Maybe we would need to apply your fix, but my point was that there is already an ExpressionConfigSourceInterceptor in the context which I would expect to expand the expression. If there is a case where the context is not expanding an expression, then we need to fix it at the context level.

I agree. Maybe I wrote it incorrectly @radcortez , I tried to stress it fixes the issue as it is valuable information.

@radcortez
Copy link
Member

Ok, the issue here is that the mapping defaults are not being propagated to the context and are on a separate chain, meaning that the values are still populated, but the chain containing the expression resolution is not applied.

@radcortez radcortez self-assigned this Dec 5, 2023
@radcortez
Copy link
Member

This will fix it: smallrye/smallrye-config#1060

For now, please set quarkus.kubernetes-service-binding.root directly, either with the expected value or the expansion expression as quarkus.kubernetes-service-binding.root=${SERVICE_BINDING_ROOT:}.

@michalvavrik
Copy link
Member Author

Alright, thank you.

@quarkus-bot quarkus-bot bot added this to the 3.7 - main milestone Jan 3, 2024
michalvavrik added a commit to michalvavrik/quarkus-test-suite that referenced this issue Jan 8, 2024
Enables PG SB test as quarkusio/quarkus#37461 is fixed, run it against 4.14 from my workstation and works. Ports quarkus-qe#1547 as the test fails without it.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment