Skip to content

Commit

Permalink
Merge pull request #85 from amzn/aws_client_protocol_async_await_support
Browse files Browse the repository at this point in the history
Experimental async/await support. ReportingConfiguration documentation.
  • Loading branch information
tachyonics authored Apr 29, 2021
2 parents 0be0300 + afa6e5a commit b1d157e
Show file tree
Hide file tree
Showing 8 changed files with 252 additions and 17 deletions.
24 changes: 19 additions & 5 deletions .travis.yml
Original file line number Diff line number Diff line change
Expand Up @@ -5,27 +5,27 @@ matrix:
dist: xenial
sudo: required
services: docker
env: DOCKER_IMAGE_TAG=swift:5.3.1-bionic ONLY_RUN_SWIFT_LINT=no USE_APT_GET=yes
env: DOCKER_IMAGE_TAG=swift:5.3.3-bionic ONLY_RUN_SWIFT_LINT=no USE_APT_GET=yes
- os: linux
dist: xenial
sudo: required
services: docker
env: DOCKER_IMAGE_TAG=swift:5.3.1-xenial ONLY_RUN_SWIFT_LINT=no USE_APT_GET=yes
env: DOCKER_IMAGE_TAG=swift:5.3.3-xenial ONLY_RUN_SWIFT_LINT=no USE_APT_GET=yes
- os: linux
dist: xenial
sudo: required
services: docker
env: DOCKER_IMAGE_TAG=swift:5.3.1-focal ONLY_RUN_SWIFT_LINT=no USE_APT_GET=yes
env: DOCKER_IMAGE_TAG=swift:5.3.3-focal ONLY_RUN_SWIFT_LINT=no USE_APT_GET=yes
- os: linux
dist: xenial
sudo: required
services: docker
env: DOCKER_IMAGE_TAG=swift:5.3.1-amazonlinux2 ONLY_RUN_SWIFT_LINT=no USE_APT_GET=no
env: DOCKER_IMAGE_TAG=swift:5.3.3-amazonlinux2 ONLY_RUN_SWIFT_LINT=no USE_APT_GET=no
- os: linux
dist: xenial
sudo: required
services: docker
env: DOCKER_IMAGE_TAG=swift:5.3.1-centos8 ONLY_RUN_SWIFT_LINT=no USE_APT_GET=no
env: DOCKER_IMAGE_TAG=swift:5.3.3-centos8 ONLY_RUN_SWIFT_LINT=no USE_APT_GET=no

- os: linux
dist: xenial
Expand All @@ -39,6 +39,20 @@ matrix:
services: docker
env: DOCKER_IMAGE_TAG=swift:5.1-bionic ONLY_RUN_SWIFT_LINT=no USE_APT_GET=yes

# Verify against nightly of upcoming Swift 5.4
- os: linux
dist: xenial
sudo: required
services: docker
env: DOCKER_IMAGE_TAG=swiftlang/swift:nightly-5.4-amazonlinux2 ONLY_RUN_SWIFT_LINT=no USE_APT_GET=no

# Verify against nightly of Swift mainline
- os: linux
dist: xenial
sudo: required
services: docker
env: DOCKER_IMAGE_TAG=swiftlang/swift:nightly-amazonlinux2 ONLY_RUN_SWIFT_LINT=no USE_APT_GET=no

