Skip to content

Commit 8412a90

Browse files
authored
Merge pull request #32 from bhautikpip/merge-conflict-trace-validator
TraceValidator
2 parents 7d539bc + 2072e38 commit 8412a90

File tree

13 files changed

+288
-88
lines changed

13 files changed

+288
-88
lines changed

validator/build.gradle

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -41,6 +41,9 @@ dependencies {
4141
// yaml reader
4242
compile group: 'com.fasterxml.jackson.dataformat', name: 'jackson-dataformat-yaml', version: '2.11.1'
4343

44+
// json flattener
45+
compile group: 'com.github.wnameless', name: 'json-flattener', version: '0.1.0'
46+
4447
// command cli
4548
compile 'info.picocli:picocli:4.3.2'
4649

validator/src/main/java/com/amazon/aoc/callers/HttpCaller.java

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -35,6 +35,7 @@ public class HttpCaller implements ICaller {
3535

3636
public HttpCaller(String endpoint, String path) {
3737
this.url = endpoint + path;
38+
log.info("validator is trying to hit this {} endpoint", this.url);
3839
}
3940

4041
@Override
@@ -44,7 +45,7 @@ public SampleAppResponse callSampleApp() throws Exception {
4445

4546
AtomicReference<SampleAppResponse> sampleAppResponseAtomicReference = new AtomicReference<>();
4647
RetryHelper.retry(
47-
60,
48+
30,
4849
() -> {
4950
try (Response response = client.newCall(request).execute()) {
5051
String responseBody = response.body().string();

validator/src/main/java/com/amazon/aoc/exception/ExceptionCode.java

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -25,10 +25,12 @@ public enum ExceptionCode {
2525

2626
// validating errors
2727
TRACE_ID_NOT_MATCHED(50001, "trace id not matched"),
28+
DATA_MODEL_NOT_MATCHED(50006, "trace id not matched"),
2829
TRACE_SPAN_LIST_NOT_MATCHED(50002, "trace span list has different length"),
2930
TRACE_SPAN_NOT_MATCHED(50003, "trace span not matched"),
3031
TRACE_LIST_NOT_MATCHED(50004, "trace list has different length"),
3132
DATA_EMITTER_UNAVAILABLE(50005, "the data emitter is unavailable to ping"),
33+
EMPTY_LIST(50007, "list is empty or null"),
3234

3335
// build validator
3436
VALIDATION_TYPE_NOT_EXISTED(60001, "validation type not existed"),

validator/src/main/java/com/amazon/aoc/fileconfigs/ExpectedTrace.java

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,10 @@
2020
@Getter
2121
public enum ExpectedTrace implements FileConfig {
2222
DEFAULT_EXPECTED_TRACE("/expected-data-template/defaultExpectedTrace.mustache"),
23+
OTEL_SDK_AWSSDK_EXPECTED_TRACE("/expected-data-template/otelSDKexpectedAWSSDKTrace.mustache"),
24+
OTEL_SDK_HTTP_EXPECTED_TRACE("/expected-data-template/otelSDKexpectedHTTPTrace.mustache"),
25+
XRAY_SDK_AWSSDK_EXPECTED_TRACE("/expected-data-template/xraySDKexpectedAWSSDKTrace.mustache"),
26+
XRAY_SDK_HTTP_EXPECTED_TRACE("/expected-data-template/xraySDKexpectedHTTPTrace.mustache"),
2327
;
2428

2529
private String path;

validator/src/main/java/com/amazon/aoc/helpers/RetryHelper.java

Lines changed: 7 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -37,11 +37,16 @@ public static void retry(
3737
throws Exception {
3838
while (retryCount-- > 0) {
3939
try {
40-
log.info("still can retry for {} times", retryCount);
40+
log.info("retry attempt left : {} ", retryCount);
4141
retryable.execute();
4242
return;
4343
} catch (Exception ex) {
44-
log.error("exception during retry, you may ignore it", ex);
44+
log.info("retrying after {} seconds", TimeUnit.MILLISECONDS.toSeconds(sleepInMilliSeconds));
45+
46+
if (retryCount == 0) {
47+
log.error("retries exhausted, possible exception: ", ex);
48+
break;
49+
}
4550
TimeUnit.MILLISECONDS.sleep(sleepInMilliSeconds);
4651
}
4752
}

validator/src/main/java/com/amazon/aoc/validators/TraceValidator.java

Lines changed: 93 additions & 79 deletions
Original file line numberDiff line numberDiff line change
@@ -26,93 +26,107 @@
2626
import com.amazon.aoc.models.ValidationConfig;
2727
import com.amazon.aoc.services.XRayService;
2828
import com.amazonaws.services.xray.model.Trace;
29+
import com.github.wnameless.json.flattener.JsonFlattener;
2930
import lombok.extern.log4j.Log4j2;
3031

31-
import java.util.Arrays;
32-
import java.util.Comparator;
33-
import java.util.List;
34-
import java.util.stream.Collectors;
32+
import java.util.*;
33+
import java.util.concurrent.atomic.AtomicReference;
3534

3635
@Log4j2
3736
public class TraceValidator implements IValidator {
38-
private MustacheHelper mustacheHelper = new MustacheHelper();
39-
private static int MAX_RETRY_COUNT = 60;
40-
private Context context;
41-
private XRayService xrayService;
42-
private ICaller caller;
43-
private FileConfig expectedTrace;
44-
45-
@Override
46-
public void init(
47-
Context context, ValidationConfig validationConfig, ICaller caller, FileConfig expectedTrace)
48-
throws Exception {
49-
this.context = context;
50-
this.xrayService = new XRayService(context.getRegion());
51-
this.caller = caller;
52-
this.expectedTrace = expectedTrace;
53-
}
54-
55-
@Override
56-
public void validate() throws Exception {
57-
List<Trace> expectedTraceList = this.getExpectedTrace();
58-
expectedTraceList.sort(Comparator.comparing(Trace::getId));
59-
RetryHelper.retry(
60-
MAX_RETRY_COUNT,
61-
() -> {
62-
List<Trace> traceList =
63-
xrayService.listTraceByIds(
64-
expectedTraceList.stream()
65-
.map(trace -> trace.getId())
66-
.collect(Collectors.toList()));
67-
68-
traceList.sort(Comparator.comparing(Trace::getId));
69-
70-
log.info("expectedTraceList: {}", expectedTraceList);
71-
log.info("traceList got from backend: {}", traceList);
72-
if (expectedTraceList.size() != traceList.size()) {
73-
throw new BaseException(ExceptionCode.TRACE_LIST_NOT_MATCHED);
74-
}
75-
76-
for (int i = 0; i != expectedTraceList.size(); ++i) {
77-
Trace trace = traceList.get(i);
78-
compareTwoTraces(expectedTraceList.get(i), trace);
79-
}
37+
private MustacheHelper mustacheHelper = new MustacheHelper();
38+
private XRayService xrayService;
39+
private ICaller caller;
40+
private Context context;
41+
private FileConfig expectedTrace;
42+
43+
@Override
44+
public void init(Context context, ValidationConfig validationConfig, ICaller caller, FileConfig expectedTrace) throws Exception {
45+
this.xrayService = new XRayService(context.getRegion());
46+
this.caller = caller;
47+
this.context = context;
48+
this.expectedTrace = expectedTrace;
49+
}
50+
51+
@Override
52+
public void validate() throws Exception {
53+
// get stored trace
54+
Map<String, Object> storedTrace = this.getStoredTrace();
55+
56+
// create trace id list to retrieve trace from x-ray service
57+
String traceId = (String) storedTrace.get("trace_id");
58+
List<String> traceIdList = Collections.singletonList(traceId);
59+
60+
// get retrieved trace from x-ray service
61+
Map<String, Object> retrievedTrace = this.getRetrievedTrace(traceIdList);
62+
63+
// validation of trace id
64+
if (!storedTrace.get("trace_id").equals(retrievedTrace.get("trace_id"))) {
65+
log.error("trace id validation failed");
66+
throw new BaseException(ExceptionCode.TRACE_ID_NOT_MATCHED);
67+
}
68+
69+
// data model validation of other fields of segment document
70+
for (Map.Entry<String, Object> entry : storedTrace.entrySet()) {
71+
if (!entry.getValue().equals(retrievedTrace.get(entry.getKey()))) {
72+
log.error("data model validation failed");
73+
log.info("mis matched data model field list");
74+
log.info("value of stored trace map: {}", entry.getValue());
75+
log.info("value of retrieved map: {}",retrievedTrace.get(entry.getKey()));
76+
log.info("==========================================");
77+
throw new BaseException(ExceptionCode.DATA_MODEL_NOT_MATCHED);
78+
}
79+
}
80+
}
81+
82+
// this method will hit get trace from x-ray service and get retrieved trace
83+
private Map<String, Object> getRetrievedTrace(List<String> traceIdList) throws Exception {
84+
Map<String, Object> flattenedJsonMapForRetrievedTrace = null;
85+
AtomicReference<List<Trace>> retrieveTraceListAtomicReference = new AtomicReference<>();
86+
int MAX_RETRY_COUNT = 3;
87+
88+
RetryHelper.retry(MAX_RETRY_COUNT, () -> {
89+
List<Trace> retrieveTraceList = null;
90+
retrieveTraceList = xrayService.listTraceByIds(traceIdList);
91+
retrieveTraceListAtomicReference.set(retrieveTraceList);
92+
93+
if (retrieveTraceList == null || retrieveTraceList.isEmpty()) {
94+
throw new BaseException(ExceptionCode.EMPTY_LIST);
95+
}
8096
});
81-
}
8297

83-
private void compareTwoTraces(Trace trace1, Trace trace2) throws BaseException {
84-
// check trace id
85-
if (!trace1.getId().equals(trace2.getId())) {
86-
throw new BaseException(ExceptionCode.TRACE_ID_NOT_MATCHED);
98+
// flattened JSON object to a map
99+
if (retrieveTraceListAtomicReference.get() != null && !retrieveTraceListAtomicReference.get().isEmpty()) {
100+
try {
101+
flattenedJsonMapForRetrievedTrace = JsonFlattener.flattenAsMap(retrieveTraceListAtomicReference.get().get(0).getSegments().get(0).getDocument());
102+
} catch (Exception e) {
103+
log.error("exception while flattening the retrieved trace: " + e.getMessage());
104+
e.printStackTrace();
105+
}
106+
} else {
107+
log.error("retrieved trace list is empty or null");
108+
throw new BaseException(ExceptionCode.EMPTY_LIST);
109+
}
110+
111+
return flattenedJsonMapForRetrievedTrace;
87112
}
88113

89-
/*
90-
if (trace1.getSegments().size() != trace2.getSegments().size()) {
91-
throw new BaseException(ExceptionCode.TRACE_SPAN_LIST_NOT_MATCHED);
114+
// this method will hit a http endpoints of sample web apps and get stored trace
115+
private Map<String, Object> getStoredTrace() throws Exception {
116+
Map<String, Object> flattenedJsonMapForStoredTraces = null;
117+
118+
SampleAppResponse sampleAppResponse = this.caller.callSampleApp();
119+
120+
String jsonExpectedTrace = mustacheHelper.render(this.expectedTrace, context);
121+
122+
try {
123+
// flattened JSON object to a map
124+
flattenedJsonMapForStoredTraces = JsonFlattener.flattenAsMap(jsonExpectedTrace);
125+
flattenedJsonMapForStoredTraces.put("trace_id", sampleAppResponse.getTraceId());
126+
} catch (Exception e) {
127+
e.printStackTrace();
128+
}
129+
130+
return flattenedJsonMapForStoredTraces;
92131
}
93-
trace1.getSegments().sort(Comparator.comparing(Segment::getId));
94-
trace2.getSegments().sort(Comparator.comparing(Segment::getId));
95-
96-
for (int i = 0; i != trace1.getSegments().size(); ++i) {
97-
// check span id
98-
if (!trace1.getSegments().get(i).getId()
99-
.equals(trace2.getSegments().get(i).getId())) {
100-
throw new BaseException(ExceptionCode.TRACE_SPAN_NOT_MATCHED);
101-
}
102-
}*/
103-
}
104-
105-
// this endpoint will be a http endpoint including the path with get method
106-
private List<Trace> getExpectedTrace() throws Exception {
107-
SampleAppResponse sampleAppResponse = this.caller.callSampleApp();
108-
109-
// convert the trace data into xray format
110-
Trace trace = new Trace();
111-
trace.setId(sampleAppResponse.getTraceId());
112-
113-
// todo: construct the trace data from the template file
114-
115-
// we can support multi expected trace id to validate if need
116-
return Arrays.asList(trace);
117-
}
118132
}
Lines changed: 58 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,58 @@
1+
{
2+
"name": "/aws-sdk-call",
3+
"fault": false,
4+
"error": false,
5+
"throttle": false,
6+
"http": {
7+
"request": {
8+
"url": "{{endpoint}}/aws-sdk-call",
9+
"method": "GET"
10+
},
11+
"response": {
12+
"status": 200
13+
}
14+
},
15+
"subsegments": [
16+
{
17+
"fault": false,
18+
"error": false,
19+
"throttle": false,
20+
"subsegments": [
21+
{
22+
"name": "S3",
23+
"fault": false,
24+
"error": false,
25+
"throttle": false,
26+
"http": {
27+
"request": {
28+
"url": "https://s3.{{region}}.amazonaws.com/",
29+
"method": "GET"
30+
},
31+
"response": {
32+
"status": 200
33+
}
34+
},
35+
"namespace": "aws",
36+
"subsegments": [
37+
{
38+
"name": "s3.{{region}}.amazonaws.com",
39+
"fault": false,
40+
"error": false,
41+
"throttle": false,
42+
"http": {
43+
"request": {
44+
"url": "https://s3.{{region}}.amazonaws.com/",
45+
"method": "GET"
46+
},
47+
"response": {
48+
"status": 200
49+
}
50+
},
51+
"namespace": "remote"
52+
}
53+
]
54+
}
55+
]
56+
}
57+
]
58+
}
Lines changed: 40 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,40 @@
1+
{
2+
"name": "/outgoing-http-call",
3+
"fault": false,
4+
"error": false,
5+
"throttle": false,
6+
"http": {
7+
"request": {
8+
"url": "{{endpoint}}/outgoing-http-call",
9+
"method": "GET"
10+
},
11+
"response": {
12+
"status": 200
13+
}
14+
},
15+
"subsegments": [
16+
{
17+
"fault": false,
18+
"error": false,
19+
"throttle": false,
20+
"subsegments": [
21+
{
22+
"name": "aws.amazon.com",
23+
"fault": false,
24+
"error": false,
25+
"throttle": false,
26+
"http": {
27+
"request": {
28+
"url": "https://aws.amazon.com/",
29+
"method": "GET"
30+
},
31+
"response": {
32+
"status": 200
33+
}
34+
},
35+
"namespace": "remote"
36+
}
37+
]
38+
}
39+
]
40+
}
Lines changed: 27 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,27 @@
1+
{
2+
"in_progress": false,
3+
"http": {
4+
"request": {
5+
"url": "{{endpoint}}/aws-sdk-call",
6+
"method": "GET"
7+
},
8+
"response": {
9+
"status": 200
10+
}
11+
},
12+
"subsegments": [
13+
{
14+
"name": "S3",
15+
"in_progress": false,
16+
"http": {
17+
"response": {
18+
"status": 200
19+
}
20+
},
21+
"aws": {
22+
"operation": "ListBuckets"
23+
},
24+
"namespace": "aws"
25+
}
26+
]
27+
}

0 commit comments

Comments
 (0)