Skip to content

Commit 07ffc3a

Browse files
author
Robert Diers
committed
s3 adapter
1 parent 7ebf519 commit 07ffc3a

12 files changed

Lines changed: 547 additions & 2 deletions

File tree

README.md

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,7 @@ Container: https://github.com/opentestingapi/impl_java/pkgs/container/opentest
1212
| 1.3 | 1.0 | introduced senddelay for Kafka producer, fixed timer bug for reloadtestcases, dependency upgrades, Sonar fixes, new DevEnv, systemtests, springdoc migration |
1313
| 1.4 | 1.0 | decreased some log levels, removed logs for "zero" values, unique names testcontainer, increased readability system test output |
1414
| 1.5 | 1.0 | result content-type and logic fixed for pause endpoint, more understandable log for checks, new field description for injects and checks |
15+
| 1.6 | 1.0 | S3-Adapter, Swagger-UI bugfixes (content-type upload) |
1516

1617
## Architecture
1718

@@ -44,6 +45,7 @@ the following adapters could be used:
4445
| kafka | [ReadMe](src/main/java/org/opentesting/services/adapter/kafka/README.md) |
4546
| jdbc | [ReadMe](src/main/java/org/opentesting/services/adapter/jdbc/README.md) |
4647
| rest/soap | [ReadMe](src/main/java/org/opentesting/services/adapter/rest/README.md) |
48+
| S3 | [ReadMe](src/main/java/org/opentesting/services/adapter/s3/README.md) |
4749

4850

4951
## Random Data / Replacements

pom.xml

Lines changed: 9 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -17,7 +17,8 @@
1717

1818
<properties>
1919
<java.version>11</java.version>
20-
<cassandra.version>3.11.1</cassandra.version>
20+
<cassandra.version>3.11.1</cassandra.version>
21+
<aws.sdk.version>1.12.234</aws.sdk.version>
2122
<springdoc.version>1.2.19</springdoc.version>
2223
<spring.cloud.version>3.1.1</spring.cloud.version>
2324
<cache2k.version>2.6.1.Final</cache2k.version>
@@ -175,6 +176,13 @@
175176
<scope>runtime</scope>
176177
</dependency>
177178

