Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
10 changes: 9 additions & 1 deletion .github/workflows/test.yml
Original file line number Diff line number Diff line change
Expand Up @@ -20,11 +20,19 @@ jobs:
steps:
- name: Checkout code
uses: actions/checkout@v4
with:
submodules: true
token: ${{ secrets.PAT_FOR_PRIVATE_RUBY }}

- name: Set up Python
uses: actions/setup-python@v5
with:
python-version: ${{ inputs.python-version || '3.11' }}

- name: Set up Ruby
uses: ruby/setup-ruby@v1
with:
ruby-version: '3.4'

- name: Set up PHP with Composer
uses: shivammathur/setup-php@v2
Expand Down Expand Up @@ -61,7 +69,7 @@ jobs:
path: |
~/.gradle/caches
~/.gradle/wrapper
test-server/java-server/.gradle
test-server/java-v3-server/.gradle
test-server/java-tests/.gradle
key: ${{ runner.os }}-gradle-${{ hashFiles('**/*.gradle*', '**/gradle-wrapper.properties') }}
restore-keys: |
Expand Down
6 changes: 6 additions & 0 deletions .gitmodules
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
[submodule "test-server/ruby-v2-server/local-ruby-sdk"]
path = test-server/ruby-v2-server/local-ruby-sdk
Comment thread
seebees marked this conversation as resolved.
url = git@github.com:aws/aws-sdk-ruby-staging.git
[submodule "test-server/ruby-v3-server/local-ruby-sdk"]
path = test-server/ruby-v3-server/local-ruby-sdk
url = git@github.com:aws/aws-sdk-ruby-staging.git
32 changes: 27 additions & 5 deletions test-server/Makefile
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,8 @@ ci: start-servers run-tests stop-servers

SERVER_DIRS := $(shell find . -maxdepth 1 -type d -name '*-server' | sed 's|^\./||' | sort)

SERVER_TARGETS := $(addprefix start-, $(SERVER_DIRS))
START_SERVER_TARGETS := $(addprefix start-, $(SERVER_DIRS))
WAIT_SERVER_TARGETS := $(addprefix wait-, $(SERVER_DIRS))

# Start all servers in parallel
start-servers:
Expand All @@ -22,14 +23,25 @@ start-servers:
$(MAKE) -C $$dir wait-for-server; \
done

start-all-servers: $(SERVER_TARGETS)
start-all-servers: $(START_SERVER_TARGETS)

$(SERVER_TARGETS): start-%:
$(START_SERVER_TARGETS): start-%:
@if [ -f $*/Makefile ]; then \
echo "Starting server in $*..."; \
$(MAKE) -C $* start-server; \
else \
echo "❌ Error: no Makefile found in $$dir"; \
echo "❌ Error: no Makefile found in $*"; \
exit 1; \
fi; \

wait-all-servers: $(WAIT_SERVER_TARGETS)

$(WAIT_SERVER_TARGETS): wait-%:
@if [ -f $*/Makefile ]; then \
echo "Waiting server in $*..."; \
$(MAKE) -C $* wait-for-server; \
else \
echo "❌ Error: no Makefile found in $*"; \
exit 1; \
fi; \

Expand All @@ -44,7 +56,7 @@ run-tests:
AWS_SECRET_ACCESS_KEY="$$AWS_SECRET_ACCESS_KEY" \
AWS_SESSION_TOKEN="$$AWS_SESSION_TOKEN" \
AWS_REGION="us-west-2" \
./gradlew --build-cache --rerun-tasks --parallel integ
./gradlew --build-cache --parallel integ -Dtest.filter.servers="$(FILTER)"
@echo "Tests completed successfully"

# Stop the servers
Expand Down Expand Up @@ -96,3 +108,13 @@ wait-for-port:
echo "Waiting for $$PORT to start ($$i/$(TIMEOUT))..."; \
sleep 1; \
done

