Skip to content

Commit 1f6b443

Browse files
Merge branch 'main' into topic/kiryazovi-redis/use-docker-for-tests
2 parents b9dceb8 + 319e315 commit 1f6b443

22 files changed

+978
-149
lines changed

.github/workflows/docs.yml

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -10,11 +10,11 @@ concurrency:
1010
group: "pages"
1111
cancel-in-progress: false
1212
jobs:
13-
build-and-deploy:
13+
publish-docs:
1414
concurrency: ci-${{ github.ref }}
1515
runs-on: ubuntu-latest
1616
steps:
17-
- uses: actions/checkout@v3
17+
- uses: actions/checkout@v4
1818
- uses: actions/setup-python@v4
1919
with:
2020
python-version: 3.9
@@ -26,11 +26,11 @@ jobs:
2626
run: |
2727
mkdocs build -d docsbuild
2828
- name: Setup Pages
29-
uses: actions/configure-pages@v3
29+
uses: actions/configure-pages@v4
3030
- name: Upload artifact
31-
uses: actions/upload-pages-artifact@v1
31+
uses: actions/upload-pages-artifact@v3
3232
with:
3333
path: 'docsbuild'
3434
- name: Deploy to GitHub Pages
3535
id: deployment
36-
uses: actions/deploy-pages@v2
36+
uses: actions/deploy-pages@v4

.github/workflows/integration.yml

Lines changed: 25 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -15,23 +15,35 @@ on:
1515
schedule:
1616
- cron: '0 1 * * *' # nightly build
1717
workflow_dispatch:
18-
inputs:
19-
redis_version:
20-
description: "Redis stack version to use for testing"
21-
required: false
22-
default: "8.0-M02"
23-
type: choice
24-
options:
25-
- "8.0-M02"
26-
- "rs-7.4.0-v1"
27-
- "rs-7.2.0-v13"
2818

2919
jobs:
30-
3120
build:
3221
name: Build and Test
3322
runs-on: ubuntu-24.04
23+
strategy:
24+
fail-fast: false
25+
matrix:
26+
redis_version:
27+
- "unstable"
28+
- "8.0"
29+
- "7.4"
30+
- "7.2"
31+
3432
steps:
33+
- name: Test Redis Server Version
34+
id: map-tags
35+
run: |
36+
# Map requested version to github or tag
37+
case "${{ matrix.redis_version }}" in
38+
"unstable") redis_branch="unstable" stack_version="8.0-M04-pre" ;;
39+
"8.0") redis_branch="8.0" stack_version="8.0-M04-pre" ;;
40+
"7.4") redis_branch="7.4" stack_version="rs-7.4.0-v2" ;;
41+
"7.2") redis_branch="7.2" stack_version="rs-7.2.0-v14" ;;
42+
*) echo "Unsupported version: ${{ matrix.redis_version }}" && exit 1 ;;
43+
esac
44+
# Save them as outputs for later use
45+
echo "redis_branch=$redis_branch" >> $GITHUB_OUTPUT
46+
echo "redis_stack_version=$stack_version" >> $GITHUB_OUTPUT
3547
- name: Checkout project
3648
uses: actions/checkout@v4
3749
- name: Set Java up in the runner
@@ -61,7 +73,8 @@ jobs:
6173
run: |
6274
make test-coverage
6375
env:
64-
REDIS_STACK_VERSION: ${{ inputs.redis_version || '8.0-M02' }}
76+
REDIS: ${{ steps.map-tags.outputs.redis_branch }}
77+
REDIS_STACK_VERSION: ${{ steps.map-tags.outputs.redis_stack_version }}
6578
JVM_OPTS: -Xmx3200m
6679
TERM: dumb
6780
- name: Upload coverage reports to Codecov

pom.xml

Lines changed: 37 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -13,7 +13,7 @@
1313
and much more.
1414
</description>
1515

16-
<url>http://github.com/lettuce-io/lettuce-core</url>
16+
<url>https://github.com/redis/lettuce</url>
1717

1818
<organization>
1919
<name>lettuce.io</name>
@@ -30,7 +30,7 @@
3030