179+
<!-- adapter S3 -->
180+
<dependency>
181+
<groupId>com.amazonaws</groupId>
182+
<artifactId>aws-java-sdk-s3</artifactId>
183+
<version>${aws.sdk.version}</version>
184+
</dependency>
185+
178186
<!-- regex to random value -->
179187
<dependency>
180188
<groupId>com.github.curious-odd-man</groupId>
Lines changed: 89 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,89 @@
1+
# S3
2+
3+
Using API to insert/check data to/from S3
4+
5+
### Configuration Inject
6+
7+
* please use sourcefile to store content to upload
8+
* please use username for accessKey and password for secretAccessKey
9+
* random data replacement processed for 'bucketName', 'fileName' (target name in S3), 'sourcefile' content
10+
11+
```
12+
{
13+
"injectid": "inject-s3-1",
14+
"cron": [ "2 * * * * ?" , "13 * * * * ?", "23 * * * * ?", "34 * * * * ?", "43 * * * * ?", "53 * * * * ?" ],
15+
"active": true,
16+
"service": {
17+
"type": "s3",
18+
"connectstring": "s3-serviceEndpoint",
19+
"username": "myaccesskey",
20+
"password": "mysecretaccesskey",
21+
"custom": [
22+
{
23+
"key": "region",
24+
"value": "myregion"
25+
},
26+
{
27+
"key": "bucketName",
28+
"value": "mybucketname"
29+
},
30+
{
31+
"key": "fileName",
32+
"value": "myfilename"
33+
}
34+
]
35+
},
36+
"sourcefile": "source_s3_1.json",
37+
"timetolive": "5d",
38+
"checks": [ "check-s3-1" ],
39+
"replacements": [...]
40+
}
41+
```
42+
43+
### Configuration Check
44+
45+
* please use username for accessKey and password for secretAccessKey
46+
* please use validation 'response' to define expected JSON result(s)
47+
* expectedtype could be 'equals', 'contains', 'containsnot', 'containsoneof'
48+
* random data replacement processed for: 'bucketName', 'fileName' (target name from S3), 'response' (content)
49+
* injects executed after successful check (please do not create infinite loops)
50+
* please use result2random to transfer JSON result (only JSON!) attributes to random data for later usage
51+
52+
```
53+
{
54+
"checkid": "check-s3-1",
55+
"active": true,
56+
"service": {
57+
"type": "jdbc",
58+
"connectstring": "s3-serviceEndpoint",
59+
"username": "myaccesskey",
60+
"password": "mysecretaccesskey",
61+
"custom": [
62+
{
63+
"key": "region",
64+
"value": "myregion"
65+
},
66+
{
67+
"key": "bucketName",
68+
"value": "mybucketname"
69+
},
70+
{
71+
"key": "fileName",
72+
"value": "myfilename"
73+
}
74+
]
75+
},
76+
"validations": [
77+
{
78+
"order": 1,
79+
"request": "",
80+
"response": [ "check_s3_1.json" ],
81+
"type": "equals"
82+
}],
83+
"maxwaittime": "10m",
84+
"mandatory": false,
85+
"injects": [ "inject-rest-1" ],
86+
"checks": [ "check-rest-1" ],
87+
"result2random": [ "attribute1", "attribute2" ]
88+
}
89+
```
Lines changed: 189 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,189 @@
1+
package org.opentesting.services.adapter.s3;
2+
3+
import java.util.Arrays;
4+
import java.util.HashMap;
5+
import java.util.List;
6+
7+
import javax.annotation.PreDestroy;
8+
9+
import org.opentesting.dto.TestCaseCheckDTO;
10+
import org.opentesting.dto.TestCaseDTO;
11+
import org.opentesting.dto.TestCaseInjectionDTO;
12+
import org.opentesting.dto.TestCaseValidationDTO;
13+
import org.opentesting.services.adapter.Adapter;
14+
import org.opentesting.util.LogExecutionTime;
15+
import org.opentesting.util.exceptions.ConnectFailedException;
16+
import org.springframework.stereotype.Component;
17+
18+
import lombok.extern.slf4j.Slf4j;
19+
20+
@Component
21+
@Slf4j
22+
public class S3 extends Adapter {
23+
24+
private static final String BUCKETNAME = "bucketName";
25+
private static final String FILENAME = "fileName";
26+
private static final String REGION = "region";
27+
28+
//Connection cache
29+
private HashMap<String, S3Connector> s3connectors = new HashMap<>();
30+
31+
@Override
32+
public String getServicename() {
33+
return "s3";
34+
}
35+
36+
@Override
37+
public boolean inject(String testid, TestCaseInjectionDTO inject) {
38+
try {
39+
//connect
40+
S3Config config = createConfig(inject.getService().getConnectstring(),
41+
inject.getService().getUsername(),
42+
inject.getService().getPassword(),
43+
inject.getService().getCustom(REGION).getValue());
44+
S3Connector connection = getConnector(testid, config);
45+
if (connection == null) return false;
46+
47+
//random data replacements
48+
String bucketName = this.addRandomData(inject.getService().getCustom(BUCKETNAME).getValue(), inject.getRandomdata());
49+
log.debug(bucketName);
50+
String filename = this.addRandomData(inject.getService().getCustom(FILENAME).getValue(), inject.getRandomdata());
51+
log.debug(filename);
52+
String data = this.addRandomData(this.getFile(testid, inject.getSourcefile()), inject.getRandomdata());
53+
log.debug(data);
54+
55+
//push to S3
56+
connection.store(filename, data, bucketName);
57+
58+
return true;
59+
} catch (Exception e) {
60+
log.error(testid+" "+inject.getInjectid()+" "+inject.getInstanceid()+": inject failed", e);
61+
return false;
62+
}
63+
}
64+
65+
@Override
66+
public boolean check(String testid, TestCaseCheckDTO check, Object... args) {
67+
try {
68+
//connect
69+
S3Config config = createConfig(check.getService().getConnectstring(),
70+
check.getService().getUsername(),
71+
check.getService().getPassword(),
72+
check.getService().getCustom(REGION).getValue());
73+
S3Connector connection = getConnector(testid, config);
74+
if (connection == null) return false;
75+
76+
//random data replacements
77+
String bucketName = this.addRandomData(check.getService().getCustom(BUCKETNAME).getValue(), check.getRandomdata());
78+
log.debug(bucketName);
79+
String filename = this.addRandomData(check.getService().getCustom(FILENAME).getValue(), check.getRandomdata());
80+
log.debug(filename);
81+
82+
//read file from S3
83+
String result = connection.read(filename, bucketName);
84+
85+
boolean retvalue = true;
86+
87+
//check all validations, sort first
88+
for (TestCaseValidationDTO validation : sortValidations(check.getValidations())) {
89+
if (!validateResult(testid, check, validation, result, "S3 "+bucketName+"/"+filename)) {
90+
retvalue = false;
91+
}
92+
}
93+
94+
return retvalue;
95+
} catch (Exception e) {
96+
log.error(testid+" "+check.getCheckid()+" "+check.getInstanceid()+": check failed", e);
97+
return false;
98+
}
99+
}
100+
101+
private S3Config createConfig(String connectstring, String accessKey, String secretAccessKey, String region) {
102+
//config creation
103+
S3Config conf = new S3Config();
104+
conf.setAccessKey(accessKey);
105+
conf.setSecretAccessKey(secretAccessKey);
106+
conf.setRegion(region);
107+
conf.setServiceEndpoint(connectstring);
108+
return conf;
109+
}
110+
111+
/**
112+
* synchronized as we want have one connection only
113+
* @throws SQLException
114+
* @throws ClassNotFoundException
115+
*/
116+
@LogExecutionTime
117+
private synchronized S3Connector getConnector(String testid, S3Config config) throws ConnectFailedException {
118+
119+
//no reuse because of different password configurations
120+
String key = createConnectionKey(testid, config.getServiceEndpoint(), config.getAccessKey(), config.getRegion());
121+
S3Connector con = s3connectors.get(key);
122+
123+
//block ones
124+
if (this.isFailedConnector(key)) {
125+
log.warn(testid + ": S3 connection blocked - please check credentials and upload testcase: " + config.getAccessKey() + "@" + config.getServiceEndpoint() + "@" + config.getRegion());
126+
return null;
127+
}
128+
129+
try {
130+
//create new
131+
if (con == null) {
132+
con = new S3Connector(config);
133+
log.info("s3Connector created: "+con.getS3Config().getServiceEndpoint() + " for " + con.getS3Config().getAccessKey());
134+
s3connectors.put(key, con);
135+
}
136+
137+
return con;
138+
} catch (Exception e) {
139+
this.addFailedConnector(key);
140+
String jdbcmessage = testid+": S3 client failed - blocked: "+config.getAccessKey() + "@" + config.getServiceEndpoint() + "@" + config.getRegion();
141+
log.error(jdbcmessage, e);
142+
throw new ConnectFailedException(jdbcmessage);
143+
}
144+
}
145+
146+
@Override
147+
@LogExecutionTime
148+
public void createRequiredComponents(TestCaseDTO test) {
149+
try {
150+
//remove blocked connections and existing connectors
151+
this.removeFailedConnectorStartingWith(test.getId());
152+
HashMap<String, S3Connector> newConnectors = new HashMap<>();
153+
for (String key : s3connectors.keySet()) {
154+
if (key.startsWith(test.getId())) {
155+
s3connectors.get(key).close();
156+
S3Config tmp = s3connectors.get(key).getS3Config();
157+
log.info("closed: "+tmp.getServiceEndpoint()+" - "+tmp.getAccessKey());
158+
} else {
159+
newConnectors.put(key, s3connectors.get(key));
160+
}
161+
}
162+
s3connectors = newConnectors;
163+
} catch (Exception e) {
164+
log.warn("cannot create timer", e);
165+
}
166+
}
167+
168+
@Override
169+
@LogExecutionTime
170+
public List<String> getRequiredTimerCrons() {
171+
return Arrays.asList(openTestingConfig.getCheckcron());
172+
}
173+
174+
@PreDestroy
175+
@LogExecutionTime
176+
private void close() {
177+
//parallel close
178+
s3connectors.values().parallelStream().forEach(
179+
con -> {
180+
try {
181+
con.close();
182+
} catch (Exception e) {
183+
log.warn("cannot close connector", e);
184+
}
185+
}
186+
);
187+
}
188+
189+
}
Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,22 @@
1+
package org.opentesting.services.adapter.s3;
2+
3+
import lombok.AllArgsConstructor;
4+
import lombok.Getter;
5+
import lombok.NoArgsConstructor;
6+
import lombok.Setter;
7+
8+
@Setter
9+
@Getter
10+
@AllArgsConstructor
11+
@NoArgsConstructor
12+
public class S3Config {
13+
14+
private String serviceEndpoint;
15+
16+
private String region;
17+
18+
private String accessKey;
19+
20+
private String secretAccessKey;
21+
22+
}

0 commit comments

Comments
 (0)