# Here are some helpful curl commands
# that you can use to test specific test servers:
test-create-client:
@echo $(PORT)
@curl -X POST \
-H "Content-Type: application/json" \
-H "User-Agent: smithy-java/0.0.3 ua/2.1 os/macos#15.5 lang/java#23.0.2" \
-d '{"config":{"enableLegacyUnauthenticatedModes":false,"enableDelayedAuthenticationMode":false,"enableLegacyWrappingAlgorithms":false,"keyMaterial":{"kmsKeyId":"arn:aws:kms:us-west-2:370957321024:alias/S3EC-Test-Server-Github-KMS-Key"}}}' \
http://localhost:$(PORT)/client
11 changes: 11 additions & 0 deletions test-server/java-tests/build.gradle.kts
Original file line number Diff line number Diff line change
Expand Up @@ -48,6 +48,17 @@ tasks {
classpath = sourceSets["it"].runtimeClasspath
outputs.upToDateWhen { false }
outputs.cacheIf { false }
// Passing information from Gradle into the tests so that we can filter our servers
systemProperty("test.filter.servers", System.getProperty("test.filter.servers"))
// For debugging
// // Enable System.out output
// testLogging {
// events("passed", "skipped", "failed", "standardOut", "standardError")
// showStandardStreams = true
// }

// // Disable AWS SDK v1 deprecation warnings
// systemProperty("aws.java.v1.disableDeprecationAnnouncement", "true")
}
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -14,11 +14,14 @@
import java.nio.ByteBuffer;
import java.nio.charset.StandardCharsets;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.HashMap;
import java.util.LinkedHashMap;
import java.util.List;
import java.util.Map;
import java.util.Objects;
import java.util.Set;
import java.util.stream.Collectors;
import java.util.stream.Stream;