# Use a docker image that contains the SwiftLint executable the verify the code against the linter.
# - os: linux
# dist: xenial
Expand Down
4 changes: 2 additions & 2 deletions Package.resolved
Original file line number Diff line number Diff line change
Expand Up @@ -15,8 +15,8 @@
"repositoryURL": "https://github.com/amzn/smoke-http.git",
"state": {
"branch": null,
"revision": "6619fe6f8e56a85d9a7d9481c27afb2dc5688459",
"version": "2.7.0"
"revision": "ab695d0c4593c39ac4494ee318703ac329ca2ae8",
"version": "2.8.0"
}
},
{
Expand Down
10 changes: 9 additions & 1 deletion Package.swift
Original file line number Diff line number Diff line change
Expand Up @@ -112,6 +112,9 @@ let package = Package(
.library(
name: "SmokeAWSHttp",
targets: ["SmokeAWSHttp"]),
.library(
name: "_SmokeAWSHttpConcurrency",
targets: ["_SmokeAWSHttpConcurrency"]),
.library(
name: "SmokeAWSMetrics",
targets: ["SmokeAWSMetrics"]),
Expand All @@ -122,7 +125,7 @@ let package = Package(
.package(url: "https://github.com/apple/swift-log", from: "1.0.0"),
.package(url: "https://github.com/apple/swift-metrics.git", "1.0.0"..<"3.0.0"),
.package(url: "https://github.com/LiveUI/XMLCoding.git", from: "0.4.1"),
.package(url: "https://github.com/amzn/smoke-http.git", from: "2.7.0"),
.package(url: "https://github.com/amzn/smoke-http.git", from: "2.8.0"),
.package(url: "https://github.com/apple/swift-crypto.git", from: "1.0.0"),
],
targets: [
Expand Down Expand Up @@ -271,6 +274,11 @@ let package = Package(
.product(name: "HTTPHeadersCoding", package: "smoke-http"),
.product(name: "Crypto", package: "swift-crypto"),
]),
.target(
name: "_SmokeAWSHttpConcurrency", dependencies: [
.target(name: "SmokeAWSHttp"),
.product(name: "_SmokeHTTPClientConcurrency", package: "smoke-http"),
]),
.target(
name: "SmokeAWSMetrics", dependencies: [
.product(name: "Logging", package: "swift-log"),
Expand Down
20 changes: 15 additions & 5 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@
<img src="https://travis-ci.com/amzn/smoke-aws.svg?branch=master" alt="Build - Master Branch">
</a>
<a href="http://swift.org">
<img src="https://img.shields.io/badge/swift-5.1|5.2|5.3-orange.svg?style=flat" alt="Swift 5.1, 5.2 and 5.3 Tested">
<img src="https://img.shields.io/badge/swift-5.1|5.2|5.3|5.4-orange.svg?style=flat" alt="Swift 5.1, 5.2 and 5.3 Tested">
</a>
<img src="https://img.shields.io/badge/ubuntu-16.04|18.04|20.04-yellow.svg?style=flat" alt="Ubuntu 16.04, 18.04 and 20.04 Tested">
<img src="https://img.shields.io/badge/CentOS-8-yellow.svg?style=flat" alt="CentOS 8 Tested">
Expand Down Expand Up @@ -136,15 +136,25 @@ import SmokeAWSCredentials
guard let credentialsProvider = AwsContainerRotatingCredentials.getCredentials(fromEnvironment: environment) else {
return Log.error("Unable to obtain credentials from the container environment.")
}

// optional: for the EC2 clients, only emit the retry count metric
// only report 5XX error counts for DescribeInstances (even if additional operations are added in the future)
// only report 4XX error counts for operations other than DescribeInstances (including if they are added in the future)
let reportingConfiguration = SmokeAWSClientReportingConfiguration<ElasticComputeCloudModelOperations>(
successCounterMatchingOperations: .none,
failure5XXCounterMatchingRequests: .onlyForOperations([.describeInstances]),
failure4XXCounterMatchingRequests: .exceptForOperations([.describeInstances]),
retryCountRecorderMatchingOperations: .all,
latencyTimerMatchingOperations: .none)

self.ec2ClientGenerator = AWSElasticComputeCloudClientGenerator(
credentialsProvider: credentialsProvider,
awsRegion: region,
endpointHostName: ec2EndpointHostName,
connectionTimeoutSeconds: connectionTimeoutSeconds,
retryConfiguration: retryConfiguration,
eventLoopProvider: .createNew,
reportingConfiguration: reportingConfiguration)
connectionTimeoutSeconds: connectionTimeoutSeconds, // optional
retryConfiguration: retryConfiguration, // optional
eventLoopProvider: .createNew, // optional
reportingConfiguration: reportingConfiguration) // optional
```

The inputs to this constructor are-
Expand Down
10 changes: 10 additions & 0 deletions Sources/SmokeAWSCore/SmokeAWSClientReportingConfiguration.swift
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,8 @@ import Foundation

public struct SmokeAWSClientReportingConfiguration<OperationIdentifer: Hashable> {

// TODO: Remove non-inclusive language
// https://github.com/amzn/smoke-aws/issues/84
public enum MatchingOperations {
case all
case whitelist(Set<OperationIdentifer>)
Expand Down Expand Up @@ -60,6 +62,14 @@ public struct SmokeAWSClientReportingConfiguration<OperationIdentifer: Hashable>
return .init(matchingOperations: .all)
}

public static func onlyForOperations(_ operations: Set<OperationIdentifer>) -> Self {
return .init(matchingOperations: .whitelist(operations))
}

public static func exceptForOperations(_ operations: Set<OperationIdentifer>) -> Self {
return .init(matchingOperations: .blacklist(operations))
}

public func reportSuccessForOperation(_ operation: OperationIdentifer) -> Bool {
return isMatchingOperation(operation, matchingOperations: successCounterMatchingOperations)
}
Expand Down
107 changes: 107 additions & 0 deletions Sources/_SmokeAWSHttpConcurrency/AWSClientProtocol.swift
Original file line number Diff line number Diff line change
@@ -0,0 +1,107 @@
//
// AWSClientProtocol.swift
//

#if compiler(>=5.5) && $AsyncAwait

import SmokeHTTPClient
import SmokeAWSCore
import NIO
import NIOHTTP1
import NIOTransportServices
import AsyncHTTPClient
import SmokeAWSHttp
import _SmokeHTTPClientConcurrency

public extension AWSClientProtocol {
func executeWithoutOutput<InvocationReportingType: HTTPClientInvocationReporting,
InputType: HTTPRequestInputProtocol, ErrorType: ConvertableError>(
httpClient: HTTPOperationsClient,
endpointPath: String = "/",
httpMethod: HTTPMethod = .POST,
requestInput: InputType,
operation: String,
reporting: InvocationReportingType,
signAllHeaders: Bool = false,
errorType: ErrorType.Type) async throws {
let handlerDelegate = AWSClientInvocationDelegate(
credentialsProvider: credentialsProvider,
awsRegion: awsRegion,
service: service,
operation: operation,
target: target,
signAllHeaders: signAllHeaders)

let invocationContext = HTTPClientInvocationContext(reporting: reporting,
handlerDelegate: handlerDelegate)

do {
return try await httpClient.executeRetriableWithoutOutput(
endpointPath: endpointPath,
httpMethod: httpMethod,
input: requestInput,
invocationContext: invocationContext,
retryConfiguration: retryConfiguration,
retryOnError: retryOnErrorProvider)
} catch {
let typedError: ErrorType = error.asTypedError()
throw typedError
}
}

func executeWithOutput<OutputType: HTTPResponseOutputProtocol, InvocationReportingType: HTTPClientInvocationReporting,
InputType: HTTPRequestInputProtocol, ErrorType: ConvertableError>(
httpClient: HTTPOperationsClient,
endpointPath: String = "/",
httpMethod: HTTPMethod = .POST,
requestInput: InputType,
operation: String,
reporting: InvocationReportingType,
signAllHeaders: Bool = false,
errorType: ErrorType.Type) async throws -> OutputType {
let handlerDelegate = AWSClientInvocationDelegate(
credentialsProvider: credentialsProvider,
awsRegion: awsRegion,
service: service,
operation: operation,
target: target,
signAllHeaders: signAllHeaders)

let invocationContext = HTTPClientInvocationContext(reporting: reporting,
handlerDelegate: handlerDelegate)

do {
return try await httpClient.executeRetriableWithOutput(
endpointPath: endpointPath,
httpMethod: httpMethod,
input: requestInput,
invocationContext: invocationContext,
retryConfiguration: retryConfiguration,
retryOnError: retryOnErrorProvider)
} catch {
let typedError: ErrorType = error.asTypedError()
throw typedError
}
}
}

public struct AWSClientHelper {
public static func getEventLoop(eventLoopGroupProvider: HTTPClient.EventLoopGroupProvider) -> EventLoopGroup {
switch eventLoopGroupProvider {
case .shared(let group):
return group
case .createNew:
#if canImport(Network)
if #available(OSX 10.14, iOS 12.0, tvOS 12.0, watchOS 6.0, *) {
return NIOTSEventLoopGroup()
} else {
return MultiThreadedEventLoopGroup(numberOfThreads: 1)
}
#else
return MultiThreadedEventLoopGroup(numberOfThreads: 1)
#endif
}
}
}

#endif
85 changes: 85 additions & 0 deletions Sources/_SmokeAWSHttpConcurrency/AWSQueryClientProtocol.swift
Original file line number Diff line number Diff line change
@@ -0,0 +1,85 @@
//
// AWSClientProtocol.swift
//

#if compiler(>=5.5) && $AsyncAwait

import SmokeHTTPClient
import SmokeAWSCore
import NIO
import SmokeAWSHttp
import _SmokeHTTPClientConcurrency

public extension AWSQueryClientProtocol {
func executeWithoutOutput<InvocationReportingType: HTTPClientInvocationReporting,
WrappedInputType: HTTPRequestInputProtocol, ErrorType: ConvertableError>(
httpClient: HTTPOperationsClient,
wrappedInput: WrappedInputType,
action: String,
reporting: InvocationReportingType,
errorType: ErrorType.Type) async throws {
let handlerDelegate = AWSClientInvocationDelegate(
credentialsProvider: credentialsProvider,
awsRegion: awsRegion,
service: service,
target: target)

let invocationContext = HTTPClientInvocationContext(reporting: reporting,
handlerDelegate: handlerDelegate)

let requestInput = QueryWrapperHTTPRequestInput(
wrappedInput: wrappedInput,
action: action,
version: apiVersion)

do {
return try await httpClient.executeRetriableWithoutOutput(
endpointPath: "/",
httpMethod: .POST,
input: requestInput,
invocationContext: invocationContext,
retryConfiguration: retryConfiguration,
retryOnError: retryOnErrorProvider)
} catch {
let typedError: ErrorType = error.asTypedError()
throw typedError
}
}

func executeWithOutput<OutputType: HTTPResponseOutputProtocol, InvocationReportingType: HTTPClientInvocationReporting,
WrappedInputType: HTTPRequestInputProtocol, ErrorType: ConvertableError>(
httpClient: HTTPOperationsClient,
wrappedInput: WrappedInputType,
action: String,
reporting: InvocationReportingType,
errorType: ErrorType.Type) async throws -> OutputType {
let handlerDelegate = AWSClientInvocationDelegate(
credentialsProvider: credentialsProvider,
awsRegion: awsRegion,
service: service,
target: target)

let invocationContext = HTTPClientInvocationContext(reporting: reporting,
handlerDelegate: handlerDelegate)

let requestInput = QueryWrapperHTTPRequestInput(
wrappedInput: wrappedInput,
action: action,
version: apiVersion)

do {
return try await httpClient.executeRetriableWithOutput(
endpointPath: "/",
httpMethod: .POST,
input: requestInput,
invocationContext: invocationContext,
retryConfiguration: retryConfiguration,
retryOnError: retryOnErrorProvider)
} catch {
let typedError: ErrorType = error.asTypedError()
throw typedError
}
}
}

#endif
9 changes: 5 additions & 4 deletions docs/Support_Policy.md
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
---
date: 2020-12-11 13:00
date: 2021-04-26 15:00
description: Support Policy for SmokeAWS.
tags: support policy
---
Expand All @@ -25,9 +25,10 @@ Once support has been removed, testing via continuous integration may be removed

Following these rules, the support level for different Swift Toolchain versions are-
1. **Swift 5.0 and earlier**: No support.
5. **Swift 5.1**: Supported for SmokeAWS 2.x. Support to be removed after 12th of May, 2021.
6. **Swift 5.2**: Supported for SmokeAWS 2.x.
7. **Swift 5.3**: Supported for SmokeAWS 2.x.
2. **Swift 5.1**: Supported for SmokeAWS 2.x. Support to be removed after 12th of May, 2021.
3. **Swift 5.2**: Supported for SmokeAWS 2.x. Support to be removed after 26th of October, 2021.
4. **Swift 5.3**: Supported for SmokeAWS 2.x.
5. **Swift 5.4**: Supported for SmokeAWS 2.x.

# Runtime Support

Expand Down

0 comments on commit b1d157e

Please sign in to comment.