3131
<issueManagement>
3232
<system>Github</system>
33-
<url>https://github.com/lettuce-io/lettuce-core/issues</url>
33+
<url>https://github.com/redis/lettuce/issues</url>
3434
</issueManagement>
3535

3636
<developers>
@@ -82,10 +82,10 @@
8282
</properties>
8383

8484
<scm>
85-
<connection>scm:git:https://github.com/lettuce-io/lettuce-core.git</connection>
86-
<developerConnection>scm:git:https://github.com/lettuce-io/lettuce-core.git
85+
<connection>scm:git:https://github.com/redis/lettuce.git</connection>
86+
<developerConnection>scm:git:https://github.com/redis/lettuce.git
8787
</developerConnection>
88-
<url>http://github.com/lettuce-io/lettuce-core</url>
88+
<url>https://github.com/redis/lettuce</url>
8989
<tag>HEAD</tag>
9090
</scm>
9191

@@ -189,12 +189,6 @@
189189
<version>0.1.1-beta1</version>
190190
<scope>test</scope>
191191
</dependency>
192-
<dependency>
193-
<groupId>io.github.cdimascio</groupId>
194-
<artifactId>dotenv-java</artifactId>
195-
<version>2.2.0</version>
196-
<scope>test</scope>
197-
</dependency>
198192
<!-- Start of core dependencies -->
199193

200194
<dependency>
@@ -1044,6 +1038,38 @@
10441038
<profile>
10451039
<id>ci</id>
10461040
</profile>
1041+
<profile>
1042+
<id>entraid-it</id>
1043+
<build>
1044+
<plugins>
1045+
<plugin>
1046+
<artifactId>maven-surefire-plugin</artifactId>
1047+
<configuration>
1048+
<skipTests>true</skipTests>
1049+
</configuration>
1050+
</plugin>
1051+
<plugin>
1052+
<artifactId>maven-failsafe-plugin</artifactId>
1053+
<configuration>
1054+
<groups>entraid</groups>
1055+
<skipITs>false</skipITs>
1056+
<includes>
1057+
<include>**/*IntegrationTests</include>
1058+
</includes>
1059+
</configuration>
1060+
<executions>
1061+
<execution>
1062+
<phase>integration-test</phase>
1063+
<goals>
1064+
<goal>integration-test</goal>
1065+
<goal>verify</goal>
1066+
</goals>
1067+
</execution>
1068+
</executions>
1069+
</plugin>
1070+
</plugins>
1071+
</build>
1072+
</profile>
10471073

10481074
<profile>
10491075

src/main/java/io/lettuce/core/AclCategory.java

Lines changed: 41 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -111,5 +111,45 @@ public enum AclCategory {
111111
/**
112112
* scripting command
113113
*/
114-
SCRIPTING
114+
SCRIPTING,
115+
116+
/**
117+
* bloom command
118+
*/
119+
BLOOM,
120+
121+
/**
122+
* cuckoo command
123+
*/
124+
CUCKOO,
125+
126+
/**
127+
* count-min-sketch command
128+
*/
129+
CMS,
130+
131+
/**
132+
* top-k command
133+
*/
134+
TOPK,
135+
136+
/**
137+
* t-digest command
138+
*/
139+
TDIGEST,
140+
141+
/**
142+
* search command
143+
*/
144+
SEARCH,
145+
146+
/**
147+
* timeseries command
148+
*/
149+
TIMESERIES,
150+
151+
/**
152+
* json command
153+
*/
154+
JSON
115155
}

src/main/java/io/lettuce/core/models/command/CommandDetailParser.java

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -95,6 +95,15 @@ public class CommandDetailParser {
9595
aclCategoriesMap.put("@connection", AclCategory.CONNECTION);
9696
aclCategoriesMap.put("@transaction", AclCategory.TRANSACTION);
9797
aclCategoriesMap.put("@scripting", AclCategory.SCRIPTING);
98+
aclCategoriesMap.put("@bloom", AclCategory.BLOOM);
99+
aclCategoriesMap.put("@cuckoo", AclCategory.CUCKOO);
100+
aclCategoriesMap.put("@cms", AclCategory.CMS);
101+
aclCategoriesMap.put("@topk", AclCategory.TOPK);
102+
aclCategoriesMap.put("@tdigest", AclCategory.TDIGEST);
103+
aclCategoriesMap.put("@search", AclCategory.SEARCH);
104+
aclCategoriesMap.put("@timeseries", AclCategory.TIMESERIES);
105+
aclCategoriesMap.put("@json", AclCategory.JSON);
106+
98107
ACL_CATEGORY_MAPPING = Collections.unmodifiableMap(aclCategoriesMap);
99108
}
100109

