Skip to content

Commit

Permalink
Merge remote-tracking branch 'elastic/master' into ccr
Browse files Browse the repository at this point in the history
* elastic/master:
  Enable setting client path prefix to / (elastic#30119)
  [DOCS] Secure settings specified per node (elastic#31621)
  has_parent builder: exception message/param fix (elastic#31182)
  • Loading branch information
jasontedor committed Jul 1, 2018
2 parents e46d23d + 2971dd5 commit 05b4517
Show file tree
Hide file tree
Showing 9 changed files with 57 additions and 21 deletions.
Expand Up @@ -794,8 +794,10 @@ static URI buildUri(String pathPrefix, String path, Map<String, String> params)
Objects.requireNonNull(path, "path must not be null");
try {
String fullPath;
if (pathPrefix != null) {
if (path.startsWith("/")) {
if (pathPrefix != null && pathPrefix.isEmpty() == false) {
if (pathPrefix.endsWith("/") && path.startsWith("/")) {
fullPath = pathPrefix.substring(0, pathPrefix.length() - 1) + path;
} else if (pathPrefix.endsWith("/") || path.startsWith("/")) {
fullPath = pathPrefix + path;
} else {
fullPath = pathPrefix + "/" + path;
Expand Down
Expand Up @@ -143,32 +143,33 @@ public RestClientBuilder setRequestConfigCallback(RequestConfigCallback requestC
* For example, if this is set to "/my/path", then any client request will become <code>"/my/path/" + endpoint</code>.
* <p>
* In essence, every request's {@code endpoint} is prefixed by this {@code pathPrefix}. The path prefix is useful for when
* Elasticsearch is behind a proxy that provides a base path; it is not intended for other purposes and it should not be supplied in
* other scenarios.
* Elasticsearch is behind a proxy that provides a base path or a proxy that requires all paths to start with '/';
* it is not intended for other purposes and it should not be supplied in other scenarios.
*
* @throws NullPointerException if {@code pathPrefix} is {@code null}.
* @throws IllegalArgumentException if {@code pathPrefix} is empty, only '/', or ends with more than one '/'.
* @throws IllegalArgumentException if {@code pathPrefix} is empty, or ends with more than one '/'.
*/
public RestClientBuilder setPathPrefix(String pathPrefix) {
Objects.requireNonNull(pathPrefix, "pathPrefix must not be null");
String cleanPathPrefix = pathPrefix;

if (pathPrefix.isEmpty()) {
throw new IllegalArgumentException("pathPrefix must not be empty");
}

String cleanPathPrefix = pathPrefix;
if (cleanPathPrefix.startsWith("/") == false) {
cleanPathPrefix = "/" + cleanPathPrefix;
}

// best effort to ensure that it looks like "/base/path" rather than "/base/path/"
if (cleanPathPrefix.endsWith("/")) {
if (cleanPathPrefix.endsWith("/") && cleanPathPrefix.length() > 1) {
cleanPathPrefix = cleanPathPrefix.substring(0, cleanPathPrefix.length() - 1);

if (cleanPathPrefix.endsWith("/")) {
throw new IllegalArgumentException("pathPrefix is malformed. too many trailing slashes: [" + pathPrefix + "]");
}
}

if (cleanPathPrefix.isEmpty() || "/".equals(cleanPathPrefix)) {
throw new IllegalArgumentException("pathPrefix must not be empty or '/': [" + pathPrefix + "]");
}

this.pathPrefix = cleanPathPrefix;
return this;
Expand Down
Expand Up @@ -180,7 +180,6 @@ public void testSetPathPrefixNull() {
}

public void testSetPathPrefixEmpty() {
assertSetPathPrefixThrows("/");
assertSetPathPrefixThrows("");
}

Expand Down
Expand Up @@ -223,12 +223,33 @@ public void onFailure(Exception exception) {
}

public void testBuildUriLeavesPathUntouched() {
final Map<String, String> emptyMap = Collections.emptyMap();
{
URI uri = RestClient.buildUri("/foo$bar", "/index/type/id", Collections.<String, String>emptyMap());
URI uri = RestClient.buildUri("/foo$bar", "/index/type/id", emptyMap);
assertEquals("/foo$bar/index/type/id", uri.getPath());
}
{
URI uri = RestClient.buildUri(null, "/foo$bar/ty/pe/i/d", Collections.<String, String>emptyMap());
URI uri = RestClient.buildUri("/", "/*", emptyMap);
assertEquals("/*", uri.getPath());
}
{
URI uri = RestClient.buildUri("/", "*", emptyMap);
assertEquals("/*", uri.getPath());
}
{
URI uri = RestClient.buildUri(null, "*", emptyMap);
assertEquals("*", uri.getPath());
}
{
URI uri = RestClient.buildUri("", "*", emptyMap);
assertEquals("*", uri.getPath());
}
{
URI uri = RestClient.buildUri(null, "/*", emptyMap);
assertEquals("/*", uri.getPath());
}
{
URI uri = RestClient.buildUri(null, "/foo$bar/ty/pe/i/d", emptyMap);
assertEquals("/foo$bar/ty/pe/i/d", uri.getPath());
}
{
Expand Down
6 changes: 5 additions & 1 deletion docs/reference/setup/secure-settings.asciidoc
@@ -1,5 +1,5 @@
[[secure-settings]]
=== Secure Settings
=== Secure settings

Some settings are sensitive, and relying on filesystem permissions to protect
their values is not sufficient. For this use case, Elasticsearch provides a
Expand All @@ -16,6 +16,10 @@ Elasticsearch.
NOTE: The elasticsearch keystore currently only provides obfuscation. In the future,
password protection will be added.

These settings, just like the regular ones in the `elasticsearch.yml` config file,
need to be specified on each node in the cluster. Currently, all secure settings
are node-specific settings that must have the same value on every node.

[float]
[[creating-keystore]]
=== Creating the keystore
Expand Down
Expand Up @@ -58,7 +58,7 @@ public class HasParentQueryBuilder extends AbstractQueryBuilder<HasParentQueryBu
public static final boolean DEFAULT_IGNORE_UNMAPPED = false;

private static final ParseField QUERY_FIELD = new ParseField("query");
private static final ParseField TYPE_FIELD = new ParseField("parent_type");
private static final ParseField PARENT_TYPE_FIELD = new ParseField("parent_type");
private static final ParseField SCORE_FIELD = new ParseField("score");
private static final ParseField INNER_HITS_FIELD = new ParseField("inner_hits");
private static final ParseField IGNORE_UNMAPPED_FIELD = new ParseField("ignore_unmapped");
Expand All @@ -74,8 +74,8 @@ public HasParentQueryBuilder(String type, QueryBuilder query, boolean score) {
}

private HasParentQueryBuilder(String type, QueryBuilder query, boolean score, InnerHitBuilder innerHitBuilder) {
this.type = requireValue(type, "[" + NAME + "] requires 'type' field");
this.query = requireValue(query, "[" + NAME + "] requires 'query' field");
this.type = requireValue(type, "[" + NAME + "] requires '" + PARENT_TYPE_FIELD.getPreferredName() + "' field");
this.query = requireValue(query, "[" + NAME + "] requires '" + QUERY_FIELD.getPreferredName() + "' field");
this.score = score;
this.innerHitBuilder = innerHitBuilder;
}
Expand Down Expand Up @@ -201,7 +201,7 @@ protected void doXContent(XContentBuilder builder, Params params) throws IOExcep
builder.startObject(NAME);
builder.field(QUERY_FIELD.getPreferredName());
query.toXContent(builder, params);
builder.field(TYPE_FIELD.getPreferredName(), type);
builder.field(PARENT_TYPE_FIELD.getPreferredName(), type);
builder.field(SCORE_FIELD.getPreferredName(), score);
builder.field(IGNORE_UNMAPPED_FIELD.getPreferredName(), ignoreUnmapped);
printBoostAndQueryName(builder);
Expand Down Expand Up @@ -235,7 +235,7 @@ public static HasParentQueryBuilder fromXContent(XContentParser parser) throws I
"[has_parent] query does not support [" + currentFieldName + "]");
}
} else if (token.isValue()) {
if (TYPE_FIELD.match(currentFieldName, parser.getDeprecationHandler())) {
if (PARENT_TYPE_FIELD.match(currentFieldName, parser.getDeprecationHandler())) {
parentType = parser.text();
} else if (SCORE_FIELD.match(currentFieldName, parser.getDeprecationHandler())) {
score = parser.booleanValue();
Expand Down
Expand Up @@ -183,7 +183,7 @@ public void testIllegalValues() throws IOException {
QueryBuilder query = new MatchAllQueryBuilder();
IllegalArgumentException e = expectThrows(IllegalArgumentException.class,
() -> hasParentQuery(null, query, false));
assertThat(e.getMessage(), equalTo("[has_parent] requires 'type' field"));
assertThat(e.getMessage(), equalTo("[has_parent] requires 'parent_type' field"));

e = expectThrows(IllegalArgumentException.class,
() -> hasParentQuery("foo", null, false));
Expand Down
Expand Up @@ -90,6 +90,7 @@ public abstract class ESRestTestCase extends ESTestCase {
public static final String TRUSTSTORE_PASSWORD = "truststore.password";
public static final String CLIENT_RETRY_TIMEOUT = "client.retry.timeout";
public static final String CLIENT_SOCKET_TIMEOUT = "client.socket.timeout";
public static final String CLIENT_PATH_PREFIX = "client.path.prefix";

/**
* Convert the entity from a {@link Response} into a map of maps.
Expand Down Expand Up @@ -383,7 +384,11 @@ private void waitForClusterStateUpdatesToFinish() throws Exception {
* Used to obtain settings for the REST client that is used to send REST requests.
*/
protected Settings restClientSettings() {
return Settings.EMPTY;
Settings.Builder builder = Settings.builder();
if (System.getProperty("tests.rest.client_path_prefix") != null) {
builder.put(CLIENT_PATH_PREFIX, System.getProperty("tests.rest.client_path_prefix"));
}
return builder.build();
}

/**
Expand Down Expand Up @@ -454,6 +459,9 @@ protected static void configureClient(RestClientBuilder builder, Settings settin
final TimeValue socketTimeout = TimeValue.parseTimeValue(socketTimeoutString, CLIENT_SOCKET_TIMEOUT);
builder.setRequestConfigCallback(conf -> conf.setSocketTimeout(Math.toIntExact(socketTimeout.getMillis())));
}
if (settings.hasValue(CLIENT_PATH_PREFIX)) {
builder.setPathPrefix(settings.get(CLIENT_PATH_PREFIX));
}
}

@SuppressWarnings("unchecked")
Expand Down
Expand Up @@ -39,6 +39,7 @@ public static Iterable<Object[]> parameters() throws Exception {
protected Settings restClientSettings() {
String token = basicAuthHeaderValue(USER, new SecureString(PASS.toCharArray()));
return Settings.builder()
.put(super.restClientSettings())
.put(ThreadContext.PREFIX + ".Authorization", token)
.build();
}
Expand Down

0 comments on commit 05b4517

Please sign in to comment.