Skip to content

Commit 02055cc

Browse files
RajatGupta02Rajat Guptapeterzhuamazon
authored
Add sample integ tests for latest systemd unit file (opensearch-project#17410)
* Add integration tests for systemd Signed-off-by: Rajat Gupta <gptrajat@amazon.com> * Fix indentation Signed-off-by: Rajat Gupta <gptrajat@amazon.com> * Remove unit file mount Signed-off-by: Rajat Gupta <gptrajat@amazon.com> * Use centos image Signed-off-by: Rajat Gupta <gptrajat@amazon.com> * Change method name Signed-off-by: Rajat Gupta <gptrajat@amazon.com> * Add sample systemd integ tests to verify behavior Signed-off-by: Rajat Gupta <gptrajat@amazon.com> * Update su with sudo probably need to have a privileged mode Signed-off-by: Peter Zhu <zhujiaxi@amazon.com> * Additional tests Signed-off-by: Rajat Gupta <gptrajat@amazon.com> * Wrap commands with su -c Signed-off-by: Rajat Gupta <gptrajat@amazon.com> * Add sudo Signed-off-by: Rajat Gupta <gptrajat@amazon.com> * Remove sudo for test process exit Signed-off-by: Rajat Gupta <gptrajat@amazon.com> * Minor fixes Signed-off-by: Rajat Gupta <gptrajat@amazon.com> * Fixed script string Signed-off-by: Rajat Gupta <gptrajat@amazon.com> * Remove redundant code Signed-off-by: Rajat Gupta <gptrajat@amazon.com> * Add terminate script Signed-off-by: Rajat Gupta <gptrajat@amazon.com> * Modified terminate script Signed-off-by: Rajat Gupta <gptrajat@amazon.com> * Add Changelog-3.0 entry Signed-off-by: Rajat Gupta <gptrajat@amazon.com> * Fix for gradle precommit workflow Signed-off-by: Rajat Gupta <gptrajat@amazon.com> * Fix testing conventions gradle precommit check Signed-off-by: Rajat Gupta <gptrajat@amazon.com> * Fix imports Signed-off-by: Rajat Gupta <gptrajat@amazon.com> * Only run as part of build integTest, remove gradle check Signed-off-by: Rajat Gupta <gptrajat@amazon.com> * Remove bash Signed-off-by: Rajat Gupta <gptrajat@amazon.com> * add sudo for systemctl command Signed-off-by: Rajat Gupta <gptrajat@amazon.com> * Remove OpenSearchIntegTest class Signed-off-by: Rajat Gupta <gptrajat@amazon.com> * Rename test file Signed-off-by: Rajat Gupta <gptrajat@amazon.com> * Add test script Signed-off-by: Rajat Gupta <gptrajat@amazon.com> * Extend LuceneTestCase class Signed-off-by: Rajat Gupta <gptrajat@amazon.com> * Remove test bash script Signed-off-by: Rajat Gupta <gptrajat@amazon.com> * Modify build.gradle Signed-off-by: Rajat Gupta <gptrajat@amazon.com> --------- Signed-off-by: Rajat Gupta <gptrajat@amazon.com> Signed-off-by: Peter Zhu <zhujiaxi@amazon.com> Signed-off-by: Rajat Gupta <72070007+RajatGupta02@users.noreply.github.com> Co-authored-by: Rajat Gupta <gptrajat@amazon.com> Co-authored-by: Peter Zhu <zhujiaxi@amazon.com>
1 parent cc82be9 commit 02055cc

File tree

4 files changed

+196
-0
lines changed

4 files changed

+196
-0
lines changed

CHANGELOG-3.0.md

+2
Original file line numberDiff line numberDiff line change
@@ -21,10 +21,12 @@ The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/),
2121
- Add execution_hint to cardinality aggregator request (#[17312](https://github.com/opensearch-project/OpenSearch/pull/17312))
2222
- Arrow Flight RPC plugin with Flight server bootstrap logic and client for internode communication ([#16962](https://github.com/opensearch-project/OpenSearch/pull/16962))
2323
- Added offset management for the pull-based Ingestion ([#17354](https://github.com/opensearch-project/OpenSearch/pull/17354))
24+
- Added integ tests for systemd configs ([#17410](https://github.com/opensearch-project/OpenSearch/pull/17410))
2425
- Add filter function for AbstractQueryBuilder, BoolQueryBuilder, ConstantScoreQueryBuilder([#17409](https://github.com/opensearch-project/OpenSearch/pull/17409))
2526
- [Star Tree] [Search] Resolving keyword & numeric bucket aggregation with metric aggregation using star-tree ([#17165](https://github.com/opensearch-project/OpenSearch/pull/17165))
2627
- Added error handling support for the pull-based ingestion ([#17427](https://github.com/opensearch-project/OpenSearch/pull/17427))
2728

29+
2830
### Dependencies
2931
- Update Apache Lucene to 10.1.0 ([#16366](https://github.com/opensearch-project/OpenSearch/pull/16366))
3032
- Bump Apache HttpCore5/HttpClient5 dependencies from 5.2.5/5.3.1 to 5.3.1/5.4.1 to support ExtendedSocketOption in HttpAsyncClient ([#16757](https://github.com/opensearch-project/OpenSearch/pull/16757))

qa/systemd-test/build.gradle

+5
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
apply plugin: 'opensearch.standalone-rest-test'
2+
3+
tasks.register("integTest", Test){
4+
include "**/*IntegTests.class"
5+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,177 @@
1+
/*
2+
* SPDX-License-Identifier: Apache-2.0
3+
*
4+
* The OpenSearch Contributors require contributions made to
5+
* this file be licensed under the Apache-2.0 license or a
6+
* compatible open source license.
7+
*/
8+
/*
9+
* Modifications Copyright OpenSearch Contributors. See
10+
* GitHub history for details.
11+
*/
12+
13+
package org.opensearch.systemdinteg;
14+
import org.apache.lucene.tests.util.LuceneTestCase;
15+
16+
import org.apache.hc.core5.http.HttpHeaders;
17+
import org.apache.hc.core5.http.HttpHost;
18+
import org.apache.hc.core5.http.HttpStatus;
19+
import org.junit.After;
20+
import org.junit.Before;
21+
import org.junit.BeforeClass;
22+
import org.junit.Test;
23+
24+
import java.io.IOException;
25+
import java.io.InputStream;
26+
import java.io.InputStreamReader;
27+
import java.net.URISyntaxException;
28+
import java.nio.charset.StandardCharsets;
29+
import java.io.BufferedReader;
30+
import java.nio.file.Files;
31+
import java.nio.file.Path;
32+
import java.nio.file.Paths;
33+
import java.nio.file.StandardOpenOption;
34+
import java.nio.file.attribute.PosixFilePermissions;
35+
import java.util.Locale;
36+
37+
import static org.junit.Assert.assertTrue;
38+
import static org.junit.Assert.assertEquals;
39+
import static org.junit.Assert.assertFalse;
40+
41+
42+
public class SystemdIntegTests extends LuceneTestCase {
43+
44+
private static String opensearchPid;
45+
46+
@BeforeClass
47+
public static void setup() throws IOException, InterruptedException {
48+
opensearchPid = getOpenSearchPid();
49+
50+
if (opensearchPid.isEmpty()) {
51+
throw new RuntimeException("Failed to find OpenSearch process ID");
52+
}
53+
}
54+
55+
private static String getOpenSearchPid() throws IOException, InterruptedException {
56+
String command = "systemctl show --property=MainPID opensearch";
57+
String output = executeCommand(command, "Failed to get OpenSearch PID");
58+
return output.replace("MainPID=", "").trim();
59+
}
60+
61+
private boolean checkPathExists(String path) throws IOException, InterruptedException {
62+
String command = String.format(Locale.ROOT, "test -e %s && echo true || echo false", path);
63+
return Boolean.parseBoolean(executeCommand(command, "Failed to check path existence"));
64+
}
65+
66+
private boolean checkPathReadable(String path) throws IOException, InterruptedException {
67+
String command = String.format(Locale.ROOT, "sudo su opensearch -s /bin/sh -c 'test -r %s && echo true || echo false'", path);
68+
return Boolean.parseBoolean(executeCommand(command, "Failed to check read permission"));
69+
}
70+
71+
private boolean checkPathWritable(String path) throws IOException, InterruptedException {
72+
String command = String.format(Locale.ROOT, "sudo su opensearch -s /bin/sh -c 'test -w %s && echo true || echo false'", path);
73+
return Boolean.parseBoolean(executeCommand(command, "Failed to check write permission"));
74+
}
75+
76+
private String getPathOwnership(String path) throws IOException, InterruptedException {
77+
String command = String.format(Locale.ROOT, "stat -c '%%U:%%G' %s", path);
78+
return executeCommand(command, "Failed to get path ownership");
79+
}
80+
81+
private static String executeCommand(String command, String errorMessage) throws IOException, InterruptedException {
82+
Process process = Runtime.getRuntime().exec(new String[]{"bash", "-c", command});
83+
try (BufferedReader reader = new BufferedReader(new InputStreamReader(process.getInputStream(), StandardCharsets.UTF_8))) {
84+
StringBuilder output = new StringBuilder();
85+
String line;
86+
while ((line = reader.readLine()) != null) {
87+
output.append(line).append("\n");
88+
}
89+
if (process.waitFor() != 0) {
90+
throw new RuntimeException(errorMessage);
91+
}
92+
return output.toString().trim();
93+
}
94+
}
95+
96+
public void testReadOnlyPaths() throws IOException, InterruptedException {
97+
String[] readOnlyPaths = {
98+
"/etc/os-release", "/usr/lib/os-release", "/etc/system-release",
99+
"/proc/self/mountinfo", "/proc/diskstats",
100+
"/proc/self/cgroup", "/sys/fs/cgroup/cpu", "/sys/fs/cgroup/cpu/-",
101+
"/sys/fs/cgroup/cpuacct", "/sys/fs/cgroup/cpuacct/-",
102+
"/sys/fs/cgroup/memory", "/sys/fs/cgroup/memory/-"
103+
};
104+
105+
for (String path : readOnlyPaths) {
106+
if (checkPathExists(path)) {
107+
assertTrue("Path should be readable: " + path, checkPathReadable(path));
108+
assertFalse("Path should not be writable: " + path, checkPathWritable(path));
109+
}
110+
}
111+
}
112+
113+
public void testReadWritePaths() throws IOException, InterruptedException {
114+
String[] readWritePaths = {"/var/log/opensearch", "/var/lib/opensearch"};
115+
for (String path : readWritePaths) {
116+
assertTrue("Path should exist: " + path, checkPathExists(path));
117+
assertTrue("Path should be readable: " + path, checkPathReadable(path));
118+
assertTrue("Path should be writable: " + path, checkPathWritable(path));
119+
assertEquals("Path should be owned by opensearch:opensearch", "opensearch:opensearch", getPathOwnership(path));
120+
}
121+
}
122+
123+
public void testMaxProcesses() throws IOException, InterruptedException {
124+
String limits = executeCommand("sudo su -c 'cat /proc/" + opensearchPid + "/limits'", "Failed to read process limits");
125+
assertTrue("Max processes limit should be 4096 or unlimited",
126+
limits.contains("Max processes 4096 4096") ||
127+
limits.contains("Max processes unlimited unlimited"));
128+
}
129+
130+
public void testFileDescriptorLimit() throws IOException, InterruptedException {
131+
String limits = executeCommand("sudo su -c 'cat /proc/" + opensearchPid + "/limits'", "Failed to read process limits");
132+
assertTrue("File descriptor limit should be at least 65535",
133+
limits.contains("Max open files 65535 65535") ||
134+
limits.contains("Max open files unlimited unlimited"));
135+
}
136+
137+
public void testSystemCallFilter() throws IOException, InterruptedException {
138+
// Check if Seccomp is enabled
139+
String seccomp = executeCommand("sudo su -c 'grep Seccomp /proc/" + opensearchPid + "/status'", "Failed to read Seccomp status");
140+
assertFalse("Seccomp should be enabled", seccomp.contains("0"));
141+
142+
// Test specific system calls that should be blocked
143+
String rebootResult = executeCommand("sudo su opensearch -c 'kill -s SIGHUP 1' 2>&1 || echo 'Operation not permitted'", "Failed to test reboot system call");
144+
assertTrue("Reboot system call should be blocked", rebootResult.contains("Operation not permitted"));
145+
146+
String swapResult = executeCommand("sudo su opensearch -c 'swapon -a' 2>&1 || echo 'Operation not permitted'", "Failed to test swap system call");
147+
assertTrue("Swap system call should be blocked", swapResult.contains("Operation not permitted"));
148+
}
149+
150+
public void testOpenSearchProcessCannotExit() throws IOException, InterruptedException {
151+
152+
String scriptPath;
153+
try {
154+
scriptPath = SystemdIntegTests.class.getResource("/scripts/terminate.sh").toURI().getPath();
155+
} catch (URISyntaxException e) {
156+
throw new RuntimeException("Failed to convert URL to URI", e);
157+
}
158+
159+
if (scriptPath == null) {
160+
throw new IllegalStateException("Could not find terminate.sh script in resources");
161+
}
162+
ProcessBuilder processBuilder = new ProcessBuilder(scriptPath, opensearchPid);
163+
Process process = processBuilder.start();
164+
165+
// Wait a moment for any potential termination to take effect
166+
Thread.sleep(2000);
167+
168+
// Verify the OpenSearch service status
169+
String serviceStatus = executeCommand(
170+
"systemctl is-active opensearch",
171+
"Failed to check OpenSearch service status"
172+
);
173+
174+
assertEquals("OpenSearch service should be active", "active", serviceStatus.trim());
175+
}
176+
177+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,12 @@
1+
#!/bin/sh
2+
3+
if [ $# -ne 1 ]; then
4+
echo "Usage: $0 <PID>"
5+
exit 1
6+
fi
7+
8+
if kill -15 $1 2>/dev/null; then
9+
echo "SIGTERM signal sent to process $1"
10+
else
11+
echo "Failed to send SIGTERM to process $1"
12+
fi

0 commit comments

Comments
 (0)