Add tests for _remote/info API (elastic/x-pack-elasticsearch#1009)
Relates to elastic/elasticsearch#23925 Depends on elastic/elasticsearch#23969 Original commit: elastic/x-pack-elasticsearch@d1e8754a57
This commit is contained in:
parent
d4d505fb97
commit
617c3ead5c
|
@ -1,68 +0,0 @@
|
||||||
/*
|
|
||||||
* Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
|
|
||||||
* or more contributor license agreements. Licensed under the Elastic License;
|
|
||||||
* you may not use this file except in compliance with the Elastic License.
|
|
||||||
*/
|
|
||||||
package org.elasticsearch.xpack.common;
|
|
||||||
|
|
||||||
import org.elasticsearch.action.ActionListener;
|
|
||||||
import org.elasticsearch.common.util.concurrent.AtomicArray;
|
|
||||||
import org.elasticsearch.common.util.concurrent.CountDown;
|
|
||||||
|
|
||||||
import java.util.Collection;
|
|
||||||
import java.util.Collections;
|
|
||||||
import java.util.List;
|
|
||||||
import java.util.Objects;
|
|
||||||
import java.util.concurrent.atomic.AtomicInteger;
|
|
||||||
import java.util.concurrent.atomic.AtomicReference;
|
|
||||||
import java.util.stream.Collectors;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* An action listener that delegates it's results to another listener once
|
|
||||||
* it has received one or more failures or N results. This allows synchronous
|
|
||||||
* tasks to be forked off in a loop with the same listener and respond to a higher level listener once all tasks responded.
|
|
||||||
*/
|
|
||||||
public final class GroupedActionListener<T> implements ActionListener<T> {
|
|
||||||
private final CountDown countDown;
|
|
||||||
private final AtomicInteger pos = new AtomicInteger();
|
|
||||||
private final AtomicArray<T> roles;
|
|
||||||
private final ActionListener<Collection<T>> delegate;
|
|
||||||
private final Collection<T> defaults;
|
|
||||||
private final AtomicReference<Exception> failure = new AtomicReference<>();
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Creates a new listener
|
|
||||||
* @param delegate the delegate listener
|
|
||||||
* @param groupSize the group size
|
|
||||||
*/
|
|
||||||
public GroupedActionListener(ActionListener<Collection<T>> delegate, int groupSize, Collection<T> defaults) {
|
|
||||||
roles = new AtomicArray<>(groupSize);
|
|
||||||
countDown = new CountDown(groupSize);
|
|
||||||
this.delegate = delegate;
|
|
||||||
this.defaults = defaults;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void onResponse(T element) {
|
|
||||||
roles.set(pos.incrementAndGet() - 1, element);
|
|
||||||
if (countDown.countDown()) {
|
|
||||||
if (failure.get() != null) {
|
|
||||||
delegate.onFailure(failure.get());
|
|
||||||
} else {
|
|
||||||
List<T> collect = this.roles.asList();
|
|
||||||
collect.addAll(defaults);
|
|
||||||
delegate.onResponse(Collections.unmodifiableList(collect));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void onFailure(Exception e) {
|
|
||||||
if (failure.compareAndSet(null, e) == false) {
|
|
||||||
failure.get().addSuppressed(e);
|
|
||||||
}
|
|
||||||
if (countDown.countDown()) {
|
|
||||||
delegate.onFailure(failure.get());
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -5,18 +5,15 @@
|
||||||
*/
|
*/
|
||||||
package org.elasticsearch.xpack.security.action.user;
|
package org.elasticsearch.xpack.security.action.user;
|
||||||
|
|
||||||
import org.apache.logging.log4j.message.ParameterizedMessage;
|
|
||||||
import org.apache.logging.log4j.util.Supplier;
|
|
||||||
import org.elasticsearch.action.ActionListener;
|
import org.elasticsearch.action.ActionListener;
|
||||||
import org.elasticsearch.action.support.ActionFilters;
|
import org.elasticsearch.action.support.ActionFilters;
|
||||||
|
import org.elasticsearch.action.support.GroupedActionListener;
|
||||||
import org.elasticsearch.action.support.HandledTransportAction;
|
import org.elasticsearch.action.support.HandledTransportAction;
|
||||||
import org.elasticsearch.cluster.metadata.IndexNameExpressionResolver;
|
import org.elasticsearch.cluster.metadata.IndexNameExpressionResolver;
|
||||||
import org.elasticsearch.common.inject.Inject;
|
import org.elasticsearch.common.inject.Inject;
|
||||||
import org.elasticsearch.common.settings.Settings;
|
import org.elasticsearch.common.settings.Settings;
|
||||||
import org.elasticsearch.threadpool.ThreadPool;
|
import org.elasticsearch.threadpool.ThreadPool;
|
||||||
import org.elasticsearch.transport.TransportService;
|
import org.elasticsearch.transport.TransportService;
|
||||||
import org.elasticsearch.xpack.common.GroupedActionListener;
|
|
||||||
import org.elasticsearch.xpack.monitoring.collector.Collector;
|
|
||||||
import org.elasticsearch.xpack.security.authc.esnative.NativeUsersStore;
|
import org.elasticsearch.xpack.security.authc.esnative.NativeUsersStore;
|
||||||
import org.elasticsearch.xpack.security.authc.esnative.ReservedRealm;
|
import org.elasticsearch.xpack.security.authc.esnative.ReservedRealm;
|
||||||
import org.elasticsearch.xpack.security.user.SystemUser;
|
import org.elasticsearch.xpack.security.user.SystemUser;
|
||||||
|
@ -30,8 +27,6 @@ import java.util.List;
|
||||||
import java.util.Objects;
|
import java.util.Objects;
|
||||||
import java.util.stream.Collectors;
|
import java.util.stream.Collectors;
|
||||||
|
|
||||||
import static org.elasticsearch.common.Strings.arrayToDelimitedString;
|
|
||||||
|
|
||||||
public class TransportGetUsersAction extends HandledTransportAction<GetUsersRequest, GetUsersResponse> {
|
public class TransportGetUsersAction extends HandledTransportAction<GetUsersRequest, GetUsersResponse> {
|
||||||
|
|
||||||
private final NativeUsersStore usersStore;
|
private final NativeUsersStore usersStore;
|
||||||
|
|
|
@ -8,6 +8,7 @@ package org.elasticsearch.xpack.security.authc.esnative;
|
||||||
import org.apache.logging.log4j.Logger;
|
import org.apache.logging.log4j.Logger;
|
||||||
import org.elasticsearch.Version;
|
import org.elasticsearch.Version;
|
||||||
import org.elasticsearch.action.ActionListener;
|
import org.elasticsearch.action.ActionListener;
|
||||||
|
import org.elasticsearch.action.support.GroupedActionListener;
|
||||||
import org.elasticsearch.action.support.WriteRequest;
|
import org.elasticsearch.action.support.WriteRequest;
|
||||||
import org.elasticsearch.action.update.UpdateResponse;
|
import org.elasticsearch.action.update.UpdateResponse;
|
||||||
import org.elasticsearch.client.Requests;
|
import org.elasticsearch.client.Requests;
|
||||||
|
@ -18,7 +19,6 @@ import org.elasticsearch.common.settings.Settings;
|
||||||
import org.elasticsearch.index.query.QueryBuilders;
|
import org.elasticsearch.index.query.QueryBuilders;
|
||||||
import org.elasticsearch.license.XPackLicenseState;
|
import org.elasticsearch.license.XPackLicenseState;
|
||||||
import org.elasticsearch.search.SearchHit;
|
import org.elasticsearch.search.SearchHit;
|
||||||
import org.elasticsearch.xpack.common.GroupedActionListener;
|
|
||||||
import org.elasticsearch.xpack.security.InternalClient;
|
import org.elasticsearch.xpack.security.InternalClient;
|
||||||
import org.elasticsearch.xpack.security.SecurityLifecycleService;
|
import org.elasticsearch.xpack.security.SecurityLifecycleService;
|
||||||
import org.elasticsearch.xpack.security.authc.support.Hasher;
|
import org.elasticsearch.xpack.security.authc.support.Hasher;
|
||||||
|
@ -40,8 +40,6 @@ import java.util.function.BiConsumer;
|
||||||
import static java.util.Collections.emptyList;
|
import static java.util.Collections.emptyList;
|
||||||
import static org.elasticsearch.xpack.security.SecurityLifecycleService.SECURITY_INDEX_NAME;
|
import static org.elasticsearch.xpack.security.SecurityLifecycleService.SECURITY_INDEX_NAME;
|
||||||
|
|
||||||
import java.util.ArrayList;
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Performs migration steps for the {@link NativeRealm} and {@link ReservedRealm}.
|
* Performs migration steps for the {@link NativeRealm} and {@link ReservedRealm}.
|
||||||
* When upgrading an Elasticsearch/X-Pack installation from a previous version, this class is responsible for ensuring that user/role
|
* When upgrading an Elasticsearch/X-Pack installation from a previous version, this class is responsible for ensuring that user/role
|
||||||
|
|
|
@ -1,107 +0,0 @@
|
||||||
/*
|
|
||||||
* Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
|
|
||||||
* or more contributor license agreements. Licensed under the Elastic License;
|
|
||||||
* you may not use this file except in compliance with the Elastic License.
|
|
||||||
*/
|
|
||||||
package org.elasticsearch.xpack.common;
|
|
||||||
|
|
||||||
import org.elasticsearch.action.ActionListener;
|
|
||||||
import org.elasticsearch.test.ESTestCase;
|
|
||||||
|
|
||||||
import java.io.IOException;
|
|
||||||
import java.util.ArrayList;
|
|
||||||
import java.util.Collection;
|
|
||||||
import java.util.Collections;
|
|
||||||
import java.util.concurrent.CyclicBarrier;
|
|
||||||
import java.util.concurrent.TimeUnit;
|
|
||||||
import java.util.concurrent.atomic.AtomicInteger;
|
|
||||||
import java.util.concurrent.atomic.AtomicReference;
|
|
||||||
|
|
||||||
public class GroupedActionListenerTests extends ESTestCase {
|
|
||||||
|
|
||||||
public void testNotifications() throws InterruptedException {
|
|
||||||
AtomicReference<Collection<Integer>> resRef = new AtomicReference<>();
|
|
||||||
ActionListener<Collection<Integer>> result = new ActionListener<Collection<Integer>>() {
|
|
||||||
@Override
|
|
||||||
public void onResponse(Collection<Integer> integers) {
|
|
||||||
resRef.set(integers);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void onFailure(Exception e) {
|
|
||||||
throw new AssertionError(e);
|
|
||||||
}
|
|
||||||
};
|
|
||||||
final int groupSize = randomIntBetween(10, 1000);
|
|
||||||
AtomicInteger count = new AtomicInteger();
|
|
||||||
Collection<Integer> defaults = randomBoolean() ? Collections.singletonList(-1) : Collections.emptyList();
|
|
||||||
GroupedActionListener<Integer> listener = new GroupedActionListener<>(result, groupSize, defaults);
|
|
||||||
int numThreads = randomIntBetween(2, 5);
|
|
||||||
Thread[] threads = new Thread[numThreads];
|
|
||||||
CyclicBarrier barrier = new CyclicBarrier(numThreads);
|
|
||||||
for (int i = 0; i < numThreads; i++) {
|
|
||||||
threads[i] = new Thread() {
|
|
||||||
@Override
|
|
||||||
public void run() {
|
|
||||||
try {
|
|
||||||
barrier.await(10, TimeUnit.SECONDS);
|
|
||||||
} catch (Exception e) {
|
|
||||||
throw new AssertionError(e);
|
|
||||||
}
|
|
||||||
int c = 0;
|
|
||||||
while((c = count.incrementAndGet()) <= groupSize) {
|
|
||||||
listener.onResponse(c-1);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
};
|
|
||||||
threads[i].start();
|
|
||||||
}
|
|
||||||
for (Thread t : threads) {
|
|
||||||
t.join();
|
|
||||||
}
|
|
||||||
assertNotNull(resRef.get());
|
|
||||||
ArrayList<Integer> list = new ArrayList<>(resRef.get());
|
|
||||||
Collections.sort(list);
|
|
||||||
int expectedSize = groupSize + defaults.size();
|
|
||||||
assertEquals(expectedSize, resRef.get().size());
|
|
||||||
int expectedValue = defaults.isEmpty() ? 0 : -1;
|
|
||||||
for (int i = 0; i < expectedSize; i++) {
|
|
||||||
assertEquals(Integer.valueOf(expectedValue++), list.get(i));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public void testFailed() {
|
|
||||||
AtomicReference<Collection<Integer>> resRef = new AtomicReference<>();
|
|
||||||
AtomicReference<Exception> excRef = new AtomicReference<>();
|
|
||||||
|
|
||||||
ActionListener<Collection<Integer>> result = new ActionListener<Collection<Integer>>() {
|
|
||||||
@Override
|
|
||||||
public void onResponse(Collection<Integer> integers) {
|
|
||||||
resRef.set(integers);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void onFailure(Exception e) {
|
|
||||||
excRef.set(e);
|
|
||||||
}
|
|
||||||
};
|
|
||||||
Collection<Integer> defaults = randomBoolean() ? Collections.singletonList(-1) : Collections.emptyList();
|
|
||||||
int size = randomIntBetween(3, 4);
|
|
||||||
GroupedActionListener<Integer> listener = new GroupedActionListener<>(result, size, defaults);
|
|
||||||
listener.onResponse(0);
|
|
||||||
IOException ioException = new IOException();
|
|
||||||
RuntimeException rtException = new RuntimeException();
|
|
||||||
listener.onFailure(rtException);
|
|
||||||
listener.onFailure(ioException);
|
|
||||||
if (size == 4) {
|
|
||||||
listener.onResponse(2);
|
|
||||||
}
|
|
||||||
assertNotNull(excRef.get());
|
|
||||||
assertEquals(rtException, excRef.get());
|
|
||||||
assertEquals(1, excRef.get().getSuppressed().length);
|
|
||||||
assertEquals(ioException, excRef.get().getSuppressed()[0]);
|
|
||||||
assertNull(resRef.get());
|
|
||||||
listener.onResponse(1);
|
|
||||||
assertNull(resRef.get());
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -0,0 +1,84 @@
|
||||||
|
---
|
||||||
|
setup:
|
||||||
|
- skip:
|
||||||
|
features: headers
|
||||||
|
|
||||||
|
- do:
|
||||||
|
cluster.health:
|
||||||
|
wait_for_status: yellow
|
||||||
|
- do:
|
||||||
|
xpack.security.put_user:
|
||||||
|
username: "joe"
|
||||||
|
body: >
|
||||||
|
{
|
||||||
|
"password": "s3krit",
|
||||||
|
"roles" : [ "x_cluster_role" ]
|
||||||
|
}
|
||||||
|
- do:
|
||||||
|
xpack.security.put_role:
|
||||||
|
name: "x_cluster_role"
|
||||||
|
body: >
|
||||||
|
{
|
||||||
|
"cluster": ["monitor"]
|
||||||
|
}
|
||||||
|
---
|
||||||
|
teardown:
|
||||||
|
- do:
|
||||||
|
xpack.security.delete_user:
|
||||||
|
username: "joe"
|
||||||
|
ignore: 404
|
||||||
|
- do:
|
||||||
|
xpack.security.delete_role:
|
||||||
|
name: "monitor_role"
|
||||||
|
ignore: 404
|
||||||
|
---
|
||||||
|
"Fetch remote cluster info for existing cluster":
|
||||||
|
|
||||||
|
- do:
|
||||||
|
headers: { Authorization: "Basic am9lOnMza3JpdA==" }
|
||||||
|
remote.info: {}
|
||||||
|
- match: { my_remote_cluster.connected: true }
|
||||||
|
- match: { my_remote_cluster.num_nodes_connected: 1}
|
||||||
|
- match: { my_remote_cluster.max_connections_per_cluster: 1}
|
||||||
|
- match: { my_remote_cluster.initial_connect_timeout: "30s" }
|
||||||
|
- is_true: my_remote_cluster.http_addresses.0
|
||||||
|
|
||||||
|
|
||||||
|
---
|
||||||
|
"Add transient remote cluster based on the preset cluster and check remote info":
|
||||||
|
- do:
|
||||||
|
cluster.get_settings:
|
||||||
|
include_defaults: true
|
||||||
|
|
||||||
|
- set: { defaults.search.remote.my_remote_cluster.seeds.0: remote_ip }
|
||||||
|
|
||||||
|
- do:
|
||||||
|
cluster.put_settings:
|
||||||
|
flat_settings: true
|
||||||
|
body:
|
||||||
|
transient:
|
||||||
|
search.remote.test_remote_cluster.seeds: $remote_ip
|
||||||
|
|
||||||
|
- match: {transient: {search.remote.test_remote_cluster.seeds: $remote_ip}}
|
||||||
|
|
||||||
|
- do:
|
||||||
|
headers: { Authorization: "Basic am9lOnMza3JpdA==" }
|
||||||
|
remote.info: {}
|
||||||
|
- set: { my_remote_cluster.http_addresses.0: remote_http }
|
||||||
|
- match: { test_remote_cluster.http_addresses.0: $remote_http }
|
||||||
|
|
||||||
|
- match: { test_remote_cluster.connected: true }
|
||||||
|
- match: { my_remote_cluster.connected: true }
|
||||||
|
|
||||||
|
- match: { test_remote_cluster.seeds.0: $remote_ip }
|
||||||
|
- match: { my_remote_cluster.seeds.0: $remote_ip }
|
||||||
|
|
||||||
|
- match: { my_remote_cluster.max_connections_per_cluster: 1}
|
||||||
|
- match: { test_remote_cluster.max_connections_per_cluster: 1}
|
||||||
|
|
||||||
|
- match: { my_remote_cluster.num_nodes_connected: 1}
|
||||||
|
- match: { test_remote_cluster.num_nodes_connected: 1}
|
||||||
|
|
||||||
|
- match: { my_remote_cluster.initial_connect_timeout: "30s" }
|
||||||
|
- match: { test_remote_cluster.initial_connect_timeout: "30s" }
|
||||||
|
|
|
@ -19,7 +19,7 @@ setup:
|
||||||
name: "x_cluster_role"
|
name: "x_cluster_role"
|
||||||
body: >
|
body: >
|
||||||
{
|
{
|
||||||
"cluster": ["all"],
|
"cluster": ["monitor"],
|
||||||
"indices": [
|
"indices": [
|
||||||
{
|
{
|
||||||
"names": "*",
|
"names": "*",
|
||||||
|
|
Loading…
Reference in New Issue