Skip to content

Commit 2c38360

Browse files
Support file-base VCAP_SERVICES bindings (#363)
* Support file-base VCAP_SERVICES bindings This change adds support for file-based VCAP services as documented in [1]. The extension will check first if the configuration file set in VCAP_SERVICES_FILE_PATH can be read. If not it will fall-back to the content of VCAP_SERVICES which is the old behaviour. [1] https://docs.cloudfoundry.org/devguide/services/application-binding.html#file-based-vcap-services Signed-off-by: Karsten Schnitter <k.schnitter@sap.com> * Fix variable name for VCAP services file path Co-authored-by: @bennygoerzig --------- Signed-off-by: Karsten Schnitter <k.schnitter@sap.com>
1 parent 75311c5 commit 2c38360

File tree

7 files changed

+155
-31
lines changed

7 files changed

+155
-31
lines changed

cf-java-logging-support-opentelemetry-agent-extension/src/main/java/com/sap/hcf/cf/logging/opentelemetry/agent/ext/binding/CaasServiceProvider.java

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -11,7 +11,7 @@ public class CaasServiceProvider implements Supplier<CloudFoundryServiceInstance
1111
private final CloudFoundryServiceInstance service;
1212

1313
public CaasServiceProvider(ConfigProperties config) {
14-
this(config, new CloudFoundryServicesAdapter());
14+
this(config, CloudFoundryServicesAdapter.builder().build());
1515
}
1616

1717
CaasServiceProvider(ConfigProperties config, CloudFoundryServicesAdapter adapter) {

cf-java-logging-support-opentelemetry-agent-extension/src/main/java/com/sap/hcf/cf/logging/opentelemetry/agent/ext/binding/CloudFoundryServicesAdapter.java

Lines changed: 46 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -6,10 +6,15 @@
66
import com.fasterxml.jackson.core.JsonToken;
77

88
import java.io.IOException;
9+
import java.nio.charset.StandardCharsets;
10+
import java.nio.file.Files;
11+
import java.nio.file.Path;
12+
import java.nio.file.Paths;
913
import java.util.ArrayList;
1014
import java.util.Comparator;
1115
import java.util.List;
1216
import java.util.function.Consumer;
17+
import java.util.function.Function;
1318
import java.util.logging.Logger;
1419
import java.util.stream.Stream;
1520

@@ -18,6 +23,7 @@ class CloudFoundryServicesAdapter {
1823
private static final Logger LOG = Logger.getLogger(CloudFoundryServicesAdapter.class.getName());
1924

2025
private static final String VCAP_SERVICES = "VCAP_SERVICES";
26+
private static final String VCAP_SERVICES_FILE_PATH = "VCAP_SERVICES_FILE_PATH";
2127
private static final String SERVICE_NAME = "name";
2228
private static final String SERVICE_TAGS = "tags";
2329
private static final String SERVICE_CREDENTIALS = "credentials";
@@ -165,4 +171,44 @@ private boolean hasServiceTag(List<String> requiredTags, List<String> instanceTa
165171
private Comparator<CloudFoundryServiceInstance> byLabels(List<String> serviceLabels) {
166172
return (l, r) -> getIndex(serviceLabels, l.getLabel()) - getIndex(serviceLabels, r.getLabel());
167173
}
174+
175+
static Builder builder() {
176+
return builder(System::getenv);
177+
}
178+
179+
static Builder builder(Function<String, String> envSupplier) {
180+
return new Builder(envSupplier);
181+
}
182+
183+
static class Builder {
184+
185+
private final Function<String, String> envSupplier;
186+
private String vcapServicesFilePathKey = VCAP_SERVICES_FILE_PATH;
187+
188+
private Builder(Function<String, String> envSupplier) {
189+
this.envSupplier = envSupplier;
190+
}
191+
192+
Builder withVcapServicesFilePathKey(String vcapServicesFilePathKey) {
193+
LOG.fine("Using environment variable " + vcapServicesFilePathKey + " to read VCAP services from file.");
194+
this.vcapServicesFilePathKey = vcapServicesFilePathKey;
195+
return this;
196+
}
197+
198+
CloudFoundryServicesAdapter build() {
199+
String vcapServicesFilePath = envSupplier.apply(vcapServicesFilePathKey);
200+
if (vcapServicesFilePath != null) {
201+
try {
202+
Path path = Paths.get(vcapServicesFilePath);
203+
String vcapServicesJson = Files.readString(path, StandardCharsets.UTF_8);
204+
LOG.fine("Successfully read VCAP services from file " + vcapServicesFilePath);
205+
return new CloudFoundryServicesAdapter(vcapServicesJson);
206+
} catch (IOException cause) {
207+
LOG.warning(
208+
"Cannot read VCAP services from file \"" + vcapServicesFilePath + "\". Falling back to environment variable " + VCAP_SERVICES);
209+
}
210+
}
211+
return new CloudFoundryServicesAdapter(envSupplier.apply(VCAP_SERVICES));
212+
}
213+
}
168214
}

cf-java-logging-support-opentelemetry-agent-extension/src/main/java/com/sap/hcf/cf/logging/opentelemetry/agent/ext/binding/CloudLoggingBindingPropertiesSupplier.java

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -27,7 +27,7 @@ public class CloudLoggingBindingPropertiesSupplier implements Supplier<Map<Strin
2727
private final PemFileCreator pemFileCreator;
2828

2929
public CloudLoggingBindingPropertiesSupplier() {
30-
this(new CloudLoggingServicesProvider(getDefaultProperties(), new CloudFoundryServicesAdapter()),
30+
this(new CloudLoggingServicesProvider(getDefaultProperties(), CloudFoundryServicesAdapter.builder().build()),
3131
new PemFileCreator());
3232
}
3333

cf-java-logging-support-opentelemetry-agent-extension/src/main/java/com/sap/hcf/cf/logging/opentelemetry/agent/ext/binding/CloudLoggingServicesProvider.java

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -15,7 +15,7 @@ public class CloudLoggingServicesProvider implements Supplier<Stream<CloudFoundr
1515
private final List<CloudFoundryServiceInstance> services;
1616

1717
public CloudLoggingServicesProvider(ConfigProperties config) {
18-
this(config, new CloudFoundryServicesAdapter());
18+
this(config, CloudFoundryServicesAdapter.builder().build());
1919
}
2020

2121
CloudLoggingServicesProvider(ConfigProperties config, CloudFoundryServicesAdapter adapter) {

cf-java-logging-support-opentelemetry-agent-extension/src/main/java/com/sap/hcf/cf/logging/opentelemetry/agent/ext/binding/DynatraceServiceProvider.java

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -12,7 +12,7 @@ public class DynatraceServiceProvider implements Supplier<CloudFoundryServiceIns
1212
private final CloudFoundryServiceInstance service;
1313

1414
public DynatraceServiceProvider(ConfigProperties config) {
15-
this(config, new CloudFoundryServicesAdapter());
15+
this(config, CloudFoundryServicesAdapter.builder().build());
1616
}
1717

1818
DynatraceServiceProvider(ConfigProperties config, CloudFoundryServicesAdapter adapter) {

cf-java-logging-support-opentelemetry-agent-extension/src/test/java/com/sap/hcf/cf/logging/opentelemetry/agent/ext/binding/CloudFoundryServicesAdapterTest.java

Lines changed: 41 additions & 27 deletions
Original file line numberDiff line numberDiff line change
@@ -2,9 +2,12 @@
22

33
import org.assertj.core.api.AbstractListAssert;
44
import org.assertj.core.api.ObjectAssert;
5-
import org.junit.jupiter.api.Test;
5+
import org.junit.jupiter.params.ParameterizedTest;
6+
import org.junit.jupiter.params.provider.MethodSource;
67

8+
import java.net.URISyntaxException;
79
import java.util.List;
10+
import java.util.stream.Stream;
811

912
import static java.util.Collections.emptyList;
1013
import static java.util.stream.Collectors.toList;
@@ -60,54 +63,65 @@ public class CloudFoundryServicesAdapterTest {
6063
" ]\n" + //
6164
"}";
6265

63-
private static final CloudFoundryServicesAdapter DEFAULT_ADAPTER =
64-
new CloudFoundryServicesAdapter(DEFAULT_VCAP_SERVICES);
66+
static Stream<CloudFoundryServicesAdapter> adapters() {
67+
return Stream.of(new CloudFoundryServicesAdapter(DEFAULT_VCAP_SERVICES), createFileAdapter());
68+
}
69+
70+
private static CloudFoundryServicesAdapter createFileAdapter() {
71+
try {
72+
String filePath = CloudFoundryServicesAdapterTest.class.getResource("vcap_services.json").toURI().getPath();
73+
return CloudFoundryServicesAdapter.builder(key -> filePath).build();
74+
} catch (URISyntaxException e) {
75+
throw new RuntimeException("Failed to load vcap_services.json", e);
76+
}
77+
}
6578

66-
@Test
67-
void getsAllServicesWithNullParameters() {
68-
List<CloudFoundryServiceInstance> services = DEFAULT_ADAPTER.stream(null, null).collect(toList());
79+
@ParameterizedTest
80+
@MethodSource("adapters")
81+
void getsAllServicesWithNullParameters(CloudFoundryServicesAdapter adapter) {
82+
List<CloudFoundryServiceInstance> services = adapter.stream(null, null).collect(toList());
6983
assertServiceNames(services).containsExactly("managed-find-me1", "managed-find-me2", "managed-other",
7084
"managed-other1", "managed-other2", "ups-find-me1", "ups-find-me2",
7185
"ups-other");
72-
73-
}
74-
75-
private static AbstractListAssert<?, List<? extends String>, String, ObjectAssert<String>> assertServiceNames(
76-
List<CloudFoundryServiceInstance> services) {
77-
return assertThat(services).extracting(CloudFoundryServiceInstance::getName);
7886
}
7987

80-
@Test
81-
void filtersBySingleLabel() {
88+
@ParameterizedTest
89+
@MethodSource("adapters")
90+
void filtersBySingleLabel(CloudFoundryServicesAdapter adapter) {
8291
List<CloudFoundryServiceInstance> services =
83-
DEFAULT_ADAPTER.stream(List.of("managed-find-me-service"), emptyList()).collect(toList());
92+
adapter.stream(List.of("managed-find-me-service"), emptyList()).collect(toList());
8493
assertServiceNames(services).containsExactlyInAnyOrder("managed-find-me1", "managed-find-me2", "managed-other");
8594
}
8695

87-
@Test
88-
void priotizesByServiceLabel() {
96+
@ParameterizedTest
97+
@MethodSource("adapters")
98+
void priotizesByServiceLabel(CloudFoundryServicesAdapter adapter) {
8999
List<CloudFoundryServiceInstance> services =
90-
DEFAULT_ADAPTER.stream(List.of("user-provided", "managed-find-me-service"), emptyList())
91-
.collect(toList());
100+
adapter.stream(List.of("user-provided", "managed-find-me-service"), emptyList()).collect(toList());
92101
assertServiceNames(services).containsExactly("ups-find-me1", "ups-find-me2", "ups-other", "managed-find-me1",
93102
"managed-find-me2", "managed-other");
94103
}
95104

96-
@Test
97-
void filtersBySingleTag() {
98-
List<CloudFoundryServiceInstance> services =
99-
DEFAULT_ADAPTER.stream(emptyList(), List.of("Find Me!")).collect(toList());
105+
@ParameterizedTest
106+
@MethodSource("adapters")
107+
void filtersBySingleTag(CloudFoundryServicesAdapter adapter) {
108+
List<CloudFoundryServiceInstance> services = adapter.stream(emptyList(), List.of("Find Me!")).collect(toList());
100109
assertServiceNames(services).containsExactlyInAnyOrder("managed-find-me1", "managed-find-me2", "managed-other1",
101110
"ups-find-me1", "ups-find-me2");
102111
}
103112

104-
@Test
105-
void standardUseCase() {
113+
@ParameterizedTest
114+
@MethodSource("adapters")
115+
void standardUseCase(CloudFoundryServicesAdapter adapter) {
106116
List<CloudFoundryServiceInstance> services =
107-
DEFAULT_ADAPTER.stream(List.of("user-provided", "managed-find-me-service"), List.of("Find Me!"))
108-
.collect(toList());
117+
adapter.stream(List.of("user-provided", "managed-find-me-service"), List.of("Find Me!"))
118+
.collect(toList());
109119
assertServiceNames(services).containsExactly("ups-find-me1", "ups-find-me2", "managed-find-me1",
110120
"managed-find-me2");
111121
}
112122

123+
private static AbstractListAssert<?, List<? extends String>, String, ObjectAssert<String>> assertServiceNames(
124+
List<CloudFoundryServiceInstance> services) {
125+
return assertThat(services).extracting(CloudFoundryServiceInstance::getName);
126+
}
113127
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,64 @@
1+
{
2+
"managed-find-me-service": [
3+
{
4+
"label": "managed-find-me-service",
5+
"tags": [
6+
"Find Me!"
7+
],
8+
"name": "managed-find-me1"
9+
},
10+
{
11+
"label": "managed-find-me-service",
12+
"tags": [
13+
"Find Me!"
14+
],
15+
"name": "managed-find-me2"
16+
},
17+
{
18+
"label": "managed-find-me-service",
19+
"tags": [
20+
"You can't see me!"
21+
],
22+
"name": "managed-other"
23+
}
24+
],
25+
"managed-notice-me-not-service": [
26+
{
27+
"label": "managed-notice-me-not-service",
28+
"tags": [
29+
"Find Me!"
30+
],
31+
"name": "managed-other1"
32+
},
33+
{
34+
"label": "managed-notice-me-not-service",
35+
"tags": [
36+
"You can't see me!"
37+
],
38+
"name": "managed-other2"
39+
}
40+
],
41+
"user-provided": [
42+
{
43+
"label": "user-provided",
44+
"tags": [
45+
"Find Me!"
46+
],
47+
"name": "ups-find-me1"
48+
},
49+
{
50+
"label": "user-provided",
51+
"tags": [
52+
"Find Me!"
53+
],
54+
"name": "ups-find-me2"
55+
},
56+
{
57+
"label": "user-provided",
58+
"tags": [
59+
"You can't see me!"
60+
],
61+
"name": "ups-other"
62+
}
63+
]
64+
}

0 commit comments

Comments
 (0)