Skip to content

Commit d65b85e

Browse files
committed
For now, use local cluster
Signed-off-by: bowenlan-amzn <bowenlan23@gmail.com>
1 parent 1e9d5e4 commit d65b85e

File tree

9 files changed

+253
-25
lines changed

9 files changed

+253
-25
lines changed
Lines changed: 83 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,83 @@
1+
name: Docker Security Test Workflow
2+
on:
3+
pull_request:
4+
branches:
5+
- "*"
6+
push:
7+
branches:
8+
- "*"
9+
10+
jobs:
11+
test:
12+
# This job runs on Linux
13+
runs-on: ubuntu-latest
14+
steps:
15+
- name: Set Up JDK
16+
uses: actions/setup-java@v1
17+
with:
18+
java-version: 17
19+
- name: Checkout Branch
20+
uses: actions/checkout@v2
21+
- name: Build Index Management
22+
run: ./gradlew assemble -Dbuild.snapshot=false
23+
- name: Pull and Run Docker
24+
run: |
25+
plugin=`basename $(ls build/distributions/*.zip)`
26+
list_of_files=`ls`
27+
list_of_all_files=`ls build/distributions/`
28+
version=`echo $plugin|awk -F- '{print $4}'| cut -d. -f 1-3`
29+
plugin_version=`echo $plugin|awk -F- '{print $4}'| cut -d. -f 1-4`
30+
qualifier=`echo $plugin|awk -F- '{print $4}'| cut -d. -f 1-1`
31+
candidate_version=`echo $plugin|awk -F- '{print $5}'| cut -d. -f 1-1`
32+
if qualifier
33+
then
34+
docker_version=$version-$qualifier
35+
else
36+
docker_version=$version
37+
fi
38+
39+
[[ -z $candidate_version ]] && candidate_version=$qualifier && qualifier=""
40+
41+
echo plugin version plugin_version qualifier candidate_version docker_version
42+
echo "($plugin) ($version) ($plugin_version) ($qualifier) ($candidate_version) ($docker_version)"
43+
echo $ls $list_of_all_files
44+
45+
if docker pull opensearchstaging/opensearch:$docker_version
46+
then
47+
echo "FROM opensearchstaging/opensearch:$docker_version" >> Dockerfile
48+
echo "RUN if [ -d /usr/share/opensearch/plugins/opensearch-index-management ]; then /usr/share/opensearch/bin/opensearch-plugin remove opensearch-index-management; fi" >> Dockerfile
49+
echo "ADD build/distributions/$plugin /tmp/" >> Dockerfile
50+
echo "RUN /usr/share/opensearch/bin/opensearch-plugin install --batch file:/tmp/$plugin" >> Dockerfile
51+
echo "RUN echo 'path.repo: ["/usr/share/opensearch/data/repo"]' >> /usr/share/opensearch/config/opensearch.yml" >> Dockerfile
52+
53+
docker build -t opensearch-index-management:test .
54+
echo "imagePresent=true" >> $GITHUB_ENV
55+
else
56+
echo "imagePresent=false" >> $GITHUB_ENV
57+
fi
58+
- name: Run Docker Image
59+
if: env.imagePresent == 'true'
60+
run: |
61+
cd ..
62+
docker run -p 9200:9200 -d -p 9600:9600 -e "discovery.type=single-node" opensearch-index-management:test
63+
sleep 120
64+
- name: Run Index Management Test for security enabled test cases
65+
if: env.imagePresent == 'true'
66+
run: |
67+
cluster_running=`curl -XGET https://localhost:9200/_cat/plugins -u admin:admin --insecure`
68+
echo $cluster_running
69+
security=`curl -XGET https://localhost:9200/_cat/plugins -u admin:admin --insecure |grep opensearch-security|wc -l`
70+
echo $security
71+
if [ $security -gt 0 ]
72+
then
73+
echo "Security plugin is available"
74+
./gradlew integTest -Dtests.rest.cluster=localhost:9200 -Dtests.cluster=localhost:9200 -Dtests.clustername=docker-cluster -Dsecurity=true -Dhttps=true -Duser=admin -Dpassword=admin
75+
else
76+
echo "Security plugin is NOT available skipping this run as tests without security have already been run"
77+
fi
78+
- name: Upload failed logs
79+
uses: actions/upload-artifact@v2
80+
if: failure()
81+
with:
82+
name: logs
83+
path: build/testclusters/integTest-*/logs/*

.github/workflows/security-test-workflow.yml

Lines changed: 7 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -21,8 +21,13 @@ jobs:
2121
# index-management
2222
- name: Checkout Branch
2323
uses: actions/checkout@v2
24-
- name: Run integration tests with security plugin
25-
run: ./gradlew integTest -Dsecurity=true -Dhttps=true
24+
- name: Start cluster with security plugin
25+
run: |
26+
./gradlew run -Dsecurity=true &
27+
sleep 120
28+
- name: Run integration tests
29+
run: |
30+
./gradlew integTestRemote -Dsecurity=true -Dhttps=true -Dtests.rest.cluster="localhost:9200" -Dtests.cluster="localhost:9200" -Dtests.clustername="integTest" -Duser=admin -Dpassword=admin
2631
- name: Upload failed logs
2732
uses: actions/upload-artifact@v2
2833
if: failure()

build.gradle

Lines changed: 119 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -5,15 +5,22 @@
55

66

77
import org.opensearch.gradle.testclusters.OpenSearchCluster
8-
import org.opensearch.gradle.testclusters.TestClusterConfiguration
98
import org.opensearch.gradle.testclusters.StandaloneRestIntegTestTask
109

10+
import javax.net.ssl.HostnameVerifier
11+
import javax.net.ssl.HttpsURLConnection
12+
import javax.net.ssl.SSLContext
13+
import javax.net.ssl.SSLSession
14+
import javax.net.ssl.TrustManager
15+
import javax.net.ssl.X509TrustManager
1116
import java.nio.charset.StandardCharsets
1217
import java.nio.file.Files
18+
import java.security.GeneralSecurityException
19+
import java.security.cert.X509Certificate
1320
import java.util.concurrent.Callable
14-
import java.util.concurrent.TimeUnit
1521
import java.util.function.Predicate
16-
import org.opensearch.gradle.http.WaitForHttpResource
22+
import java.util.stream.Collectors
23+
import java.util.concurrent.TimeUnit
1724

1825

1926
buildscript {
@@ -301,7 +308,7 @@ afterEvaluate {
301308
node.setting("plugins.security.ssl.http.pemtrustedcas_filepath", "root-ca.pem")
302309
node.setting("plugins.security.allow_unsafe_democertificates", "true")
303310
node.setting("plugins.security.allow_default_init_securityindex", "true")
304-
node.setting("plugins.security.authcz.admin_dn", "\n - CN=kirk,OU=client,O=client,L=test, C=de")
311+
node.setting("plugins.security.authcz.admin_dn", "\n - CN=kirk,OU=client,O=client,L=test,C=de")
305312
node.setting("plugins.security.audit.type", "internal_elasticsearch")
306313
node.setting("plugins.security.enable_snapshot_restore_privilege", "true")
307314
node.setting("plugins.security.check_snapshot_restore_write_privileges", "true")
@@ -405,22 +412,125 @@ testClusters.integTest {
405412
setting 'path.repo', repo.absolutePath
406413
}
407414

415+
// Re-write WaitForHttpResource with updated code to support security plugin use case
416+
class WaitForClusterYellow {
417+
418+
private URL url
419+
private String username
420+
private String password
421+
Set<Integer> validResponseCodes = Collections.singleton(200)
422+
423+
WaitForClusterYellow(String protocol, String host, int numberOfNodes) throws MalformedURLException {
424+
this(new URL(protocol + "://" + host + "/_cluster/health?wait_for_nodes=>=" + numberOfNodes + "&wait_for_status=yellow"))
425+
}
426+
427+
WaitForClusterYellow(URL url) {
428+
this.url = url
429+
}
430+
431+
boolean wait(int durationInMs) throws GeneralSecurityException, InterruptedException, IOException {
432+
final long waitUntil = System.nanoTime() + TimeUnit.MILLISECONDS.toNanos(durationInMs)
433+
final long sleep = 100
434+
435+
IOException failure = null
436+
while (true) {
437+
try {
438+
checkResource()
439+
return true
440+
} catch (IOException e) {
441+
failure = e
442+
}
443+
if (System.nanoTime() < waitUntil) {
444+
Thread.sleep(sleep)
445+
} else {
446+
throw failure
447+
}
448+
}
449+
}
450+
451+
void setUsername(String username) {
452+
this.username = username
453+
}
454+
455+
void setPassword(String password) {
456+
this.password = password
457+
}
458+
459+
void checkResource() throws IOException {
460+
final HttpURLConnection connection = buildConnection()
461+
connection.connect()
462+
final Integer response = connection.getResponseCode()
463+
if (validResponseCodes.contains(response)) {
464+
return
465+
} else {
466+
throw new IOException(response + " " + connection.getResponseMessage())
467+
}
468+
}
469+
470+
HttpURLConnection buildConnection() throws IOException {
471+
final HttpURLConnection connection = (HttpURLConnection) this.@url.openConnection()
472+
473+
if (connection instanceof HttpsURLConnection) {
474+
TrustManager[] trustAllCerts = [new X509TrustManager() {
475+
X509Certificate[] getAcceptedIssuers() {
476+
return null
477+
}
478+
479+
void checkClientTrusted(X509Certificate[] certs, String authType) {
480+
}
481+
482+
void checkServerTrusted(X509Certificate[] certs, String authType) {
483+
}
484+
}
485+
] as TrustManager[]
486+
SSLContext sc = SSLContext.getInstance("SSL")
487+
sc.init(null, trustAllCerts, new java.security.SecureRandom())
488+
connection.setSSLSocketFactory(sc.getSocketFactory())
489+
// Create all-trusting host name verifier
490+
HostnameVerifier allHostsValid = new HostnameVerifier() {
491+
boolean verify(String hostname, SSLSession session) {
492+
return true
493+
}
494+
}
495+
// Install the all-trusting host verifier
496+
connection.setHostnameVerifier(allHostsValid)
497+
}
498+
499+
configureBasicAuth(connection)
500+
connection.setRequestMethod("GET")
501+
return connection
502+
}
503+
504+
void configureBasicAuth(HttpURLConnection connection) {
505+
if (username != null) {
506+
if (password == null) {
507+
throw new IllegalStateException("Basic Auth user [" + username + "] has been set, but no password has been configured")
508+
}
509+
connection.setRequestProperty(
510+
"Authorization",
511+
"Basic " + Base64.getEncoder().encodeToString((username + ":" + password).getBytes(StandardCharsets.UTF_8))
512+
)
513+
}
514+
}
515+
516+
}
517+
408518
def waitForClusterSetup(OpenSearchCluster cluster, Boolean securityEnabled) {
409519
cluster.@waitConditions.clear()
410520
String unicastUris = cluster.nodes.stream().flatMap { node ->
411521
node.getAllTransportPortURI().stream()
412522
}.collect(Collectors.joining("\n"))
413523
cluster.nodes.forEach {node ->
414524
try {
415-
Files.write(node.getConfigDir().resolve("unicast_hosts.txt"), unicastUris.getBytes(StandardCharsets.UTF_8));
525+
Files.write(node.getConfigDir().resolve("unicast_hosts.txt"), unicastUris.getBytes(StandardCharsets.UTF_8))
416526
} catch (IOException e) {
417-
throw new java.io.UncheckedIOException("Failed to write configuation files for " + this, e);
527+
throw new java.io.UncheckedIOException("Failed to write configuation files for " + this, e)
418528
}
419529
}
420530

421531
Predicate pred = {
422532
String protocol = securityEnabled ? "https" : "http"
423-
WaitForHttpResource wait = new WaitForHttpResource(protocol, cluster.getFirstNode().getHttpSocketURI(), cluster.nodes.size())
533+
WaitForClusterYellow wait = new WaitForClusterYellow(protocol, cluster.getFirstNode().getHttpSocketURI(), cluster.nodes.size())
424534
wait.setUsername(System.getProperty("user", "admin"))
425535
wait.setPassword(System.getProperty("password", "admin"))
426536
return wait.wait(500)
@@ -451,7 +561,7 @@ integTest {
451561
}
452562
}
453563

454-
// The -Dcluster.debug option makes the cluster debuggable; this makes the tests debuggable
564+
// The -Dcluster.debug option makes the cluster debuggable, this makes the tests debuggable
455565
if (System.getProperty("test.debug") != null) {
456566
jvmArgs '-agentlib:jdwp=transport=dt_socket,server=n,suspend=y,address=8000'
457567
}
@@ -490,6 +600,7 @@ task integTestRemote(type: RestIntegTestTask) {
490600
systemProperty "https", System.getProperty("https")
491601
systemProperty "user", System.getProperty("user")
492602
systemProperty "password", System.getProperty("password")
603+
systemProperty 'buildDir', buildDir.path
493604

494605
if (System.getProperty("tests.rest.bwcsuite") == null) {
495606
filter {

src/main/kotlin/org/opensearch/indexmanagement/rollup/action/index/TransportIndexRollupAction.kt

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -191,7 +191,7 @@ class TransportIndexRollupAction @Inject constructor(
191191

192192
private fun validateTargetIndexName(): Boolean {
193193
val targetIndexResolvedName = RollupFieldValueExpressionResolver.resolve(request.rollup, request.rollup.targetIndex)
194-
return targetIndexResolvedName.contains("*") == false && targetIndexResolvedName.contains("?") == false
194+
return !targetIndexResolvedName.contains("*") && !targetIndexResolvedName.contains("?")
195195
}
196196
}
197197
}

src/test/kotlin/org/opensearch/indexmanagement/ODFERestTestCase.kt

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -50,7 +50,9 @@ abstract class ODFERestTestCase : OpenSearchRestTestCase() {
5050
// create adminDN (super-admin) client
5151
val uri = javaClass.classLoader.getResource("security/sample.pem")?.toURI()
5252
val configPath = PathUtils.get(uri).parent.toAbsolutePath()
53-
SecureRestClientBuilder(settings, configPath, hosts).setSocketTimeout(5000).build()
53+
// TODO once common utils is updated in maven, we can use this method to define hosts
54+
// SecureRestClientBuilder(settings, configPath, hosts).setSocketTimeout(5000).build()
55+
SecureRestClientBuilder(settings, configPath).setSocketTimeout(5000).build()
5456
}
5557
false -> {
5658
// create client with passed user

src/test/kotlin/org/opensearch/indexmanagement/indexstatemanagement/action/RolloverActionIT.kt

Lines changed: 8 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -281,11 +281,14 @@ class RolloverActionIT : IndexStateManagementRestTestCase() {
281281
insertSampleData(index = firstIndex, docCount = 20, delay = 0, jsonString = "{ \"test_field\": \"${OpenSearchTestCase.randomAlphaOfLength(7000)}\" }", routing = "custom_routing")
282282
flush(firstIndex, true)
283283
forceMerge(firstIndex, "1")
284-
val primaryShards = (cat("shards/$firstIndex?format=json&bytes=b") as List<Map<String, Any>>).filter { it["prirep"] == "p" }
285-
// TODO seeing flakyness of multiple shards over 100kb, log out shards to further debug
286-
logger.info("cat shards result: $primaryShards")
287-
val primaryShardsOver100KB = primaryShards.filter { (it["store"] as String).toInt() > 100000 }
288-
assertTrue("Found multiple shards over 100kb", primaryShardsOver100KB.size == 1)
284+
val primaryShards = waitFor {
285+
val primaryShards = (cat("shards/$firstIndex?format=json&bytes=b") as List<Map<String, Any>>).filter { it["prirep"] == "p" }
286+
// TODO seeing flakyness of multiple shards over 100kb, log out shards to further debug
287+
logger.info("cat shards result: $primaryShards")
288+
val primaryShardsOver100KB = primaryShards.filter { (it["store"] as String).toInt() > 100000 }
289+
assertTrue("Shard over 100kb is not exactly 1", primaryShardsOver100KB.size == 1)
290+
primaryShards
291+
}
289292
primaryShardSizeBytes = primaryShards.maxOf { (it["store"] as String).toInt() }
290293
}
291294

src/test/kotlin/org/opensearch/indexmanagement/refreshanalyzer/RefreshSearchAnalyzerActionIT.kt

Lines changed: 5 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -18,8 +18,10 @@ import java.io.InputStreamReader
1818
import java.nio.charset.Charset
1919
import java.nio.charset.StandardCharsets
2020
import java.nio.file.Files
21+
import java.util.Locale
2122

2223
class RefreshSearchAnalyzerActionIT : IndexManagementRestTestCase() {
24+
private val testIndexName = javaClass.simpleName.lowercase(Locale.ROOT)
2325

2426
@After
2527
fun clearIndicesAfterEachTest() {
@@ -34,7 +36,7 @@ class RefreshSearchAnalyzerActionIT : IndexManagementRestTestCase() {
3436
fun `test index time analyzer`() {
3537
val buildDir = System.getProperty("buildDir")
3638
val numNodes = System.getProperty("cluster.number_of_nodes", "1").toInt()
37-
val indexName = "testindex"
39+
val indexName = "${testIndexName}_index_1"
3840

3941
for (i in 0 until numNodes) {
4042
writeToFile("$buildDir/testclusters/integTest-$i/config/pacman_synonyms.txt", "hello, hola")
@@ -80,7 +82,7 @@ class RefreshSearchAnalyzerActionIT : IndexManagementRestTestCase() {
8082
fun `test search time analyzer`() {
8183
val buildDir = System.getProperty("buildDir")
8284
val numNodes = System.getProperty("cluster.number_of_nodes", "1").toInt()
83-
val indexName = "testindex"
85+
val indexName = "${testIndexName}_index_2"
8486

8587
for (i in 0 until numNodes) {
8688
writeToFile("$buildDir/testclusters/integTest-$i/config/pacman_synonyms.txt", "hello, hola")
@@ -124,7 +126,7 @@ class RefreshSearchAnalyzerActionIT : IndexManagementRestTestCase() {
124126
}
125127

126128
fun `test alias`() {
127-
val indexName = "testindex"
129+
val indexName = "${testIndexName}_index_3"
128130
val numNodes = System.getProperty("cluster.number_of_nodes", "1").toInt()
129131
val buildDir = System.getProperty("buildDir")
130132
val aliasName = "test"

src/test/kotlin/org/opensearch/indexmanagement/rollup/RollupRestTestCase.kt

Lines changed: 18 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -54,7 +54,17 @@ abstract class RollupRestTestCase : IndexManagementRestTestCase() {
5454
fun setDebugLogLevel() {
5555
client().makeRequest(
5656
"PUT", "_cluster/settings",
57-
StringEntity("""{"transient":{"logger.org.opensearch.indexmanagement.rollup":"DEBUG"}}""", APPLICATION_JSON)
57+
StringEntity(
58+
"""
59+
{
60+
"transient": {
61+
"logger.org.opensearch.indexmanagement.rollup":"DEBUG",
62+
"logger.org.opensearch.jobscheduler":"DEBUG"
63+
}
64+
}
65+
""".trimIndent(),
66+
APPLICATION_JSON
67+
)
5868
)
5969
}
6070

@@ -233,8 +243,14 @@ abstract class RollupRestTestCase : IndexManagementRestTestCase() {
233243
}
234244
val intervalSchedule = (update.jobSchedule as IntervalSchedule)
235245
val millis = Duration.of(intervalSchedule.interval.toLong(), intervalSchedule.unit).minusSeconds(2).toMillis()
236-
val startTimeMillis = desiredStartTimeMillis ?: Instant.now().toEpochMilli() - millis
246+
val startTimeMillis = desiredStartTimeMillis ?: (Instant.now().toEpochMilli() - millis)
237247
val waitForActiveShards = if (isMultiNode) "all" else "1"
248+
// TODO flaky: Add this log to confirm this update is missed by job scheduler
249+
// This miss is because shard remove, job scheduler deschedule on the original node and reschedule on another node
250+
// However the shard comes back, and job scheduler deschedule on the another node and reschedule on the original node
251+
// During this period, this update got missed
252+
// Since from the log, this happens very fast (within 0.1~0.2s), the above cluster explain may not have the granularity to catch this.
253+
logger.info("Update rollup start time to $startTimeMillis")
238254
val response = client().makeRequest(
239255
"POST", "$INDEX_MANAGEMENT_INDEX/_update/${update.id}?wait_for_active_shards=$waitForActiveShards",
240256
StringEntity(

0 commit comments

Comments
 (0)