Skip to content
Merged
Changes from 1 commit
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
Original file line number Diff line number Diff line change
@@ -0,0 +1,165 @@
/*
* Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
* or more contributor license agreements. Licensed under the "Elastic License
* 2.0", the "GNU Affero General Public License v3.0 only", and the "Server Side
* Public License v 1"; you may not use this file except in compliance with, at
* your election, the "Elastic License 2.0", the "GNU Affero General Public
* License v3.0 only", or the "Server Side Public License, v 1".
*/

package org.elasticsearch.action.admin.indices.stats;

import org.elasticsearch.client.Request;
import org.elasticsearch.client.ResponseException;
import org.elasticsearch.common.Strings;
import org.elasticsearch.common.settings.SecureString;
import org.elasticsearch.common.settings.Settings;
import org.elasticsearch.common.util.concurrent.ThreadContext;
import org.elasticsearch.multiproject.MultiProjectRestTestCase;
import org.elasticsearch.test.cluster.ElasticsearchCluster;
import org.elasticsearch.test.cluster.local.distribution.DistributionType;
import org.elasticsearch.test.rest.ObjectPath;
import org.junit.ClassRule;

import java.io.IOException;
import java.util.Map;

import static org.hamcrest.Matchers.containsInAnyOrder;
import static org.hamcrest.Matchers.equalTo;