import com.amazonaws.services.s3.model.KMSEncryptionMaterials;
Expand Down Expand Up @@ -61,8 +64,9 @@ public class RoundTripTests {
private static final String NET_V3 = "NET-V3";
private static final String PHP_V2 = "PHP-V2";
private static final String PHP_V3 = "PHP-V3";
private static final String RUBY_V2 = "Ruby-V2";
private static final String RUBY_V3 = "Ruby-V3";

private static final List<LanguageServerTarget> serverList;
private static final Map<String, LanguageServerTarget> serverMap;

private static final String KMS_KEY_ARN = System.getenv("TEST_SERVER_KMS_KEY_ARN") != null ?
Expand All @@ -72,25 +76,45 @@ public class RoundTripTests {
System.getenv("TEST_SERVER_S3_BUCKET") : "s3ec-test-server-github-bucket";

static {
serverList = new ArrayList<>(14);
serverList.add(new LanguageServerTarget(JAVA_V3, "8080"));
serverList.add(new LanguageServerTarget(PYTHON_V3, "8081"));
serverList.add(new LanguageServerTarget(GO_V3, "8082"));
serverList.add(new LanguageServerTarget(NET_V2, "8083"));
serverList.add(new LanguageServerTarget(NET_V3, "8084"));
serverList.add(new LanguageServerTarget(PHP_V2, "8087"));
serverList.add(new LanguageServerTarget(PHP_V3, "8093"));

serverMap = new HashMap<>(14);
serverMap.put(JAVA_V3, new LanguageServerTarget(JAVA_V3, "8080"));
serverMap.put(PYTHON_V3, new LanguageServerTarget(PYTHON_V3, "8081"));
serverMap.put(GO_V3, new LanguageServerTarget(GO_V3, "8082"));
serverMap.put(NET_V2, new LanguageServerTarget(NET_V2, "8083"));
serverMap.put(NET_V3, new LanguageServerTarget(NET_V3, "8084"));
serverMap.put(PHP_V2, new LanguageServerTarget(PHP_V2, "8087"));
serverMap.put(PHP_V3, new LanguageServerTarget(PHP_V3, "8093"));
final Map<String, LanguageServerTarget> servers = new LinkedHashMap<>();
servers.put(JAVA_V3, new LanguageServerTarget(JAVA_V3, "8080"));
servers.put(PYTHON_V3, new LanguageServerTarget(PYTHON_V3, "8081"));
servers.put(GO_V3, new LanguageServerTarget(GO_V3, "8082"));
servers.put(NET_V2, new LanguageServerTarget(NET_V2, "8083"));
servers.put(NET_V3, new LanguageServerTarget(NET_V3, "8084"));
servers.put(PHP_V2, new LanguageServerTarget(PHP_V2, "8087"));
servers.put(PHP_V3, new LanguageServerTarget(PHP_V3, "8093"));
servers.put(RUBY_V2, new LanguageServerTarget(RUBY_V2, "8086"));
servers.put(RUBY_V3, new LanguageServerTarget(RUBY_V3, "8092"));

serverMap = filterServers(servers);
}

private static Map<String, LanguageServerTarget> filterServers(Map<String, LanguageServerTarget> allServers) {

final String maybeFilter = System.getProperty("test.filter.servers");
if (maybeFilter == null || maybeFilter.trim().isEmpty()) {
return allServers; // No filtering - use all servers
}

final String[] filters = Arrays.stream(maybeFilter.split(","))
.map(String::trim)
.map(String::toLowerCase)
.toArray(String[]::new);

return allServers.entrySet().stream()
.filter(entry -> {
String key = entry.getKey().toLowerCase();
return Arrays.stream(filters).anyMatch(key::contains);
})
.collect(Collectors.toMap(
Map.Entry::getKey,
Map.Entry::getValue,
(e1, e2) -> e1, // merge function (not really needed)
LinkedHashMap::new // preserve order
));
}

// Encryption context validation behavior varies by implementation:
// - Go: Does not validate encryption context on decrypt operations
// - .NET: Only validates against encryption context stored in the object metadata
Expand Down Expand Up @@ -149,7 +173,7 @@ public String toString() {
@BeforeAll
public static void setup() {
// Wait for servers to start
for (LanguageServerTarget server : serverList) {
for (LanguageServerTarget server : serverMap.values()) {
if (!serverListening(server.getServerURI())) {
throw new RuntimeException(String.format("Test Server for %s is not running at endpoint: %s", server.getLanguageName(), server.getServerURI()));
}
Expand Down Expand Up @@ -179,14 +203,14 @@ static S3ECTestServerClient testServerClientFor(LanguageServerTarget server) {
}

static Stream<Arguments> clientsForTest() {
return serverList.stream()
return serverMap.values().stream()
.map(LanguageServerTarget::getLanguageName)
.map(Arguments::of);
}

static Stream<Arguments> crossLanguageClients() {
return serverList.stream()
.flatMap(t1 -> serverList.stream()
return serverMap.values().stream()
.flatMap(t1 -> serverMap.values().stream()
.flatMap(t2 -> Stream.of(
Arguments.of(t1, t2)
)));
Expand Down Expand Up @@ -337,7 +361,11 @@ public void crossLanguageTestKmsWithSubsetEncCtxFails(LanguageServerTarget encLa
.build());
fail("Expected exception!");
} catch (S3EncryptionClientError e) {
assertTrue(e.getMessage().contains("Provided encryption context does not match information retrieved from S3"));
if (decLang.languageName.equals(RUBY_V3) || decLang.languageName.equals(RUBY_V2)) {
assertTrue(e.getMessage().contains("Value of encryption context from envelope does not match the provided encryption context"));
} else {
assertTrue(e.getMessage().contains("Provided encryption context does not match information retrieved from S3"));
}
}
}

Expand Down Expand Up @@ -389,7 +417,11 @@ public void crossLanguageTestKmsWithIncorrectEncCtxFails(LanguageServerTarget en
.build());
fail("Expected exception!");
} catch (S3EncryptionClientError e) {
assertTrue(e.getMessage().contains("Provided encryption context does not match information retrieved from S3"));
if (decLang.languageName.equals(RUBY_V3) || decLang.languageName.equals(RUBY_V2)) {
assertTrue(e.getMessage().contains("Value of encryption context from envelope does not match the provided encryption context"));
} else {
assertTrue(e.getMessage().contains("Provided encryption context does not match information retrieved from S3"));
}
}
}

Expand Down Expand Up @@ -529,6 +561,8 @@ public void kmsV1LegacyFailsWhenLegacyDisabled(String language) {
assertTrue(e.getMessage().contains(
"The requested object is encrypted with V1 encryption schemas that have been disabled by client configuration V2."
));
} else if (language.equals(RUBY_V3) || language.equals(RUBY_V2)) {
assertTrue(e.getMessage().contains("The requested object is encrypted with V1 encryption schemas that have been disabled by client configuration security_profile = :v2. Retry with :v2_and_legacy or re-encrypt the object."));
} else {
assertTrue(e.getMessage().contains("Enable legacy wrapping algorithms to use legacy key wrapping algorithm: kms"));
}
Expand Down
2 changes: 2 additions & 0 deletions test-server/ruby-v2-server/.gitignore
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
vendor
server.pid
15 changes: 15 additions & 0 deletions test-server/ruby-v2-server/Gemfile
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
source 'https://rubygems.org'

ruby '~> 3.0'

gem 'sinatra', '~> 3.0'
gem 'puma', '~> 6.0'
gem 'aws-sdk-s3', path: 'local-ruby-sdk/gems/aws-sdk-s3'
gem 'aws-sdk-kms', path: 'local-ruby-sdk/gems/aws-sdk-kms'
gem 'json', '~> 2.0'
gem 'concurrent-ruby', '~> 1.0'
gem 'nokogiri', '~> 1.13'

group :development do
gem 'rubocop', '~> 1.0'
end
102 changes: 102 additions & 0 deletions test-server/ruby-v2-server/Gemfile.lock
Original file line number Diff line number Diff line change
@@ -0,0 +1,102 @@
PATH
remote: local-ruby-sdk/gems/aws-sdk-kms
specs:
aws-sdk-kms (1.112.0)
aws-sdk-core (~> 3, >= 3.231.0)
aws-sigv4 (~> 1.5)

PATH
remote: local-ruby-sdk/gems/aws-sdk-s3
specs:
aws-sdk-s3 (1.199.0)
aws-sdk-core (~> 3, >= 3.231.0)
aws-sdk-kms (~> 1)
aws-sigv4 (~> 1.5)

GEM
remote: https://rubygems.org/
specs:
ast (2.4.3)
aws-eventstream (1.4.0)
aws-partitions (1.1160.0)
aws-sdk-core (3.232.0)
aws-eventstream (~> 1, >= 1.3.0)
aws-partitions (~> 1, >= 1.992.0)
aws-sigv4 (~> 1.9)
base64
bigdecimal
jmespath (~> 1, >= 1.6.1)
logger
aws-sigv4 (1.12.1)
aws-eventstream (~> 1, >= 1.0.2)
base64 (0.3.0)
bigdecimal (3.2.3)
concurrent-ruby (1.3.5)
jmespath (1.6.2)
json (2.13.2)
language_server-protocol (3.17.0.5)
lint_roller (1.1.0)
logger (1.7.0)
mustermann (3.0.4)
ruby2_keywords (~> 0.0.1)
nio4r (2.7.4)
nokogiri (1.18.10-arm64-darwin)
racc (~> 1.4)
parallel (1.27.0)
parser (3.3.9.0)
ast (~> 2.4.1)
racc
prism (1.5.1)
puma (6.6.1)
nio4r (~> 2.0)
racc (1.8.1)
rack (2.2.17)
rack-protection (3.2.0)
base64 (>= 0.1.0)
rack (~> 2.2, >= 2.2.4)
rainbow (3.1.1)
regexp_parser (2.11.3)
rubocop (1.80.2)
json (~> 2.3)
language_server-protocol (~> 3.17.0.2)
lint_roller (~> 1.1.0)
parallel (~> 1.10)
parser (>= 3.3.0.2)
rainbow (>= 2.2.2, < 4.0)
regexp_parser (>= 2.9.3, < 3.0)
rubocop-ast (>= 1.46.0, < 2.0)
ruby-progressbar (~> 1.7)
unicode-display_width (>= 2.4.0, < 4.0)
rubocop-ast (1.46.0)
parser (>= 3.3.7.2)
prism (~> 1.4)
ruby-progressbar (1.13.0)
ruby2_keywords (0.0.5)
sinatra (3.2.0)
mustermann (~> 3.0)
rack (~> 2.2, >= 2.2.4)
rack-protection (= 3.2.0)
tilt (~> 2.0)
tilt (2.6.1)
unicode-display_width (3.2.0)
unicode-emoji (~> 4.1)
unicode-emoji (4.1.0)

PLATFORMS
arm64-darwin-24

DEPENDENCIES
aws-sdk-kms!
aws-sdk-s3!
concurrent-ruby (~> 1.0)
json (~> 2.0)
nokogiri (~> 1.13)
puma (~> 6.0)
rubocop (~> 1.0)
sinatra (~> 3.0)

RUBY VERSION
ruby 3.4.5p51

BUNDLED WITH
2.6.9
Loading
Loading