Skip to content

Commit b1196d3

Browse files
artur-ciocanuArtur Ciocanu
andauthored
Adding Dapr configuration support to Testcontainers (#1148)
* Adding Dapr configuration support to Testcontainers Signed-off-by: Artur Ciocanu <ciocanu@adobe.com> * Fixing a small styling issue Signed-off-by: Artur Ciocanu <ciocanu@adobe.com> * Fixing Dapr Testcontainer Component test Signed-off-by: Artur Ciocanu <ciocanu@adobe.com> * Fix Dapr Testcontainer Subscription test Signed-off-by: Artur Ciocanu <ciocanu@adobe.com> * Fix Darp Testcontainer Configuration test Signed-off-by: Artur Ciocanu <ciocanu@adobe.com> * Fix expected YAML structure Signed-off-by: Artur Ciocanu <ciocanu@adobe.com> * Fix Configuration test Signed-off-by: Artur Ciocanu <ciocanu@adobe.com> * Fix Dapr Component test Signed-off-by: Artur Ciocanu <ciocanu@adobe.com> --------- Signed-off-by: Artur Ciocanu <ciocanu@adobe.com> Co-authored-by: Artur Ciocanu <ciocanu@adobe.com>
1 parent 7dcab0b commit b1196d3

15 files changed

+549
-167
lines changed
Lines changed: 45 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,45 @@
1+
/*
2+
* Copyright 2024 The Dapr Authors
3+
* Licensed under the Apache License, Version 2.0 (the "License");
4+
* you may not use this file except in compliance with the License.
5+
* You may obtain a copy of the License at
6+
* http://www.apache.org/licenses/LICENSE-2.0
7+
* Unless required by applicable law or agreed to in writing, software
8+
* distributed under the License is distributed on an "AS IS" BASIS,
9+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
10+
* See the License for the specific language governing permissions and
11+
limitations under the License.
12+
*/
13+
14+
package io.dapr.testcontainers;
15+
16+
/**
17+
* Represents a Dapr component.
18+
*/
19+
public class Configuration {
20+
private final String name;
21+
private final TracingConfigurationSettings tracing;
22+
23+
//@TODO: add httpPipeline
24+
//@TODO: add secrets
25+
//@TODO: add components
26+
//@TODO: add accessControl
27+
28+
/**
29+
* Creates a new configuration.
30+
* @param name Configuration name.
31+
* @param tracing TracingConfigParameters tracing configuration parameters.
32+
*/
33+
public Configuration(String name, TracingConfigurationSettings tracing) {
34+
this.name = name;
35+
this.tracing = tracing;
36+
}
37+
38+
public String getName() {
39+
return name;
40+
}
41+
42+
public TracingConfigurationSettings getTracing() {
43+
return tracing;
44+
}
45+
}
Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,6 @@
1+
package io.dapr.testcontainers;
2+
3+
// This is a marker interface, so we could get
4+
// a list of all the configuration settings implementations
5+
public interface ConfigurationSettings {
6+
}

testcontainers-dapr/src/main/java/io/dapr/testcontainers/DaprContainer.java

Lines changed: 56 additions & 105 deletions
Original file line numberDiff line numberDiff line change
@@ -13,49 +13,54 @@
1313

1414
package io.dapr.testcontainers;
1515

16+
import io.dapr.testcontainers.converter.ComponentYamlConverter;
17+
import io.dapr.testcontainers.converter.ConfigurationYamlConverter;
18+
import io.dapr.testcontainers.converter.SubscriptionYamlConverter;
19+
import io.dapr.testcontainers.converter.YamlConverter;
20+
import io.dapr.testcontainers.converter.YamlMapperFactory;
1621
import org.testcontainers.containers.GenericContainer;
1722
import org.testcontainers.containers.Network;
1823
import org.testcontainers.containers.wait.strategy.Wait;
1924
import org.testcontainers.containers.wait.strategy.WaitStrategy;
2025
import org.testcontainers.images.builder.Transferable;
2126
import org.testcontainers.utility.DockerImageName;
22-
import org.yaml.snakeyaml.DumperOptions;
2327
import org.yaml.snakeyaml.Yaml;
24-
import org.yaml.snakeyaml.nodes.Tag;
25-
import org.yaml.snakeyaml.representer.Representer;
2628

2729
import java.io.IOException;
2830
import java.nio.file.Files;
2931
import java.nio.file.Path;
3032
import java.util.ArrayList;
3133
import java.util.Collections;
32-
import java.util.HashMap;
3334
import java.util.HashSet;
34-
import java.util.LinkedHashMap;
3535
import java.util.List;
3636
import java.util.Map;
3737
import java.util.Set;
3838

3939
public class DaprContainer extends GenericContainer<DaprContainer> {
40-
4140
private static final int DAPRD_DEFAULT_HTTP_PORT = 3500;
4241
private static final int DAPRD_DEFAULT_GRPC_PORT = 50001;
42+
private static final DaprProtocol DAPR_PROTOCOL = DaprProtocol.HTTP;
43+
private static final DockerImageName DEFAULT_IMAGE_NAME = DockerImageName.parse("daprio/daprd");
44+
private static final Yaml YAML_MAPPER = YamlMapperFactory.create();
45+
private static final YamlConverter<Component> COMPONENT_CONVERTER = new ComponentYamlConverter(YAML_MAPPER);
46+
private static final YamlConverter<Subscription> SUBSCRIPTION_CONVERTER = new SubscriptionYamlConverter(YAML_MAPPER);
47+
private static final YamlConverter<Configuration> CONFIGURATION_CONVERTER = new ConfigurationYamlConverter(
48+
YAML_MAPPER);
4349
private static final WaitStrategy WAIT_STRATEGY = Wait.forHttp("/v1.0/healthz/outbound")
4450
.forPort(DAPRD_DEFAULT_HTTP_PORT)
4551
.forStatusCodeMatching(statusCode -> statusCode >= 200 && statusCode <= 399);
4652

4753
private final Set<Component> components = new HashSet<>();
4854
private final Set<Subscription> subscriptions = new HashSet<>();
49-
private DaprProtocol protocol = DaprProtocol.HTTP;
50-
private String appName;
51-
private Integer appPort = null;
5255
private DaprLogLevel daprLogLevel = DaprLogLevel.INFO;
5356
private String appChannelAddress = "localhost";
5457
private String placementService = "placement";
55-
private static final DockerImageName DEFAULT_IMAGE_NAME = DockerImageName.parse("daprio/daprd");
56-
private static final Yaml yaml = getYamlMapper();
57-
private DaprPlacementContainer placementContainer;
5858
private String placementDockerImageName = "daprio/placement";
59+
60+
private Configuration configuration;
61+
private DaprPlacementContainer placementContainer;
62+
private String appName;
63+
private Integer appPort;
5964
private boolean shouldReusePlacement;
6065

6166
/**
@@ -78,6 +83,10 @@ public DaprContainer(String image) {
7883
this(DockerImageName.parse(image));
7984
}
8085

86+
public Configuration getConfiguration() {
87+
return configuration;
88+
}
89+
8190
public Set<Component> getComponents() {
8291
return components;
8392
}
@@ -91,6 +100,16 @@ public DaprContainer withAppPort(Integer port) {
91100
return this;
92101
}
93102

103+
public DaprContainer withAppChannelAddress(String appChannelAddress) {
104+
this.appChannelAddress = appChannelAddress;
105+
return this;
106+
}
107+
108+
public DaprContainer withConfiguration(Configuration configuration) {
109+
this.configuration = configuration;
110+
return this;
111+
}
112+
94113
public DaprContainer withPlacementService(String placementService) {
95114
this.placementService = placementService;
96115
return this;
@@ -111,6 +130,21 @@ public DaprContainer withSubscription(Subscription subscription) {
111130
return this;
112131
}
113132

133+
public DaprContainer withPlacementImage(String placementDockerImageName) {
134+
this.placementDockerImageName = placementDockerImageName;
135+
return this;
136+
}
137+
138+
public DaprContainer withReusablePlacement(boolean reuse) {
139+
this.shouldReusePlacement = reuse;
140+
return this;
141+
}
142+
143+
public DaprContainer withPlacementContainer(DaprPlacementContainer placementContainer) {
144+
this.placementContainer = placementContainer;
145+
return this;
146+
}
147+
114148
public DaprContainer withComponent(Component component) {
115149
components.add(component);
116150
return this;
@@ -123,7 +157,7 @@ public DaprContainer withComponent(Component component) {
123157
*/
124158
public DaprContainer withComponent(Path path) {
125159
try {
126-
Map<String, Object> component = this.yaml.loadAs(Files.newInputStream(path), Map.class);
160+
Map<String, Object> component = this.YAML_MAPPER.loadAs(Files.newInputStream(path), Map.class);
127161

128162
String type = (String) component.get("type");
129163
Map<String, Object> metadata = (Map<String, Object>) component.get("metadata");
@@ -165,60 +199,6 @@ public String getGrpcEndpoint() {
165199
return ":" + getMappedPort(DAPRD_DEFAULT_GRPC_PORT);
166200
}
167201

168-
public DaprContainer withAppChannelAddress(String appChannelAddress) {
169-
this.appChannelAddress = appChannelAddress;
170-
return this;
171-
}
172-
173-
/**
174-
* Get a map of Dapr component details.
175-
* @param component A Dapr Component.
176-
* @return Map of component details.
177-
*/
178-
public Map<String, Object> componentToMap(Component component) {
179-
Map<String, Object> componentProps = new HashMap<>();
180-
componentProps.put("apiVersion", "dapr.io/v1alpha1");
181-
componentProps.put("kind", "Component");
182-
183-
Map<String, String> componentMetadata = new LinkedHashMap<>();
184-
componentMetadata.put("name", component.getName());
185-
componentProps.put("metadata", componentMetadata);
186-
187-
Map<String, Object> componentSpec = new HashMap<>();
188-
componentSpec.put("type", component.getType());
189-
componentSpec.put("version", component.getVersion());
190-
191-
if (!component.getMetadata().isEmpty()) {
192-
componentSpec.put("metadata", component.getMetadata());
193-
}
194-
195-
componentProps.put("spec", componentSpec);
196-
return Collections.unmodifiableMap(componentProps);
197-
}
198-
199-
/**
200-
* Get a map of Dapr subscription details.
201-
* @param subscription A Dapr Subscription.
202-
* @return Map of subscription details.
203-
*/
204-
public Map<String, Object> subscriptionToMap(Subscription subscription) {
205-
Map<String, Object> subscriptionProps = new HashMap<>();
206-
subscriptionProps.put("apiVersion", "dapr.io/v1alpha1");
207-
subscriptionProps.put("kind", "Subscription");
208-
209-
Map<String, String> subscriptionMetadata = new LinkedHashMap<>();
210-
subscriptionMetadata.put("name", subscription.getName());
211-
subscriptionProps.put("metadata", subscriptionMetadata);
212-
213-
Map<String, Object> subscriptionSpec = new HashMap<>();
214-
subscriptionSpec.put("pubsubname", subscription.getPubsubName());
215-
subscriptionSpec.put("topic", subscription.getTopic());
216-
subscriptionSpec.put("route", subscription.getRoute());
217-
218-
subscriptionProps.put("spec", subscriptionSpec);
219-
return Collections.unmodifiableMap(subscriptionProps);
220-
}
221-
222202
@Override
223203
protected void configure() {
224204
super.configure();
@@ -241,7 +221,7 @@ protected void configure() {
241221
cmds.add(appName);
242222
cmds.add("--dapr-listen-addresses=0.0.0.0");
243223
cmds.add("--app-protocol");
244-
cmds.add(protocol.getName());
224+
cmds.add(DAPR_PROTOCOL.getName());
245225
cmds.add("-placement-host-address");
246226
cmds.add(placementService + ":50005");
247227

@@ -257,10 +237,15 @@ protected void configure() {
257237

258238
cmds.add("--log-level");
259239
cmds.add(daprLogLevel.toString());
260-
cmds.add("-components-path");
240+
cmds.add("--resources-path");
261241
cmds.add("/dapr-resources");
262242
withCommand(cmds.toArray(new String[]{}));
263243

244+
if (configuration != null) {
245+
String configurationYaml = CONFIGURATION_CONVERTER.convert(configuration);
246+
withCopyToContainer(Transferable.of(configurationYaml), "/dapr-resources/" + configuration.getName() + ".yaml");
247+
}
248+
264249
if (components.isEmpty()) {
265250
components.add(new Component("kvstore", "state.in-memory", "v1", Collections.emptyMap()));
266251
components.add(new Component("pubsub", "pubsub.in-memory", "v1", Collections.emptyMap()));
@@ -271,28 +256,18 @@ protected void configure() {
271256
}
272257

273258
for (Component component : components) {
274-
String componentYaml = componentToYaml(component);
259+
String componentYaml = COMPONENT_CONVERTER.convert(component);
275260
withCopyToContainer(Transferable.of(componentYaml), "/dapr-resources/" + component.getName() + ".yaml");
276261
}
277262

278263
for (Subscription subscription : subscriptions) {
279-
String subscriptionYaml = subscriptionToYaml(subscription);
264+
String subscriptionYaml = SUBSCRIPTION_CONVERTER.convert(subscription);
280265
withCopyToContainer(Transferable.of(subscriptionYaml), "/dapr-resources/" + subscription.getName() + ".yaml");
281266
}
282267

283268
dependsOn(placementContainer);
284269
}
285270

286-
public String subscriptionToYaml(Subscription subscription) {
287-
Map<String, Object> subscriptionMap = subscriptionToMap(subscription);
288-
return yaml.dumpAsMap(subscriptionMap);
289-
}
290-
291-
public String componentToYaml(Component component) {
292-
Map<String, Object> componentMap = componentToMap(component);
293-
return yaml.dumpAsMap(componentMap);
294-
}
295-
296271
public String getAppName() {
297272
return appName;
298273
}
@@ -313,21 +288,6 @@ public static DockerImageName getDefaultImageName() {
313288
return DEFAULT_IMAGE_NAME;
314289
}
315290

316-
public DaprContainer withPlacementImage(String placementDockerImageName) {
317-
this.placementDockerImageName = placementDockerImageName;
318-
return this;
319-
}
320-
321-
public DaprContainer withReusablePlacement(boolean reuse) {
322-
this.shouldReusePlacement = reuse;
323-
return this;
324-
}
325-
326-
public DaprContainer withPlacementContainer(DaprPlacementContainer placementContainer) {
327-
this.placementContainer = placementContainer;
328-
return this;
329-
}
330-
331291
// Required by spotbugs plugin
332292
@Override
333293
public boolean equals(Object o) {
@@ -338,13 +298,4 @@ public boolean equals(Object o) {
338298
public int hashCode() {
339299
return super.hashCode();
340300
}
341-
342-
private static Yaml getYamlMapper() {
343-
DumperOptions options = new DumperOptions();
344-
options.setDefaultFlowStyle(DumperOptions.FlowStyle.BLOCK);
345-
options.setPrettyFlow(true);
346-
Representer representer = new Representer(options);
347-
representer.addClassTag(MetadataEntry.class, Tag.MAP);
348-
return new Yaml(representer);
349-
}
350301
}
Lines changed: 34 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,34 @@
1+
package io.dapr.testcontainers;
2+
3+
/**
4+
* Configuration settings for Otel tracing.
5+
*/
6+
public class OtelTracingConfigurationSettings implements ConfigurationSettings {
7+
private final String endpointAddress;
8+
private final Boolean isSecure;
9+
private final String protocol;
10+
11+
/**
12+
* Creates a new configuration.
13+
* @param endpointAddress tracing endpoint address
14+
* @param isSecure if the endpoint is secure
15+
* @param protocol tracing protocol
16+
*/
17+
public OtelTracingConfigurationSettings(String endpointAddress, Boolean isSecure, String protocol) {
18+
this.endpointAddress = endpointAddress;
19+
this.isSecure = isSecure;
20+
this.protocol = protocol;
21+
}
22+
23+
public String getEndpointAddress() {
24+
return endpointAddress;
25+
}
26+
27+
public Boolean getSecure() {
28+
return isSecure;
29+
}
30+
31+
public String getProtocol() {
32+
return protocol;
33+
}
34+
}

0 commit comments

Comments
 (0)