public class IndicesStatsMultiProjectIT extends MultiProjectRestTestCase {

private static final String PASSWORD = "hunter2";

@ClassRule
public static ElasticsearchCluster cluster = ElasticsearchCluster.local()
.nodes(1)
.distribution(DistributionType.INTEG_TEST)
.module("test-multi-project")
.setting("test.multi_project.enabled", "true")
.setting("xpack.security.enabled", "true")
.user("admin", PASSWORD)
.build();

@Override
protected String getTestRestCluster() {
return cluster.getHttpAddresses();
}

@Override
protected Settings restClientSettings() {
final String token = basicAuthHeaderValue("admin", new SecureString(PASSWORD.toCharArray()));
return Settings.builder().put(ThreadContext.PREFIX + ".Authorization", token).build();
}

public void testIndicesStats() throws IOException {
// Create two projects. We will use the default project as the third project in this test.
createProject("project-1");
createProject("project-2");

// Create and write data into a number of indices.
// Some of the index names are used only in one project, some in two projects, some in all three.
int numDocs1Only = createPopulatedIndex("project-1", "my-index-project-1-only");
int numDocs2Only = createPopulatedIndex("project-2", "my-index-project-2-only");
int numDocsDefaultOnly = createPopulatedIndex("default", "my-index-default-project-only");
int numDocs1Of1And2 = createPopulatedIndex("project-1", "my-index-projects-1-and-2");
int numDocs2Of1And2 = createPopulatedIndex("project-2", "my-index-projects-1-and-2");
int numDocs2Of2AndDefault = createPopulatedIndex("project-2", "my-index-projects-2-and-default");
int numDocsDefaultOf2AndDefault = createPopulatedIndex("default", "my-index-projects-2-and-default");
int numDocs1All = createPopulatedIndex("project-1", "my-index-all-projects");
int numDocs2All = createPopulatedIndex("project-2", "my-index-all-projects");
int numDocsDefaultAll = createPopulatedIndex("default", "my-index-all-projects");

// Check indices stats for project 1.
Map<String, Object> statsForProject1 = getAsOrderedMapInProject("/_stats", "project-1");
assertThat(ObjectPath.evaluate(statsForProject1, "_all.total.docs.count"), equalTo(numDocs1Only + numDocs1Of1And2 + numDocs1All));
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Nit: I think you can make getAsOrderedMapInProject return an ObjectPath by doing

private static ObjectPath getAsObjectPathInProject(String endpoint, String projectId) throws IOException {
        return ObjectPath.createFromResponse(client().performRequest(setRequestProjectId(new Request("GET", endpoint), projectId)));
    }

and then you can do

Suggested change
Map<String, Object> statsForProject1 = getAsOrderedMapInProject("/_stats", "project-1");
assertThat(ObjectPath.evaluate(statsForProject1, "_all.total.docs.count"), equalTo(numDocs1Only + numDocs1Of1And2 + numDocs1All));
ObjectPath statsForProject1 = getAsObjectPathInProject("/_stats", "project-1");
assertThat(statsForProject1.evaluate("_all.total.docs.count"), equalTo(numDocs1Only + numDocs1Of1And2 + numDocs1All));

(and we even have ObjectPath#evaluateMapKeys which you can use below)
As I said, it's a nit pick, it's just a little bit more compact.

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thanks, I've done that. I also introduced a parallel getAsObjectPathInDefaultProject method. It's not doing as much, but I think it makes the tests easier to read if all the gets are done via getAsObjectPathInProject(endpoint, project) or getAsObjectPathInDefaultProject(endpoint).

assertThat(
ObjectPath.<Map<String, Object>>evaluate(statsForProject1, "indices").keySet(),
containsInAnyOrder("my-index-project-1-only", "my-index-projects-1-and-2", "my-index-all-projects")
);
assertThat(ObjectPath.evaluate(statsForProject1, "indices.my-index-project-1-only.total.docs.count"), equalTo(numDocs1Only));
assertThat(ObjectPath.evaluate(statsForProject1, "indices.my-index-projects-1-and-2.total.docs.count"), equalTo(numDocs1Of1And2));
assertThat(ObjectPath.evaluate(statsForProject1, "indices.my-index-all-projects.total.docs.count"), equalTo(numDocs1All));

// Check indices stats for project 2.
Map<String, Object> statsForProject2 = getAsOrderedMapInProject("/_stats", "project-2");
assertThat(
ObjectPath.evaluate(statsForProject2, "_all.total.docs.count"),
equalTo(numDocs2Only + numDocs2Of1And2 + numDocs2Of2AndDefault + numDocs2All)
);
assertThat(
ObjectPath.<Map<String, Object>>evaluate(statsForProject2, "indices").keySet(),
containsInAnyOrder(
"my-index-project-2-only",
"my-index-projects-1-and-2",
"my-index-projects-2-and-default",
"my-index-all-projects"
)
);
assertThat(ObjectPath.evaluate(statsForProject2, "indices.my-index-all-projects.total.docs.count"), equalTo(numDocs2All));

// Check indices stats for default project.
Map<String, Object> statsForDefaultProject = getAsOrderedMap("/_stats");
assertThat(
ObjectPath.evaluate(statsForDefaultProject, "_all.total.docs.count"),
equalTo(numDocsDefaultOnly + numDocsDefaultOf2AndDefault + numDocsDefaultAll)
);
assertThat(
ObjectPath.<Map<String, Object>>evaluate(statsForDefaultProject, "indices").keySet(),
containsInAnyOrder("my-index-default-project-only", "my-index-projects-2-and-default", "my-index-all-projects")
);
assertThat(
ObjectPath.evaluate(statsForDefaultProject, "indices.my-index-all-projects.total.docs.count"),
equalTo(numDocsDefaultAll)
);

// Check single-index stats for each project.
assertThat(
ObjectPath.evaluate(getAsOrderedMapInProject("/my-index-all-projects/_stats", "project-1"), "_all.total.docs.count"),
equalTo(numDocs1All)
);
assertThat(
ObjectPath.evaluate(getAsOrderedMapInProject("/my-index-all-projects/_stats", "project-2"), "_all.total.docs.count"),
equalTo(numDocs2All)
);
assertThat(
ObjectPath.evaluate(getAsOrderedMap("/my-index-all-projects/_stats"), "_all.total.docs.count"),
equalTo(numDocsDefaultAll)
);

// Check that getting single-index stats for an index that does not exist in a project returns a 404.
ResponseException exception = assertThrows(
ResponseException.class,
() -> client().performRequest(setRequestProjectId(new Request("GET", "/my-index-project-1-only/_stats"), "project-2"))
);
assertThat(exception.getResponse().getStatusLine().getStatusCode(), equalTo(404));

// Check using a wildcard gets the matching indices for that project.
Map<String, Object> statsWithWildcardForProject1 = getAsOrderedMapInProject("/my-index-project*/_stats", "project-1");
assertThat(ObjectPath.evaluate(statsWithWildcardForProject1, "_all.total.docs.count"), equalTo(numDocs1Only + numDocs1Of1And2));
assertThat(
ObjectPath.<Map<String, Object>>evaluate(statsWithWildcardForProject1, "indices").keySet(),
containsInAnyOrder("my-index-project-1-only", "my-index-projects-1-and-2")
);
}

private int createPopulatedIndex(String projectId, String indexName) throws IOException {
createIndex(req -> {
setRequestProjectId(req, projectId);
return client().performRequest(req);
}, indexName, null, null, null);
int numDocs = randomIntBetween(5, 10);
for (int i = 0; i < numDocs; i++) {
Request request = new Request("POST", "/" + indexName + "/_doc");
request.setJsonEntity(Strings.format("{ \"num\": %d, \"str\": \"%s\" }", randomInt(), randomAlphaOfLengthBetween(5, 10)));
setRequestProjectId(request, projectId);
client().performRequest(request);
}
client().performRequest(setRequestProjectId(new Request("POST", "/" + indexName + "/_refresh"), projectId));
return numDocs;
}

private static Map<String, Object> getAsOrderedMapInProject(String endpoint, String projectId) throws IOException {
return responseAsOrderedMap(client().performRequest(setRequestProjectId(new Request("GET", endpoint), projectId)));
}
}