|
| 1 | +import com.amazonaws.AmazonClientException |
| 2 | +import com.amazonaws.ClientConfiguration |
| 3 | +import com.amazonaws.Request |
1 | 4 | import com.amazonaws.SDKGlobalConfiguration
|
| 5 | +import com.amazonaws.auth.AWSCredentialsProviderChain |
2 | 6 | import com.amazonaws.auth.BasicAWSCredentials
|
| 7 | +import com.amazonaws.auth.EnvironmentVariableCredentialsProvider |
| 8 | +import com.amazonaws.auth.InstanceProfileCredentialsProvider |
| 9 | +import com.amazonaws.auth.SystemPropertiesCredentialsProvider |
| 10 | +import com.amazonaws.auth.profile.ProfileCredentialsProvider |
3 | 11 | import com.amazonaws.handlers.RequestHandler2
|
| 12 | +import com.amazonaws.retry.PredefinedRetryPolicies |
4 | 13 | import com.amazonaws.services.ec2.AmazonEC2Client
|
5 | 14 | import com.amazonaws.services.rds.AmazonRDSClient
|
6 | 15 | import com.amazonaws.services.rds.model.DeleteOptionGroupRequest
|
7 | 16 | import com.amazonaws.services.s3.AmazonS3Client
|
8 | 17 | import com.amazonaws.services.s3.S3ClientOptions
|
9 | 18 | import datadog.trace.agent.test.AgentTestRunner
|
10 | 19 | import datadog.trace.api.DDSpanTypes
|
| 20 | +import io.opentracing.Tracer |
11 | 21 | import io.opentracing.tag.Tags
|
| 22 | +import org.apache.http.conn.HttpHostConnectException |
| 23 | +import org.apache.http.impl.execchain.RequestAbortedException |
12 | 24 | import spock.lang.AutoCleanup
|
13 | 25 | import spock.lang.Shared
|
14 | 26 |
|
15 | 27 | import java.util.concurrent.atomic.AtomicReference
|
16 | 28 |
|
17 | 29 | import static datadog.trace.agent.test.server.http.TestHttpServer.httpServer
|
| 30 | +import static datadog.trace.agent.test.utils.PortUtils.UNUSABLE_PORT |
18 | 31 |
|
19 | 32 | class AWSClientTest extends AgentTestRunner {
|
| 33 | + |
| 34 | + private static final CREDENTIALS_PROVIDER_CHAIN = new AWSCredentialsProviderChain( |
| 35 | + new EnvironmentVariableCredentialsProvider(), |
| 36 | + new SystemPropertiesCredentialsProvider(), |
| 37 | + new ProfileCredentialsProvider(), |
| 38 | + new InstanceProfileCredentialsProvider()) |
| 39 | + |
20 | 40 | def setupSpec() {
|
21 | 41 | System.setProperty(SDKGlobalConfiguration.ACCESS_KEY_SYSTEM_PROPERTY, "my-access-key")
|
22 | 42 | System.setProperty(SDKGlobalConfiguration.SECRET_KEY_SYSTEM_PROPERTY, "my-secret-key")
|
@@ -63,9 +83,11 @@ class AWSClientTest extends AgentTestRunner {
|
63 | 83 | def "send #operation request with mocked response"() {
|
64 | 84 | setup:
|
65 | 85 | responseBody.set(body)
|
| 86 | + |
| 87 | + when: |
66 | 88 | def response = call.call(client)
|
67 | 89 |
|
68 |
| - expect: |
| 90 | + then: |
69 | 91 | response != null
|
70 | 92 |
|
71 | 93 | client.requestHandler2s != null
|
@@ -135,4 +157,175 @@ class AWSClientTest extends AgentTestRunner {
|
135 | 157 | </DeleteOptionGroupResponse>
|
136 | 158 | """ | new AmazonRDSClient().withEndpoint("http://localhost:$server.address.port")
|
137 | 159 | }
|
| 160 | + |
| 161 | + def "send #operation request to closed port"() { |
| 162 | + setup: |
| 163 | + responseBody.set(body) |
| 164 | + |
| 165 | + when: |
| 166 | + call.call(client) |
| 167 | + |
| 168 | + then: |
| 169 | + thrown AmazonClientException |
| 170 | + |
| 171 | + assertTraces(1) { |
| 172 | + trace(0, 2) { |
| 173 | + span(0) { |
| 174 | + serviceName "java-aws-sdk" |
| 175 | + operationName "aws.http" |
| 176 | + resourceName "$service.$operation" |
| 177 | + spanType DDSpanTypes.HTTP_CLIENT |
| 178 | + errored true |
| 179 | + parent() |
| 180 | + tags { |
| 181 | + "$Tags.COMPONENT.key" "java-aws-sdk" |
| 182 | + "$Tags.HTTP_URL.key" "http://localhost:${UNUSABLE_PORT}" |
| 183 | + "$Tags.HTTP_METHOD.key" "$method" |
| 184 | + "$Tags.SPAN_KIND.key" Tags.SPAN_KIND_CLIENT |
| 185 | + "aws.service" { it.contains(service) } |
| 186 | + "aws.endpoint" "http://localhost:${UNUSABLE_PORT}" |
| 187 | + "aws.operation" "${operation}Request" |
| 188 | + "aws.agent" "java-aws-sdk" |
| 189 | + errorTags AmazonClientException, ~/Unable to execute HTTP request/ |
| 190 | + defaultTags() |
| 191 | + } |
| 192 | + } |
| 193 | + span(1) { |
| 194 | + operationName "http.request" |
| 195 | + resourceName "$method /$url" |
| 196 | + spanType DDSpanTypes.HTTP_CLIENT |
| 197 | + errored true |
| 198 | + childOf(span(0)) |
| 199 | + tags { |
| 200 | + "$Tags.COMPONENT.key" "apache-httpclient" |
| 201 | + "$Tags.HTTP_URL.key" "http://localhost:${UNUSABLE_PORT}/$url" |
| 202 | + "$Tags.PEER_HOSTNAME.key" "localhost" |
| 203 | + "$Tags.PEER_PORT.key" UNUSABLE_PORT |
| 204 | + "$Tags.HTTP_METHOD.key" "$method" |
| 205 | + "$Tags.SPAN_KIND.key" Tags.SPAN_KIND_CLIENT |
| 206 | + errorTags HttpHostConnectException, ~/Connection refused/ |
| 207 | + defaultTags() |
| 208 | + } |
| 209 | + } |
| 210 | + } |
| 211 | + } |
| 212 | + |
| 213 | + where: |
| 214 | + service | operation | method | url | call | body | client |
| 215 | + "S3" | "GetObject" | "GET" | "someBucket/someKey" | { client -> client.getObject("someBucket", "someKey") } | "" | new AmazonS3Client(CREDENTIALS_PROVIDER_CHAIN, new ClientConfiguration().withRetryPolicy(PredefinedRetryPolicies.getDefaultRetryPolicyWithCustomMaxRetries(0))).withEndpoint("http://localhost:${UNUSABLE_PORT}") |
| 216 | + } |
| 217 | + |
| 218 | + def "naughty request handler doesn't break the trace"() { |
| 219 | + setup: |
| 220 | + def client = new AmazonS3Client(CREDENTIALS_PROVIDER_CHAIN) |
| 221 | + client.addRequestHandler(new RequestHandler2() { |
| 222 | + void beforeRequest(Request<?> request) { |
| 223 | + throw new RuntimeException("bad handler") |
| 224 | + } |
| 225 | + }) |
| 226 | + |
| 227 | + when: |
| 228 | + client.getObject("someBucket", "someKey") |
| 229 | + |
| 230 | + then: |
| 231 | + ((Tracer) TEST_TRACER).activeSpan() == null |
| 232 | + thrown RuntimeException |
| 233 | + |
| 234 | + assertTraces(1) { |
| 235 | + trace(0, 1) { |
| 236 | + span(0) { |
| 237 | + serviceName "java-aws-sdk" |
| 238 | + operationName "aws.http" |
| 239 | + resourceName "S3.GetObject" |
| 240 | + spanType DDSpanTypes.HTTP_CLIENT |
| 241 | + errored true |
| 242 | + parent() |
| 243 | + tags { |
| 244 | + "$Tags.COMPONENT.key" "java-aws-sdk" |
| 245 | + "$Tags.HTTP_URL.key" "https://s3.amazonaws.com" |
| 246 | + "$Tags.HTTP_METHOD.key" "GET" |
| 247 | + "$Tags.SPAN_KIND.key" Tags.SPAN_KIND_CLIENT |
| 248 | + "aws.service" "Amazon S3" |
| 249 | + "aws.endpoint" "https://s3.amazonaws.com" |
| 250 | + "aws.operation" "GetObjectRequest" |
| 251 | + "aws.agent" "java-aws-sdk" |
| 252 | + errorTags RuntimeException, "bad handler" |
| 253 | + defaultTags() |
| 254 | + } |
| 255 | + } |
| 256 | + } |
| 257 | + } |
| 258 | + } |
| 259 | + |
| 260 | + def "timeout and retry errors captured"() { |
| 261 | + setup: |
| 262 | + def server = httpServer { |
| 263 | + handlers { |
| 264 | + all { |
| 265 | + Thread.sleep(500) |
| 266 | + response.status(200).send() |
| 267 | + } |
| 268 | + } |
| 269 | + } |
| 270 | + AmazonS3Client client = new AmazonS3Client(new ClientConfiguration().withRequestTimeout(50 /* ms */)) |
| 271 | + .withEndpoint("http://localhost:$server.address.port") |
| 272 | + |
| 273 | + when: |
| 274 | + client.getObject("someBucket", "someKey") |
| 275 | + |
| 276 | + then: |
| 277 | + ((Tracer) TEST_TRACER).activeSpan() == null |
| 278 | + thrown AmazonClientException |
| 279 | + |
| 280 | + assertTraces(1) { |
| 281 | + trace(0, 5) { |
| 282 | + span(0) { |
| 283 | + serviceName "java-aws-sdk" |
| 284 | + operationName "aws.http" |
| 285 | + resourceName "S3.GetObject" |
| 286 | + spanType DDSpanTypes.HTTP_CLIENT |
| 287 | + errored true |
| 288 | + parent() |
| 289 | + tags { |
| 290 | + "$Tags.COMPONENT.key" "java-aws-sdk" |
| 291 | + "$Tags.HTTP_URL.key" "http://localhost:$server.address.port" |
| 292 | + "$Tags.HTTP_METHOD.key" "GET" |
| 293 | + "$Tags.SPAN_KIND.key" Tags.SPAN_KIND_CLIENT |
| 294 | + "aws.service" "Amazon S3" |
| 295 | + "aws.endpoint" "http://localhost:$server.address.port" |
| 296 | + "aws.operation" "GetObjectRequest" |
| 297 | + "aws.agent" "java-aws-sdk" |
| 298 | + errorTags AmazonClientException, ~/Unable to execute HTTP request/ |
| 299 | + defaultTags() |
| 300 | + } |
| 301 | + } |
| 302 | + (1..4).each { |
| 303 | + span(it) { |
| 304 | + operationName "http.request" |
| 305 | + resourceName "GET /someBucket/someKey" |
| 306 | + spanType DDSpanTypes.HTTP_CLIENT |
| 307 | + errored true |
| 308 | + childOf(span(0)) |
| 309 | + tags { |
| 310 | + "$Tags.COMPONENT.key" "apache-httpclient" |
| 311 | + "$Tags.HTTP_URL.key" "http://localhost:$server.address.port/someBucket/someKey" |
| 312 | + "$Tags.PEER_HOSTNAME.key" "localhost" |
| 313 | + "$Tags.PEER_PORT.key" server.address.port |
| 314 | + "$Tags.HTTP_METHOD.key" "GET" |
| 315 | + "$Tags.SPAN_KIND.key" Tags.SPAN_KIND_CLIENT |
| 316 | + try { |
| 317 | + errorTags SocketException, "Socket closed" |
| 318 | + } catch (AssertionError e) { |
| 319 | + errorTags RequestAbortedException, "Request aborted" |
| 320 | + } |
| 321 | + defaultTags() |
| 322 | + } |
| 323 | + } |
| 324 | + } |
| 325 | + } |
| 326 | + } |
| 327 | + |
| 328 | + cleanup: |
| 329 | + server.close() |
| 330 | + } |
138 | 331 | }
|
0 commit comments