github.com/aavshr/aws-sdk-go@v1.41.3/aws/csm/reporter_test.go (about) 1 //go:build go1.7 2 // +build go1.7 3 4 package csm_test 5 6 import ( 7 "context" 8 "fmt" 9 "net/http" 10 "net/http/httptest" 11 "sort" 12 "testing" 13 "time" 14 15 "github.com/aavshr/aws-sdk-go/aws" 16 "github.com/aavshr/aws-sdk-go/aws/awserr" 17 "github.com/aavshr/aws-sdk-go/aws/client" 18 "github.com/aavshr/aws-sdk-go/aws/client/metadata" 19 "github.com/aavshr/aws-sdk-go/aws/csm" 20 "github.com/aavshr/aws-sdk-go/aws/request" 21 v4 "github.com/aavshr/aws-sdk-go/aws/signer/v4" 22 "github.com/aavshr/aws-sdk-go/awstesting/unit" 23 "github.com/aavshr/aws-sdk-go/private/protocol/jsonrpc" 24 ) 25 26 func TestReportingMetrics(t *testing.T) { 27 sess := unit.Session.Copy(&aws.Config{ 28 SleepDelay: func(time.Duration) {}, 29 }) 30 sess.Handlers.Validate.Clear() 31 sess.Handlers.Sign.Clear() 32 sess.Handlers.Send.Clear() 33 34 reporter := csm.Get() 35 if reporter == nil { 36 t.Errorf("expected non-nil reporter") 37 } 38 reporter.InjectHandlers(&sess.Handlers) 39 40 cases := map[string]struct { 41 Request *request.Request 42 ExpectMetrics []map[string]interface{} 43 }{ 44 "successful request": { 45 Request: func() *request.Request { 46 md := metadata.ClientInfo{} 47 op := &request.Operation{Name: "OperationName"} 48 req := request.New(*sess.Config, md, sess.Handlers, client.DefaultRetryer{NumMaxRetries: 3}, op, nil, nil) 49 req.Handlers.Send.PushBack(func(r *request.Request) { 50 req.HTTPResponse = &http.Response{ 51 StatusCode: 200, 52 Header: http.Header{}, 53 } 54 }) 55 return req 56 }(), 57 ExpectMetrics: []map[string]interface{}{ 58 { 59 "Type": "ApiCallAttempt", 60 "HttpStatusCode": float64(200), 61 }, 62 { 63 "Type": "ApiCall", 64 "FinalHttpStatusCode": float64(200), 65 }, 66 }, 67 }, 68 "failed request, no retry": { 69 Request: func() *request.Request { 70 md := metadata.ClientInfo{} 71 op := &request.Operation{Name: "OperationName"} 72 req := request.New(*sess.Config, md, sess.Handlers, client.DefaultRetryer{NumMaxRetries: 3}, op, nil, nil) 73 req.Handlers.Send.PushBack(func(r *request.Request) { 74 req.HTTPResponse = &http.Response{ 75 StatusCode: 400, 76 Header: http.Header{}, 77 } 78 req.Retryable = aws.Bool(false) 79 req.Error = awserr.New("Error", "Message", nil) 80 }) 81 82 return req 83 }(), 84 ExpectMetrics: []map[string]interface{}{ 85 { 86 "Type": "ApiCallAttempt", 87 "HttpStatusCode": float64(400), 88 "AwsException": "Error", 89 "AwsExceptionMessage": "Error: Message", 90 }, 91 { 92 "Type": "ApiCall", 93 "FinalHttpStatusCode": float64(400), 94 "FinalAwsException": "Error", 95 "FinalAwsExceptionMessage": "Error: Message", 96 "AttemptCount": float64(1), 97 }, 98 }, 99 }, 100 "failed request, with retry": { 101 Request: func() *request.Request { 102 md := metadata.ClientInfo{} 103 op := &request.Operation{Name: "OperationName"} 104 req := request.New(*sess.Config, md, sess.Handlers, client.DefaultRetryer{NumMaxRetries: 1}, op, nil, nil) 105 resps := []*http.Response{ 106 { 107 StatusCode: 500, 108 Header: http.Header{}, 109 }, 110 { 111 StatusCode: 500, 112 Header: http.Header{}, 113 }, 114 } 115 req.Handlers.Send.PushBack(func(r *request.Request) { 116 req.HTTPResponse = resps[0] 117 resps = resps[1:] 118 }) 119 120 return req 121 }(), 122 ExpectMetrics: []map[string]interface{}{ 123 { 124 "Type": "ApiCallAttempt", 125 "HttpStatusCode": float64(500), 126 "AwsException": "UnknownError", 127 "AwsExceptionMessage": "UnknownError: unknown error", 128 }, 129 { 130 "Type": "ApiCallAttempt", 131 "HttpStatusCode": float64(500), 132 "AwsException": "UnknownError", 133 "AwsExceptionMessage": "UnknownError: unknown error", 134 }, 135 { 136 "Type": "ApiCall", 137 "FinalHttpStatusCode": float64(500), 138 "FinalAwsException": "UnknownError", 139 "FinalAwsExceptionMessage": "UnknownError: unknown error", 140 "AttemptCount": float64(2), 141 }, 142 }, 143 }, 144 "success request, with retry": { 145 Request: func() *request.Request { 146 md := metadata.ClientInfo{} 147 op := &request.Operation{Name: "OperationName"} 148 req := request.New(*sess.Config, md, sess.Handlers, client.DefaultRetryer{NumMaxRetries: 3}, op, nil, nil) 149 errs := []error{ 150 awserr.New("AWSError", "aws error", nil), 151 awserr.New(request.ErrCodeRequestError, "sdk error", nil), 152 nil, 153 } 154 resps := []*http.Response{ 155 { 156 StatusCode: 500, 157 Header: http.Header{}, 158 }, 159 { 160 StatusCode: 500, 161 Header: http.Header{}, 162 }, 163 { 164 StatusCode: 200, 165 Header: http.Header{}, 166 }, 167 } 168 req.Handlers.Send.PushBack(func(r *request.Request) { 169 req.HTTPResponse = resps[0] 170 resps = resps[1:] 171 req.Error = errs[0] 172 errs = errs[1:] 173 }) 174 175 return req 176 }(), 177 ExpectMetrics: []map[string]interface{}{ 178 { 179 "Type": "ApiCallAttempt", 180 "AwsException": "AWSError", 181 "AwsExceptionMessage": "AWSError: aws error", 182 "HttpStatusCode": float64(500), 183 }, 184 { 185 "Type": "ApiCallAttempt", 186 "SdkException": request.ErrCodeRequestError, 187 "SdkExceptionMessage": request.ErrCodeRequestError + ": sdk error", 188 "HttpStatusCode": float64(500), 189 }, 190 { 191 "Type": "ApiCallAttempt", 192 "AwsException": nil, 193 "AwsExceptionMessage": nil, 194 "SdkException": nil, 195 "SdkExceptionMessage": nil, 196 "HttpStatusCode": float64(200), 197 }, 198 { 199 "Type": "ApiCall", 200 "FinalHttpStatusCode": float64(200), 201 "FinalAwsException": nil, 202 "FinalAwsExceptionMessage": nil, 203 "FinalSdkException": nil, 204 "FinalSdkExceptionMessage": nil, 205 "AttemptCount": float64(3), 206 }, 207 }, 208 }, 209 } 210 211 for name, c := range cases { 212 t.Run(name, func(t *testing.T) { 213 ctx, cancelFn := context.WithTimeout(context.Background(), time.Second) 214 defer cancelFn() 215 216 c.Request.Send() 217 for i := 0; i < len(c.ExpectMetrics); i++ { 218 select { 219 case m := <-csm.MetricsCh: 220 for ek, ev := range c.ExpectMetrics[i] { 221 if ev == nil { 222 // must not be set 223 if _, ok := m[ek]; ok { 224 t.Errorf("%d, expect %v metric member, not to be set, %v", i, ek, m[ek]) 225 } 226 continue 227 } 228 if _, ok := m[ek]; !ok { 229 t.Errorf("%d, expect %v metric member, keys: %v", i, ek, keys(m)) 230 } 231 if e, a := ev, m[ek]; e != a { 232 t.Errorf("%d, expect %v:%v(%T), metric value, got %v(%T)", i, ek, e, e, a, a) 233 } 234 } 235 case <-ctx.Done(): 236 t.Errorf("timeout waiting for metrics") 237 return 238 } 239 } 240 241 var extraMetrics []map[string]interface{} 242 Loop: 243 for { 244 select { 245 case m := <-csm.MetricsCh: 246 extraMetrics = append(extraMetrics, m) 247 default: 248 break Loop 249 } 250 } 251 if len(extraMetrics) != 0 { 252 t.Fatalf("unexpected metrics, %#v", extraMetrics) 253 } 254 }) 255 } 256 } 257 258 type mockService struct { 259 *client.Client 260 } 261 262 type input struct{} 263 type output struct{} 264 265 func (s *mockService) Request(i input) *request.Request { 266 op := &request.Operation{ 267 Name: "foo", 268 HTTPMethod: "POST", 269 HTTPPath: "/", 270 } 271 272 o := output{} 273 req := s.NewRequest(op, &i, &o) 274 return req 275 } 276 277 func BenchmarkWithCSM(b *testing.B) { 278 server := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { 279 w.Write([]byte(fmt.Sprintf("{}"))) 280 })) 281 defer server.Close() 282 283 cfg := aws.Config{ 284 Endpoint: aws.String(server.URL), 285 } 286 287 sess := unit.Session.Copy(&cfg) 288 r := csm.Get() 289 290 r.InjectHandlers(&sess.Handlers) 291 292 c := sess.ClientConfig("id", &cfg) 293 294 svc := mockService{ 295 client.New( 296 *c.Config, 297 metadata.ClientInfo{ 298 ServiceName: "service", 299 ServiceID: "id", 300 SigningName: "signing", 301 SigningRegion: "region", 302 Endpoint: server.URL, 303 APIVersion: "0", 304 JSONVersion: "1.1", 305 TargetPrefix: "prefix", 306 }, 307 c.Handlers, 308 ), 309 } 310 311 svc.Handlers.Sign.PushBackNamed(v4.SignRequestHandler) 312 svc.Handlers.Build.PushBackNamed(jsonrpc.BuildHandler) 313 svc.Handlers.Unmarshal.PushBackNamed(jsonrpc.UnmarshalHandler) 314 svc.Handlers.UnmarshalMeta.PushBackNamed(jsonrpc.UnmarshalMetaHandler) 315 svc.Handlers.UnmarshalError.PushBackNamed(jsonrpc.UnmarshalErrorHandler) 316 317 for i := 0; i < b.N; i++ { 318 req := svc.Request(input{}) 319 req.Send() 320 } 321 } 322 323 func BenchmarkWithCSMNoUDPConnection(b *testing.B) { 324 server := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { 325 w.Write([]byte(fmt.Sprintf("{}"))) 326 })) 327 defer server.Close() 328 329 cfg := aws.Config{ 330 Endpoint: aws.String(server.URL), 331 } 332 333 sess := unit.Session.Copy(&cfg) 334 r := csm.Get() 335 r.Pause() 336 r.InjectHandlers(&sess.Handlers) 337 defer r.Pause() 338 339 c := sess.ClientConfig("id", &cfg) 340 341 svc := mockService{ 342 client.New( 343 *c.Config, 344 metadata.ClientInfo{ 345 ServiceName: "service", 346 ServiceID: "id", 347 SigningName: "signing", 348 SigningRegion: "region", 349 Endpoint: server.URL, 350 APIVersion: "0", 351 JSONVersion: "1.1", 352 TargetPrefix: "prefix", 353 }, 354 c.Handlers, 355 ), 356 } 357 358 svc.Handlers.Sign.PushBackNamed(v4.SignRequestHandler) 359 svc.Handlers.Build.PushBackNamed(jsonrpc.BuildHandler) 360 svc.Handlers.Unmarshal.PushBackNamed(jsonrpc.UnmarshalHandler) 361 svc.Handlers.UnmarshalMeta.PushBackNamed(jsonrpc.UnmarshalMetaHandler) 362 svc.Handlers.UnmarshalError.PushBackNamed(jsonrpc.UnmarshalErrorHandler) 363 364 for i := 0; i < b.N; i++ { 365 req := svc.Request(input{}) 366 req.Send() 367 } 368 } 369 370 func BenchmarkWithoutCSM(b *testing.B) { 371 server := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { 372 w.Write([]byte(fmt.Sprintf("{}"))) 373 })) 374 defer server.Close() 375 376 cfg := aws.Config{ 377 Endpoint: aws.String(server.URL), 378 } 379 sess := unit.Session.Copy(&cfg) 380 c := sess.ClientConfig("id", &cfg) 381 382 svc := mockService{ 383 client.New( 384 *c.Config, 385 metadata.ClientInfo{ 386 ServiceName: "service", 387 ServiceID: "id", 388 SigningName: "signing", 389 SigningRegion: "region", 390 Endpoint: server.URL, 391 APIVersion: "0", 392 JSONVersion: "1.1", 393 TargetPrefix: "prefix", 394 }, 395 c.Handlers, 396 ), 397 } 398 399 svc.Handlers.Sign.PushBackNamed(v4.SignRequestHandler) 400 svc.Handlers.Build.PushBackNamed(jsonrpc.BuildHandler) 401 svc.Handlers.Unmarshal.PushBackNamed(jsonrpc.UnmarshalHandler) 402 svc.Handlers.UnmarshalMeta.PushBackNamed(jsonrpc.UnmarshalMetaHandler) 403 svc.Handlers.UnmarshalError.PushBackNamed(jsonrpc.UnmarshalErrorHandler) 404 405 for i := 0; i < b.N; i++ { 406 req := svc.Request(input{}) 407 req.Send() 408 } 409 } 410 411 func keys(m map[string]interface{}) []string { 412 ks := make([]string, 0, len(m)) 413 for k := range m { 414 ks = append(ks, k) 415 } 416 sort.Strings(ks) 417 return ks 418 }