Skip to content

Commit

Permalink
Allow rest tests with Serverless API protections
Browse files Browse the repository at this point in the history
This commit changes the `ESRestTestCase` in 3 ways

- Refactor the "hasXYZ" flags into a single `EnumSet` of
  `ProductFeature` flags
- Detect serverless distributions, and in those cases treat
  certain features as unavailable (even if the plugin is installed)
- Add a few more feature checks for features that can now be disabled

It also changes `ESClientYamlSuiteTestCase` to register a composable
global template if legacy templates are unavailable

Relates: elastic#98250, elastic#98373
  • Loading branch information
tvernum committed Aug 15, 2023
1 parent 54effe1 commit 6dbf7d1
Show file tree
Hide file tree
Showing 2 changed files with 101 additions and 70 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -89,6 +89,7 @@
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Base64;
import java.util.EnumSet;
import java.util.HashMap;
import java.util.HashSet;
import java.util.List;
Expand Down Expand Up @@ -170,10 +171,10 @@ public static List<Object> entityAsList(Response response) throws IOException {
* Does any node in the cluster being tested have x-pack installed?
*/
public static boolean hasXPack() {
if (hasXPack == null) {
if (availableFeatures == null) {
throw new IllegalStateException("must be called inside of a rest test case test");
}
return hasXPack;
return availableFeatures.contains(ProductFeature.XPACK);
}

private static List<HttpHost> clusterHosts;
Expand All @@ -186,23 +187,26 @@ public static boolean hasXPack() {
* completes
*/
private static RestClient adminClient;
private static Boolean hasXPack;
private static Boolean hasIlm;
private static Boolean hasRollups;
private static Boolean hasCcr;
private static Boolean hasShutdown;

public enum ProductFeature {
XPACK,
ILM,
SLM,
ROLLUPS,
CCR,
SHUTDOWN,
LEGACY_TEMPLATES,
}

private static EnumSet<ProductFeature> availableFeatures;
private static TreeSet<Version> nodeVersions;

@Before
public void initClient() throws IOException {
if (client == null) {
assert adminClient == null;
assert clusterHosts == null;
assert hasXPack == null;
assert hasIlm == null;
assert hasRollups == null;
assert hasCcr == null;
assert hasShutdown == null;
assert availableFeatures == null;
assert nodeVersions == null;
String cluster = getTestRestCluster();
String[] stringUrls = cluster.split(",");
Expand All @@ -221,12 +225,9 @@ public void initClient() throws IOException {
client = buildClient(restClientSettings(), clusterHosts.toArray(new HttpHost[clusterHosts.size()]));
adminClient = buildClient(restAdminSettings(), clusterHosts.toArray(new HttpHost[clusterHosts.size()]));

hasXPack = false;
hasIlm = false;
hasRollups = false;
hasCcr = false;
hasShutdown = false;
availableFeatures = EnumSet.of(ProductFeature.LEGACY_TEMPLATES);
nodeVersions = new TreeSet<>();
boolean serverless = false;
Map<?, ?> response = entityAsMap(adminClient.performRequest(new Request("GET", "_nodes/plugins")));
Map<?, ?> nodes = (Map<?, ?>) response.get("nodes");
for (Map.Entry<?, ?> node : nodes.entrySet()) {
Expand All @@ -236,34 +237,49 @@ public void initClient() throws IOException {
Map<?, ?> moduleInfo = (Map<?, ?>) module;
final String moduleName = moduleInfo.get("name").toString();
if (moduleName.startsWith("x-pack")) {
hasXPack = true;
availableFeatures.add(ProductFeature.XPACK);
}
if (moduleName.equals("x-pack-ilm")) {
hasIlm = true;
availableFeatures.add(ProductFeature.ILM);
availableFeatures.add(ProductFeature.SLM);
}
if (moduleName.equals("x-pack-rollup")) {
hasRollups = true;
availableFeatures.add(ProductFeature.ROLLUPS);
}
if (moduleName.equals("x-pack-ccr")) {
hasCcr = true;
availableFeatures.add(ProductFeature.CCR);
}
if (moduleName.equals("x-pack-shutdown")) {
hasShutdown = true;
availableFeatures.add(ProductFeature.SHUTDOWN);
}
if (moduleName.startsWith("serverless-")) {
serverless = true;
}
}
if (serverless) {
availableFeatures.removeAll(
List.of(
ProductFeature.ILM,
ProductFeature.SLM,
ProductFeature.ROLLUPS,
ProductFeature.CCR,
ProductFeature.LEGACY_TEMPLATES
)
);
}
}
}
assert client != null;
assert adminClient != null;
assert clusterHosts != null;
assert hasXPack != null;
assert hasIlm != null;
assert hasRollups != null;
assert hasCcr != null;
assert hasShutdown != null;
assert availableFeatures != null;
assert nodeVersions != null;
}

protected static boolean has(ProductFeature feature) {
return availableFeatures.contains(feature);
}

protected String getTestRestCluster() {
String cluster = System.getProperty("tests.rest.cluster");
if (cluster == null) {
Expand Down Expand Up @@ -414,11 +430,7 @@ public static void closeClients() throws IOException {
clusterHosts = null;
client = null;
adminClient = null;
hasXPack = null;
hasRollups = null;
hasCcr = null;
hasShutdown = null;
hasIlm = null;
availableFeatures = null;
nodeVersions = null;
}
}
Expand Down Expand Up @@ -674,18 +686,20 @@ private void wipeCluster() throws Exception {
// Cleanup rollup before deleting indices. A rollup job might have bulks in-flight,
// so we need to fully shut them down first otherwise a job might stall waiting
// for a bulk to finish against a non-existing index (and then fail tests)
if (hasRollups && false == preserveRollupJobsUponCompletion()) {
if (has(ProductFeature.ROLLUPS) && false == preserveRollupJobsUponCompletion()) {
wipeRollupJobs();
waitForPendingRollupTasks();
}

if (preserveSLMPoliciesUponCompletion() == false) {
if (has(ProductFeature.SLM) && preserveSLMPoliciesUponCompletion() == false) {
// Clean up SLM policies before trying to wipe snapshots so that no new ones get started by SLM after wiping
deleteAllSLMPolicies();
}

// Clean up searchable snapshots indices before deleting snapshots and repositories
if (hasXPack() && nodeVersions.first().onOrAfter(Version.V_7_8_0) && preserveSearchableSnapshotsIndicesUponCompletion() == false) {
if (has(ProductFeature.XPACK)
&& nodeVersions.first().onOrAfter(Version.V_7_8_0)
&& preserveSearchableSnapshotsIndicesUponCompletion() == false) {
wipeSearchableSnapshotsIndices();
}

Expand All @@ -708,7 +722,7 @@ private void wipeCluster() throws Exception {

// wipe index templates
if (preserveTemplatesUponCompletion() == false) {
if (hasXPack) {
if (hasXPack()) {
/*
* Delete only templates that xpack doesn't automatically
* recreate. Deleting them doesn't hurt anything, but it
Expand Down Expand Up @@ -784,26 +798,30 @@ private void wipeCluster() throws Exception {
// We hit a version of ES that doesn't support index templates v2 yet, so it's safe to ignore
}
}
// Always check for legacy templates:
Request getLegacyTemplatesRequest = new Request("GET", "_template");
Map<String, Object> legacyTemplates = XContentHelper.convertToMap(
JsonXContent.jsonXContent,
EntityUtils.toString(adminClient().performRequest(getLegacyTemplatesRequest).getEntity()),
false
);
for (String name : legacyTemplates.keySet()) {
if (isXPackTemplate(name)) {
continue;
}
try {
adminClient().performRequest(new Request("DELETE", "_template/" + name));
} catch (ResponseException e) {
logger.debug(() -> format("unable to remove index template %s", name), e);

if (has(ProductFeature.LEGACY_TEMPLATES)) {
Request getLegacyTemplatesRequest = new Request("GET", "_template");
Map<String, Object> legacyTemplates = XContentHelper.convertToMap(
JsonXContent.jsonXContent,
EntityUtils.toString(adminClient().performRequest(getLegacyTemplatesRequest).getEntity()),
false
);
for (String name : legacyTemplates.keySet()) {
if (isXPackTemplate(name)) {
continue;
}
try {
adminClient().performRequest(new Request("DELETE", "_template/" + name));
} catch (ResponseException e) {
logger.debug(() -> format("unable to remove index template %s", name), e);
}
}
}
} else {
logger.debug("Clearing all templates");
adminClient().performRequest(new Request("DELETE", "_template/*"));
if (has(ProductFeature.LEGACY_TEMPLATES)) {
adminClient().performRequest(new Request("DELETE", "_template/*"));
}
try {
adminClient().performRequest(new Request("DELETE", "_index_template/*"));
adminClient().performRequest(new Request("DELETE", "_component_template/*"));
Expand All @@ -818,11 +836,11 @@ private void wipeCluster() throws Exception {
wipeClusterSettings();
}

if (hasIlm && false == preserveILMPoliciesUponCompletion()) {
if (has(ProductFeature.ILM) && false == preserveILMPoliciesUponCompletion()) {
deleteAllILMPolicies(preserveILMPolicyIds());
}

if (hasCcr && false == preserveAutoFollowPatternsUponCompletion()) {
if (has(ProductFeature.CCR) && false == preserveAutoFollowPatternsUponCompletion()) {
deleteAllAutoFollowPatterns();
}

Expand All @@ -837,7 +855,7 @@ private void wipeCluster() throws Exception {
* @throws IOException
*/
private void checkForUnexpectedlyRecreatedObjects() throws IOException {
if (hasIlm && false == preserveILMPoliciesUponCompletion()) {
if (has(ProductFeature.ILM) && false == preserveILMPoliciesUponCompletion()) {
Set<String> unexpectedIlmPlicies = getAllUnexpectedIlmPolicies(preserveILMPolicyIds());
assertTrue(
"Expected no ILM policies after deletions, but found " + String.join(", ", unexpectedIlmPlicies),
Expand Down Expand Up @@ -875,7 +893,7 @@ private Set<String> getAllUnexpectedIlmPolicies(Set<String> exclusions) throws I
private Set<String> getAllUnexpectedTemplates() throws IOException {
Set<String> unexpectedTemplates = new HashSet<>();
if (preserveDataStreamsUponCompletion() == false && preserveTemplatesUponCompletion() == false) {
if (hasXPack) {
if (has(ProductFeature.XPACK)) {
// In case of bwc testing, if all nodes are before 7.8.0 then no need to attempt to delete component and composable
// index templates, because these were introduced in 7.8.0:
if (nodeVersions.stream().allMatch(version -> version.onOrAfter(Version.V_7_8_0))) {
Expand All @@ -899,16 +917,18 @@ private Set<String> getAllUnexpectedTemplates() throws IOException {
.filter(name -> isXPackTemplate(name) == false)
.forEach(unexpectedTemplates::add);
}
// Always check for legacy templates:
Request getLegacyTemplatesRequest = new Request("GET", "_template");
Map<String, Object> legacyTemplates = XContentHelper.convertToMap(
JsonXContent.jsonXContent,
EntityUtils.toString(adminClient().performRequest(getLegacyTemplatesRequest).getEntity()),
false
);
unexpectedTemplates.addAll(
legacyTemplates.keySet().stream().filter(template -> isXPackTemplate(template) == false).collect(Collectors.toSet())
);

if (has(ProductFeature.LEGACY_TEMPLATES)) {
Request getLegacyTemplatesRequest = new Request("GET", "_template");
Map<String, Object> legacyTemplates = XContentHelper.convertToMap(
JsonXContent.jsonXContent,
EntityUtils.toString(adminClient().performRequest(getLegacyTemplatesRequest).getEntity()),
false
);
unexpectedTemplates.addAll(
legacyTemplates.keySet().stream().filter(template -> isXPackTemplate(template) == false).collect(Collectors.toSet())
);
}
} else {
// Do nothing
}
Expand All @@ -921,7 +941,7 @@ private Set<String> getAllUnexpectedTemplates() throws IOException {
*/
@SuppressWarnings("unchecked")
protected void deleteAllNodeShutdownMetadata() throws IOException {
if (hasShutdown == false || minimumNodeVersion().before(Version.V_7_15_0)) {
if (has(ProductFeature.SHUTDOWN) == false || minimumNodeVersion().before(Version.V_7_15_0)) {
// Node shutdown APIs are only present in xpack
return;
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -485,25 +485,36 @@ public void test() throws IOException {

final Settings globalTemplateSettings = getGlobalTemplateSettings(testCandidate.getTestSection().getSkipSection().getFeatures());
if (globalTemplateSettings.isEmpty() == false) {
boolean useComponentTemplate = ESRestTestCase.has(ProductFeature.LEGACY_TEMPLATES) == false;

final XContentBuilder template = jsonBuilder();
template.startObject();
{
template.startArray("index_patterns").value("*").endArray();
if (useComponentTemplate) {
template.field("priority", 4); // relatively low priority, but hopefully uncommon enough not to conflict
template.startObject("template");
}
template.startObject("settings");
globalTemplateSettings.toXContent(template, ToXContent.EMPTY_PARAMS);
template.endObject();
if (useComponentTemplate) {
template.endObject();
}
}
template.endObject();

final Request request = new Request("PUT", "/_template/global");
final Request request = new Request("PUT", useComponentTemplate ? "/_index_template/global" : "/_template/global");
request.setJsonEntity(Strings.toString(template));
// Because this has not yet transitioned to a composable template, it's possible that
// Because not all case have transitioned to a composable template, it's possible that
// this can overlap an installed composable template since this is a global (*)
// template. In order to avoid this failing the test, we override the warnings handler
// to be permissive in this case. This can be removed once all tests use composable
// templates instead of legacy templates
RequestOptions.Builder builder = RequestOptions.DEFAULT.toBuilder();
builder.setWarningsHandler(WarningsHandler.PERMISSIVE);
if (useComponentTemplate == false) {
builder.setWarningsHandler(WarningsHandler.PERMISSIVE);
}
request.setOptions(builder.build());
adminClient().performRequest(request);
}
Expand Down

0 comments on commit 6dbf7d1

Please sign in to comment.