src/test/java/io/lettuce/TestTags.java

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -29,4 +29,9 @@ public class TestTags {
2929
*/
3030
public static final String API_GENERATOR = "api_generator";
3131

32+
/**
33+
* Tag for EntraId integration tests (require a running environment with configured microsoft EntraId authentication)
34+
*/
35+
public static final String ENTRA_ID = "entraid";
36+
3237
}
Lines changed: 140 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,140 @@
1+
package io.lettuce.authx;
2+
3+
import io.lettuce.core.ClientOptions;
4+
import io.lettuce.core.RedisURI;
5+
import io.lettuce.core.SocketOptions;
6+
import io.lettuce.core.TimeoutOptions;
7+
import io.lettuce.core.api.StatefulRedisConnection;
8+
import io.lettuce.core.cluster.ClusterClientOptions;
9+
import io.lettuce.core.cluster.ClusterTopologyRefreshOptions;
10+
import io.lettuce.core.cluster.RedisClusterClient;
11+
import io.lettuce.core.cluster.api.StatefulRedisClusterConnection;
12+
import io.lettuce.core.cluster.api.sync.RedisAdvancedClusterCommands;
13+
import io.lettuce.core.resource.ClientResources;
14+
import io.lettuce.core.resource.DnsResolver;
15+
import io.lettuce.test.env.Endpoints;
16+
import io.lettuce.test.env.Endpoints.Endpoint;
17+
import org.junit.jupiter.api.AfterAll;
18+
import org.junit.jupiter.api.Assumptions;
19+
import org.junit.jupiter.api.BeforeAll;
20+
import org.junit.jupiter.api.Tag;
21+
import org.junit.jupiter.api.Test;
22+
import redis.clients.authentication.core.TokenAuthConfig;
23+
import redis.clients.authentication.entraid.EntraIDTokenAuthConfigBuilder;
24+
25+
import java.time.Duration;
26+
import java.util.ArrayList;
27+
import java.util.HashMap;
28+
import java.util.List;
29+
import java.util.Map;
30+
import java.util.UUID;
31+
import java.util.concurrent.ExecutionException;
32+
33+
import static io.lettuce.TestTags.ENTRA_ID;
34+
import static org.assertj.core.api.Assertions.assertThat;
35+
import static org.assertj.core.api.Assertions.fail;
36+
import static org.junit.jupiter.api.Assumptions.assumeTrue;
37+
38+
@Tag(ENTRA_ID)
39+
public class EntraIdClusterIntegrationTests {
40+
41+
private static final EntraIdTestContext testCtx = EntraIdTestContext.DEFAULT;
42+
43+
private static TokenBasedRedisCredentialsProvider credentialsProvider;
44+
45+
private static RedisClusterClient clusterClient;
46+
47+
private static ClientResources resources;
48+
49+
private static Endpoint cluster;
50+
51+
@BeforeAll
52+
public static void setup() {
53+
cluster = Endpoints.DEFAULT.getEndpoint("cluster-entraid-acl");
54+
if (cluster != null) {
55+
Assumptions.assumeTrue(testCtx.getClientId() != null && testCtx.getClientSecret() != null,
56+
"Skipping EntraID tests. Azure AD credentials not provided!");
57+
58+
// Configure timeout options to assure fast test failover
59+
ClusterClientOptions clientOptions = ClusterClientOptions.builder()
60+
.socketOptions(SocketOptions.builder().connectTimeout(Duration.ofSeconds(1)).build())
61+
.timeoutOptions(TimeoutOptions.enabled(Duration.ofSeconds(1)))
62+
// enable re-authentication
63+
.reauthenticateBehavior(ClientOptions.ReauthenticateBehavior.ON_NEW_CREDENTIALS).build();
64+
65+
TokenAuthConfig tokenAuthConfig = EntraIDTokenAuthConfigBuilder.builder().clientId(testCtx.getClientId())
66+
.secret(testCtx.getClientSecret()).authority(testCtx.getAuthority()).scopes(testCtx.getRedisScopes())
67+
.expirationRefreshRatio(0.0000001F).build();
68+
69+
credentialsProvider = TokenBasedRedisCredentialsProvider.create(tokenAuthConfig);
70+
71+
resources = ClientResources.builder()
72+
// .dnsResolver(DnsResolver.jvmDefault())
73+
.build();
74+
75+
List<RedisURI> seedURI = new ArrayList<>();
76+
for (String addr : cluster.getRawEndpoints().get(0).getAddr()) {
77+
seedURI.add(RedisURI.builder().withAuthentication(credentialsProvider).withHost(addr)
78+
.withPort(cluster.getRawEndpoints().get(0).getPort()).build());
79+
}
80+
81+
clusterClient = RedisClusterClient.create(resources, seedURI);
82+
clusterClient.setOptions(clientOptions);
83+
}
84+
}
85+
86+
@AfterAll
87+
public static void cleanup() {
88+
if (credentialsProvider != null) {
89+
credentialsProvider.close();
90+
}
91+
if (resources != null) {
92+
resources.shutdown();
93+
}
94+
}
95+
96+
// T.1.1
97+
// Verify authentication using Azure AD with service principals using Redis Cluster Client
98+
@Test
99+
public void clusterWithSecret_azureServicePrincipalIntegrationTest() throws ExecutionException, InterruptedException {
100+
assumeTrue(cluster != null, "Skipping EntraID tests. Redis host with enabled EntraId not provided!");
101+
102+
try (StatefulRedisClusterConnection<String, String> defaultConnection = clusterClient.connect()) {
103+
RedisAdvancedClusterCommands<String, String> sync = defaultConnection.sync();
104+
String keyPrefix = UUID.randomUUID().toString();
105+
Map<String, String> mset = prepareMset(keyPrefix);
106+
107+
assertThat(sync.mset(mset)).isEqualTo("OK");
108+
109+
for (String mykey : mset.keySet()) {
110+
assertThat(defaultConnection.sync().get(mykey)).isEqualTo("value-" + mykey);
111+
assertThat(defaultConnection.async().get(mykey).get()).isEqualTo("value-" + mykey);
112+
assertThat(defaultConnection.reactive().get(mykey).block()).isEqualTo("value-" + mykey);
113+
}
114+
assertThat(sync.del(mset.keySet().toArray(new String[0]))).isEqualTo(mset.keySet().size());
115+
116+
// Test connections to each node
117+
defaultConnection.getPartitions().forEach((partition) -> {
118+
StatefulRedisConnection<?, ?> nodeConnection = defaultConnection.getConnection(partition.getNodeId());
119+
assertThat(nodeConnection.sync().ping()).isEqualTo("PONG");
120+
});
121+
122+
defaultConnection.getPartitions().forEach((partition) -> {
123+
StatefulRedisConnection<?, ?> nodeConnection = defaultConnection.getConnection(partition.getUri().getHost(),
124+
partition.getUri().getPort());
125+
assertThat(nodeConnection.sync().ping()).isEqualTo("PONG");
126+
});
127+
}
128+
}
129+
130+
Map<String, String> prepareMset(String keyPrefix) {
131+
Map<String, String> mset = new HashMap<>();
132+
for (char c = 'a'; c <= 'z'; c++) {
133+
String keySuffix = new String(new char[] { c, c, c }); // Generates "aaa", "bbb", etc.
134+
String key = String.format("%s-{%s}", keyPrefix, keySuffix);
135+
mset.put(key, "value-" + key);
136+
}
137+
return mset;
138+
}
139+
140+
}

0 commit comments

Comments
 (0)