Merge branch 'stable-2.15' into stable-2.16 * stable-2.15: Add support for Elasticsearch version 7.3.* Also update ElasticV7QueryProjectsTest to use V7_3. Change-Id: Ib7081c5b15756e2ca91d448430928b2f5b8aca50
diff --git a/gerrit-elasticsearch/src/main/java/com/google/gerrit/elasticsearch/ElasticAccountIndex.java b/gerrit-elasticsearch/src/main/java/com/google/gerrit/elasticsearch/ElasticAccountIndex.java deleted file mode 100644 index 722e6d8..0000000 --- a/gerrit-elasticsearch/src/main/java/com/google/gerrit/elasticsearch/ElasticAccountIndex.java +++ /dev/null
@@ -1,207 +0,0 @@ -// Copyright (C) 2016 The Android Open Source Project -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -package com.google.gerrit.elasticsearch; - -import static com.google.gerrit.server.index.account.AccountField.ID; - -import com.google.common.collect.Lists; -import com.google.gerrit.elasticsearch.ElasticMapping.MappingProperties; -import com.google.gerrit.elasticsearch.builders.QueryBuilder; -import com.google.gerrit.elasticsearch.builders.SearchSourceBuilder; -import com.google.gerrit.elasticsearch.bulk.BulkRequest; -import com.google.gerrit.elasticsearch.bulk.IndexRequest; -import com.google.gerrit.elasticsearch.bulk.UpdateRequest; -import com.google.gerrit.index.QueryOptions; -import com.google.gerrit.index.Schema; -import com.google.gerrit.index.query.DataSource; -import com.google.gerrit.index.query.Predicate; -import com.google.gerrit.index.query.QueryParseException; -import com.google.gerrit.reviewdb.client.Account; -import com.google.gerrit.server.account.AccountCache; -import com.google.gerrit.server.account.AccountState; -import com.google.gerrit.server.config.SitePaths; -import com.google.gerrit.server.index.IndexUtils; -import com.google.gerrit.server.index.account.AccountField; -import com.google.gerrit.server.index.account.AccountIndex; -import com.google.gson.JsonArray; -import com.google.gson.JsonElement; -import com.google.gson.JsonObject; -import com.google.gson.JsonParser; -import com.google.gwtorm.server.OrmException; -import com.google.gwtorm.server.ResultSet; -import com.google.inject.Provider; -import com.google.inject.assistedinject.Assisted; -import com.google.inject.assistedinject.AssistedInject; -import java.io.IOException; -import java.util.Collections; -import java.util.Iterator; -import java.util.List; -import java.util.Set; -import org.apache.http.HttpStatus; -import org.apache.http.StatusLine; -import org.elasticsearch.client.Response; -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; - -public class ElasticAccountIndex extends AbstractElasticIndex<Account.Id, AccountState> - implements AccountIndex { - private static final Logger log = LoggerFactory.getLogger(ElasticAccountIndex.class); - - static class AccountMapping { - final MappingProperties accounts; - - AccountMapping(Schema<AccountState> schema, ElasticQueryAdapter adapter) { - this.accounts = ElasticMapping.createMapping(schema, adapter); - } - } - - private static final String ACCOUNTS = "accounts"; - - private final AccountMapping mapping; - private final Provider<AccountCache> accountCache; - private final Schema<AccountState> schema; - - @AssistedInject - ElasticAccountIndex( - ElasticConfiguration cfg, - SitePaths sitePaths, - Provider<AccountCache> accountCache, - ElasticRestClientProvider client, - @Assisted Schema<AccountState> schema) { - super(cfg, sitePaths, schema, client, ACCOUNTS); - this.accountCache = accountCache; - this.mapping = new AccountMapping(schema, client.adapter()); - this.schema = schema; - } - - @Override - public void replace(AccountState as) throws IOException { - BulkRequest bulk = - new IndexRequest(getId(as), indexName, type, client.adapter()) - .add(new UpdateRequest<>(schema, as)); - - String uri = getURI(type, BULK); - Response response = postRequest(uri, bulk, getRefreshParam()); - int statusCode = response.getStatusLine().getStatusCode(); - if (statusCode != HttpStatus.SC_OK) { - throw new IOException( - String.format( - "Failed to replace account %s in index %s: %s", - as.getAccount().getId(), indexName, statusCode)); - } - } - - @Override - public DataSource<AccountState> getSource(Predicate<AccountState> p, QueryOptions opts) - throws QueryParseException { - return new QuerySource(p, opts); - } - - @Override - protected String getDeleteActions(Account.Id a) { - return delete(type, a); - } - - @Override - protected String getMappings() { - return getMappingsForSingleType(ACCOUNTS, mapping.accounts); - } - - @Override - protected String getId(AccountState as) { - return as.getAccount().getId().toString(); - } - - private class QuerySource implements DataSource<AccountState> { - private final String search; - private final Set<String> fields; - - QuerySource(Predicate<AccountState> p, QueryOptions opts) throws QueryParseException { - QueryBuilder qb = queryBuilder.toQueryBuilder(p); - fields = IndexUtils.accountFields(opts); - SearchSourceBuilder searchSource = - new SearchSourceBuilder(client.adapter()) - .query(qb) - .from(opts.start()) - .size(opts.limit()) - .fields(Lists.newArrayList(fields)); - - JsonArray sortArray = getSortArray(AccountField.ID.getName()); - search = getSearch(searchSource, sortArray); - } - - @Override - public int getCardinality() { - return 10; - } - - @Override - public ResultSet<AccountState> read() throws OrmException { - try { - List<AccountState> results = Collections.emptyList(); - String uri = getURI(type, SEARCH); - Response response = postRequest(uri, search); - StatusLine statusLine = response.getStatusLine(); - if (statusLine.getStatusCode() == HttpStatus.SC_OK) { - String content = getContent(response); - JsonObject obj = - new JsonParser().parse(content).getAsJsonObject().getAsJsonObject("hits"); - if (obj.get("hits") != null) { - JsonArray json = obj.getAsJsonArray("hits"); - results = Lists.newArrayListWithCapacity(json.size()); - for (int i = 0; i < json.size(); i++) { - results.add(toAccountState(json.get(i))); - } - } - } else { - log.error(statusLine.getReasonPhrase()); - } - final List<AccountState> r = Collections.unmodifiableList(results); - return new ResultSet<AccountState>() { - @Override - public Iterator<AccountState> iterator() { - return r.iterator(); - } - - @Override - public List<AccountState> toList() { - return r; - } - - @Override - public void close() { - // Do nothing. - } - }; - } catch (IOException e) { - throw new OrmException(e); - } - } - - private AccountState toAccountState(JsonElement json) { - JsonElement source = json.getAsJsonObject().get("_source"); - if (source == null) { - source = json.getAsJsonObject().get("fields"); - } - - Account.Id id = new Account.Id(source.getAsJsonObject().get(ID.getName()).getAsInt()); - // Use the AccountCache rather than depending on any stored fields in the - // document (of which there shouldn't be any). The most expensive part to - // compute anyway is the effective group IDs, and we don't have a good way - // to reindex when those change. - return accountCache.get().get(id); - } - } -}
diff --git a/gerrit-elasticsearch/src/main/java/com/google/gerrit/elasticsearch/ElasticChangeIndex.java b/gerrit-elasticsearch/src/main/java/com/google/gerrit/elasticsearch/ElasticChangeIndex.java deleted file mode 100644 index 3cb56f4..0000000 --- a/gerrit-elasticsearch/src/main/java/com/google/gerrit/elasticsearch/ElasticChangeIndex.java +++ /dev/null
@@ -1,466 +0,0 @@ -// Copyright (C) 2014 The Android Open Source Project -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -package com.google.gerrit.elasticsearch; - -import static com.google.common.base.Preconditions.checkNotNull; -import static com.google.gerrit.server.index.change.ChangeField.APPROVAL_CODEC; -import static com.google.gerrit.server.index.change.ChangeField.CHANGE_CODEC; -import static com.google.gerrit.server.index.change.ChangeField.PATCH_SET_CODEC; -import static com.google.gerrit.server.index.change.ChangeIndexRewriter.CLOSED_STATUSES; -import static com.google.gerrit.server.index.change.ChangeIndexRewriter.OPEN_STATUSES; -import static java.nio.charset.StandardCharsets.UTF_8; -import static org.apache.commons.codec.binary.Base64.decodeBase64; - -import com.google.common.collect.FluentIterable; -import com.google.common.collect.ImmutableMap; -import com.google.common.collect.Iterables; -import com.google.common.collect.ListMultimap; -import com.google.common.collect.Lists; -import com.google.common.collect.MultimapBuilder; -import com.google.common.collect.Sets; -import com.google.gerrit.elasticsearch.ElasticMapping.MappingProperties; -import com.google.gerrit.elasticsearch.builders.QueryBuilder; -import com.google.gerrit.elasticsearch.builders.SearchSourceBuilder; -import com.google.gerrit.elasticsearch.bulk.BulkRequest; -import com.google.gerrit.elasticsearch.bulk.DeleteRequest; -import com.google.gerrit.elasticsearch.bulk.IndexRequest; -import com.google.gerrit.elasticsearch.bulk.UpdateRequest; -import com.google.gerrit.index.QueryOptions; -import com.google.gerrit.index.Schema; -import com.google.gerrit.index.query.Predicate; -import com.google.gerrit.index.query.QueryParseException; -import com.google.gerrit.reviewdb.client.Account; -import com.google.gerrit.reviewdb.client.Change; -import com.google.gerrit.reviewdb.client.Change.Id; -import com.google.gerrit.reviewdb.client.Project; -import com.google.gerrit.reviewdb.server.ReviewDb; -import com.google.gerrit.server.ReviewerByEmailSet; -import com.google.gerrit.server.ReviewerSet; -import com.google.gerrit.server.config.SitePaths; -import com.google.gerrit.server.index.IndexUtils; -import com.google.gerrit.server.index.change.ChangeField; -import com.google.gerrit.server.index.change.ChangeIndex; -import com.google.gerrit.server.index.change.ChangeIndexRewriter; -import com.google.gerrit.server.project.SubmitRuleOptions; -import com.google.gerrit.server.query.change.ChangeData; -import com.google.gerrit.server.query.change.ChangeDataSource; -import com.google.gson.JsonArray; -import com.google.gson.JsonElement; -import com.google.gson.JsonObject; -import com.google.gson.JsonParser; -import com.google.gwtorm.server.OrmException; -import com.google.gwtorm.server.ResultSet; -import com.google.inject.Inject; -import com.google.inject.Provider; -import com.google.inject.assistedinject.Assisted; -import java.io.IOException; -import java.io.UnsupportedEncodingException; -import java.util.Collections; -import java.util.Iterator; -import java.util.List; -import java.util.Set; -import org.apache.commons.codec.binary.Base64; -import org.apache.http.HttpStatus; -import org.apache.http.StatusLine; -import org.elasticsearch.client.Response; -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; - -/** Secondary index implementation using Elasticsearch. */ -class ElasticChangeIndex extends AbstractElasticIndex<Change.Id, ChangeData> - implements ChangeIndex { - private static final Logger log = LoggerFactory.getLogger(ElasticChangeIndex.class); - - static class ChangeMapping { - final MappingProperties changes; - final MappingProperties openChanges; - final MappingProperties closedChanges; - - ChangeMapping(Schema<ChangeData> schema, ElasticQueryAdapter adapter) { - MappingProperties mapping = ElasticMapping.createMapping(schema, adapter); - this.changes = mapping; - this.openChanges = mapping; - this.closedChanges = mapping; - } - } - - private static final String CHANGES = "changes"; - private static final String OPEN_CHANGES = "open_" + CHANGES; - private static final String CLOSED_CHANGES = "closed_" + CHANGES; - private static final String ALL_CHANGES = OPEN_CHANGES + "," + CLOSED_CHANGES; - private final ChangeMapping mapping; - private final Provider<ReviewDb> db; - private final ChangeData.Factory changeDataFactory; - private final Schema<ChangeData> schema; - - @Inject - ElasticChangeIndex( - ElasticConfiguration cfg, - Provider<ReviewDb> db, - ChangeData.Factory changeDataFactory, - SitePaths sitePaths, - ElasticRestClientProvider client, - @Assisted Schema<ChangeData> schema) { - super(cfg, sitePaths, schema, client, CHANGES, ALL_CHANGES); - this.db = db; - this.changeDataFactory = changeDataFactory; - this.schema = schema; - this.mapping = new ChangeMapping(schema, client.adapter()); - } - - @Override - public void replace(ChangeData cd) throws IOException { - String deleteIndex; - String insertIndex; - - try { - if (cd.change().getStatus().isOpen()) { - insertIndex = OPEN_CHANGES; - deleteIndex = CLOSED_CHANGES; - } else { - insertIndex = CLOSED_CHANGES; - deleteIndex = OPEN_CHANGES; - } - } catch (OrmException e) { - throw new IOException(e); - } - - ElasticQueryAdapter adapter = client.adapter(); - BulkRequest bulk = - new IndexRequest(getId(cd), indexName, adapter.getType(insertIndex), adapter) - .add(new UpdateRequest<>(schema, cd)); - if (adapter.deleteToReplace()) { - bulk.add(new DeleteRequest(cd.getId().toString(), indexName, deleteIndex, adapter)); - } - - String uri = getURI(type, BULK); - Response response = postRequest(uri, bulk, getRefreshParam()); - int statusCode = response.getStatusLine().getStatusCode(); - if (statusCode != HttpStatus.SC_OK) { - throw new IOException( - String.format( - "Failed to replace change %s in index %s: %s", cd.getId(), indexName, statusCode)); - } - } - - @Override - public ChangeDataSource getSource(Predicate<ChangeData> p, QueryOptions opts) - throws QueryParseException { - Set<Change.Status> statuses = ChangeIndexRewriter.getPossibleStatus(p); - List<String> indexes = Lists.newArrayListWithCapacity(2); - if (!client.adapter().omitType()) { - if (client.adapter().useV6Type()) { - if (!Sets.intersection(statuses, OPEN_STATUSES).isEmpty() - || !Sets.intersection(statuses, CLOSED_STATUSES).isEmpty()) { - indexes.add(ElasticQueryAdapter.V6_TYPE); - } - } else { - if (!Sets.intersection(statuses, OPEN_STATUSES).isEmpty()) { - indexes.add(OPEN_CHANGES); - } - if (!Sets.intersection(statuses, CLOSED_STATUSES).isEmpty()) { - indexes.add(CLOSED_CHANGES); - } - } - } - return new QuerySource(indexes, p, opts); - } - - @Override - protected String getDeleteActions(Id c) { - if (!client.adapter().useV5Type()) { - return delete(client.adapter().getType(), c); - } - return delete(OPEN_CHANGES, c) + delete(CLOSED_CHANGES, c); - } - - @Override - protected String getMappings() { - if (!client.adapter().useV5Type()) { - return getMappingsFor(client.adapter().getType(), mapping.changes); - } - return gson.toJson(ImmutableMap.of(MAPPINGS, mapping)); - } - - @Override - protected String getId(ChangeData cd) { - return cd.getId().toString(); - } - - private class QuerySource implements ChangeDataSource { - private final String search; - private final Set<String> fields; - private final List<String> types; - - QuerySource(List<String> types, Predicate<ChangeData> p, QueryOptions opts) - throws QueryParseException { - QueryBuilder qb = queryBuilder.toQueryBuilder(p); - fields = IndexUtils.changeFields(opts); - SearchSourceBuilder searchSource = - new SearchSourceBuilder(client.adapter()) - .query(qb) - .from(opts.start()) - .size(opts.limit()) - .fields(Lists.newArrayList(fields)); - - search = getSearch(searchSource, getSortArray()); - this.types = types; - } - - @Override - public int getCardinality() { - return 10; - } - - @Override - public ResultSet<ChangeData> read() throws OrmException { - try { - List<ChangeData> results = Collections.emptyList(); - String uri = getURI(types); - Response response = postRequest(uri, search); - StatusLine statusLine = response.getStatusLine(); - if (statusLine.getStatusCode() == HttpStatus.SC_OK) { - String content = getContent(response); - JsonObject obj = - new JsonParser().parse(content).getAsJsonObject().getAsJsonObject("hits"); - if (obj.get("hits") != null) { - JsonArray json = obj.getAsJsonArray("hits"); - results = Lists.newArrayListWithCapacity(json.size()); - for (int i = 0; i < json.size(); i++) { - results.add(toChangeData(json.get(i))); - } - } - } else { - log.error(statusLine.getReasonPhrase()); - } - final List<ChangeData> r = Collections.unmodifiableList(results); - return new ResultSet<ChangeData>() { - @Override - public Iterator<ChangeData> iterator() { - return r.iterator(); - } - - @Override - public List<ChangeData> toList() { - return r; - } - - @Override - public void close() { - // Do nothing. - } - }; - } catch (IOException e) { - throw new OrmException(e); - } - } - - @Override - public boolean hasChange() { - return false; - } - - private ChangeData toChangeData(JsonElement json) { - JsonElement sourceElement = json.getAsJsonObject().get("_source"); - if (sourceElement == null) { - sourceElement = json.getAsJsonObject().get("fields"); - } - JsonObject source = sourceElement.getAsJsonObject(); - JsonElement c = source.get(ChangeField.CHANGE.getName()); - - if (c == null) { - int id = source.get(ChangeField.LEGACY_ID.getName()).getAsInt(); - // IndexUtils#changeFields ensures either CHANGE or PROJECT is always present. - String projectName = checkNotNull(source.get(ChangeField.PROJECT.getName()).getAsString()); - return changeDataFactory.create( - db.get(), new Project.NameKey(projectName), new Change.Id(id)); - } - - ChangeData cd = - changeDataFactory.create( - db.get(), CHANGE_CODEC.decode(Base64.decodeBase64(c.getAsString()))); - - // Patch sets. - cd.setPatchSets(decodeProtos(source, ChangeField.PATCH_SET.getName(), PATCH_SET_CODEC)); - - // Approvals. - if (source.get(ChangeField.APPROVAL.getName()) != null) { - cd.setCurrentApprovals( - decodeProtos(source, ChangeField.APPROVAL.getName(), APPROVAL_CODEC)); - } else if (fields.contains(ChangeField.APPROVAL.getName())) { - cd.setCurrentApprovals(Collections.emptyList()); - } - - JsonElement addedElement = source.get(ChangeField.ADDED.getName()); - JsonElement deletedElement = source.get(ChangeField.DELETED.getName()); - if (addedElement != null && deletedElement != null) { - // Changed lines. - int added = addedElement.getAsInt(); - int deleted = deletedElement.getAsInt(); - cd.setChangedLines(added, deleted); - } - - // Star. - JsonElement starredElement = source.get(ChangeField.STAR.getName()); - if (starredElement != null) { - ListMultimap<Account.Id, String> stars = - MultimapBuilder.hashKeys().arrayListValues().build(); - JsonArray starBy = starredElement.getAsJsonArray(); - if (starBy.size() > 0) { - for (int i = 0; i < starBy.size(); i++) { - String[] indexableFields = starBy.get(i).getAsString().split(":"); - Account.Id id = Account.Id.parse(indexableFields[0]); - stars.put(id, indexableFields[1]); - } - } - cd.setStars(stars); - } - - // Mergeable. - JsonElement mergeableElement = source.get(ChangeField.MERGEABLE.getName()); - if (mergeableElement != null) { - String mergeable = mergeableElement.getAsString(); - if ("1".equals(mergeable)) { - cd.setMergeable(true); - } else if ("0".equals(mergeable)) { - cd.setMergeable(false); - } - } - - // Reviewed-by. - if (source.get(ChangeField.REVIEWEDBY.getName()) != null) { - JsonArray reviewedBy = source.get(ChangeField.REVIEWEDBY.getName()).getAsJsonArray(); - if (reviewedBy.size() > 0) { - Set<Account.Id> accounts = Sets.newHashSetWithExpectedSize(reviewedBy.size()); - for (int i = 0; i < reviewedBy.size(); i++) { - int aId = reviewedBy.get(i).getAsInt(); - if (reviewedBy.size() == 1 && aId == ChangeField.NOT_REVIEWED) { - break; - } - accounts.add(new Account.Id(aId)); - } - cd.setReviewedBy(accounts); - } - } else if (fields.contains(ChangeField.REVIEWEDBY.getName())) { - cd.setReviewedBy(Collections.emptySet()); - } - - if (source.get(ChangeField.REVIEWER.getName()) != null) { - cd.setReviewers( - ChangeField.parseReviewerFieldValues( - FluentIterable.from(source.get(ChangeField.REVIEWER.getName()).getAsJsonArray()) - .transform(JsonElement::getAsString))); - } else if (fields.contains(ChangeField.REVIEWER.getName())) { - cd.setReviewers(ReviewerSet.empty()); - } - - if (source.get(ChangeField.REVIEWER_BY_EMAIL.getName()) != null) { - cd.setReviewersByEmail( - ChangeField.parseReviewerByEmailFieldValues( - FluentIterable.from( - source.get(ChangeField.REVIEWER_BY_EMAIL.getName()).getAsJsonArray()) - .transform(JsonElement::getAsString))); - } else if (fields.contains(ChangeField.REVIEWER_BY_EMAIL.getName())) { - cd.setReviewersByEmail(ReviewerByEmailSet.empty()); - } - - if (source.get(ChangeField.PENDING_REVIEWER.getName()) != null) { - cd.setPendingReviewers( - ChangeField.parseReviewerFieldValues( - FluentIterable.from( - source.get(ChangeField.PENDING_REVIEWER.getName()).getAsJsonArray()) - .transform(JsonElement::getAsString))); - } else if (fields.contains(ChangeField.PENDING_REVIEWER.getName())) { - cd.setPendingReviewers(ReviewerSet.empty()); - } - - if (source.get(ChangeField.PENDING_REVIEWER_BY_EMAIL.getName()) != null) { - cd.setPendingReviewersByEmail( - ChangeField.parseReviewerByEmailFieldValues( - FluentIterable.from( - source - .get(ChangeField.PENDING_REVIEWER_BY_EMAIL.getName()) - .getAsJsonArray()) - .transform(JsonElement::getAsString))); - } else if (fields.contains(ChangeField.PENDING_REVIEWER_BY_EMAIL.getName())) { - cd.setPendingReviewersByEmail(ReviewerByEmailSet.empty()); - } - decodeSubmitRecords( - source, - ChangeField.STORED_SUBMIT_RECORD_STRICT.getName(), - ChangeField.SUBMIT_RULE_OPTIONS_STRICT, - cd); - decodeSubmitRecords( - source, - ChangeField.STORED_SUBMIT_RECORD_LENIENT.getName(), - ChangeField.SUBMIT_RULE_OPTIONS_LENIENT, - cd); - decodeUnresolvedCommentCount(source, ChangeField.UNRESOLVED_COMMENT_COUNT.getName(), cd); - - if (fields.contains(ChangeField.REF_STATE.getName())) { - cd.setRefStates(getByteArray(source, ChangeField.REF_STATE.getName())); - } - if (fields.contains(ChangeField.REF_STATE_PATTERN.getName())) { - cd.setRefStatePatterns(getByteArray(source, ChangeField.REF_STATE_PATTERN.getName())); - } - - return cd; - } - - private Iterable<byte[]> getByteArray(JsonObject source, String name) { - JsonElement element = source.get(name); - return element != null - ? Iterables.transform(element.getAsJsonArray(), e -> Base64.decodeBase64(e.getAsString())) - : Collections.emptyList(); - } - - private void decodeSubmitRecords( - JsonObject doc, String fieldName, SubmitRuleOptions opts, ChangeData out) { - JsonArray records = doc.getAsJsonArray(fieldName); - if (records == null) { - return; - } - ChangeField.parseSubmitRecords( - FluentIterable.from(records) - .transform(i -> new String(decodeBase64(i.toString()), UTF_8)) - .toList(), - opts, - out); - } - - private void decodeUnresolvedCommentCount(JsonObject doc, String fieldName, ChangeData out) { - JsonElement count = doc.get(fieldName); - if (count == null) { - return; - } - out.setUnresolvedCommentCount(count.getAsInt()); - } - - private JsonArray getSortArray() { - JsonObject properties = new JsonObject(); - properties.addProperty(ORDER, "desc"); - - JsonArray sortArray = new JsonArray(); - addNamedElement(ChangeField.UPDATED.getName(), properties, sortArray); - addNamedElement(ChangeField.LEGACY_ID.getName(), properties, sortArray); - return sortArray; - } - } - - private String getURI(List<String> types) throws UnsupportedEncodingException { - String joinedTypes = String.join(",", types); - return getURI(joinedTypes, SEARCH); - } -}
diff --git a/gerrit-elasticsearch/src/main/java/com/google/gerrit/elasticsearch/ElasticGroupIndex.java b/gerrit-elasticsearch/src/main/java/com/google/gerrit/elasticsearch/ElasticGroupIndex.java deleted file mode 100644 index 79701e1..0000000 --- a/gerrit-elasticsearch/src/main/java/com/google/gerrit/elasticsearch/ElasticGroupIndex.java +++ /dev/null
@@ -1,206 +0,0 @@ -// Copyright (C) 2017 The Android Open Source Project -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -package com.google.gerrit.elasticsearch; - -import com.google.common.collect.Lists; -import com.google.gerrit.elasticsearch.ElasticMapping.MappingProperties; -import com.google.gerrit.elasticsearch.builders.QueryBuilder; -import com.google.gerrit.elasticsearch.builders.SearchSourceBuilder; -import com.google.gerrit.elasticsearch.bulk.BulkRequest; -import com.google.gerrit.elasticsearch.bulk.IndexRequest; -import com.google.gerrit.elasticsearch.bulk.UpdateRequest; -import com.google.gerrit.index.QueryOptions; -import com.google.gerrit.index.Schema; -import com.google.gerrit.index.query.DataSource; -import com.google.gerrit.index.query.Predicate; -import com.google.gerrit.index.query.QueryParseException; -import com.google.gerrit.reviewdb.client.AccountGroup; -import com.google.gerrit.server.account.GroupCache; -import com.google.gerrit.server.config.SitePaths; -import com.google.gerrit.server.group.InternalGroup; -import com.google.gerrit.server.index.IndexUtils; -import com.google.gerrit.server.index.group.GroupField; -import com.google.gerrit.server.index.group.GroupIndex; -import com.google.gson.JsonArray; -import com.google.gson.JsonElement; -import com.google.gson.JsonObject; -import com.google.gson.JsonParser; -import com.google.gwtorm.server.OrmException; -import com.google.gwtorm.server.ResultSet; -import com.google.inject.Provider; -import com.google.inject.assistedinject.Assisted; -import com.google.inject.assistedinject.AssistedInject; -import java.io.IOException; -import java.util.Collections; -import java.util.Iterator; -import java.util.List; -import java.util.Optional; -import java.util.Set; -import org.apache.http.HttpStatus; -import org.apache.http.StatusLine; -import org.elasticsearch.client.Response; -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; - -public class ElasticGroupIndex extends AbstractElasticIndex<AccountGroup.UUID, InternalGroup> - implements GroupIndex { - private static final Logger log = LoggerFactory.getLogger(ElasticGroupIndex.class); - - static class GroupMapping { - final MappingProperties groups; - - GroupMapping(Schema<InternalGroup> schema, ElasticQueryAdapter adapter) { - this.groups = ElasticMapping.createMapping(schema, adapter); - } - } - - private static final String GROUPS = "groups"; - - private final GroupMapping mapping; - private final Provider<GroupCache> groupCache; - private final Schema<InternalGroup> schema; - - @AssistedInject - ElasticGroupIndex( - ElasticConfiguration cfg, - SitePaths sitePaths, - Provider<GroupCache> groupCache, - ElasticRestClientProvider client, - @Assisted Schema<InternalGroup> schema) { - super(cfg, sitePaths, schema, client, GROUPS); - this.groupCache = groupCache; - this.mapping = new GroupMapping(schema, client.adapter()); - this.schema = schema; - } - - @Override - public void replace(InternalGroup group) throws IOException { - BulkRequest bulk = - new IndexRequest(getId(group), indexName, type, client.adapter()) - .add(new UpdateRequest<>(schema, group)); - - String uri = getURI(type, BULK); - Response response = postRequest(uri, bulk, getRefreshParam()); - int statusCode = response.getStatusLine().getStatusCode(); - if (statusCode != HttpStatus.SC_OK) { - throw new IOException( - String.format( - "Failed to replace group %s in index %s: %s", - group.getGroupUUID().get(), indexName, statusCode)); - } - } - - @Override - public DataSource<InternalGroup> getSource(Predicate<InternalGroup> p, QueryOptions opts) - throws QueryParseException { - return new QuerySource(p, opts); - } - - @Override - protected String getDeleteActions(AccountGroup.UUID g) { - return delete(type, g); - } - - @Override - protected String getMappings() { - return getMappingsForSingleType(GROUPS, mapping.groups); - } - - @Override - protected String getId(InternalGroup group) { - return group.getGroupUUID().get(); - } - - private class QuerySource implements DataSource<InternalGroup> { - private final String search; - private final Set<String> fields; - - QuerySource(Predicate<InternalGroup> p, QueryOptions opts) throws QueryParseException { - QueryBuilder qb = queryBuilder.toQueryBuilder(p); - fields = IndexUtils.groupFields(opts); - SearchSourceBuilder searchSource = - new SearchSourceBuilder(client.adapter()) - .query(qb) - .from(opts.start()) - .size(opts.limit()) - .fields(Lists.newArrayList(fields)); - - JsonArray sortArray = getSortArray(GroupField.UUID.getName()); - search = getSearch(searchSource, sortArray); - } - - @Override - public int getCardinality() { - return 10; - } - - @Override - public ResultSet<InternalGroup> read() throws OrmException { - try { - List<InternalGroup> results = Collections.emptyList(); - String uri = getURI(type, SEARCH); - Response response = postRequest(uri, search); - StatusLine statusLine = response.getStatusLine(); - if (statusLine.getStatusCode() == HttpStatus.SC_OK) { - String content = getContent(response); - JsonObject obj = - new JsonParser().parse(content).getAsJsonObject().getAsJsonObject("hits"); - if (obj.get("hits") != null) { - JsonArray json = obj.getAsJsonArray("hits"); - results = Lists.newArrayListWithCapacity(json.size()); - for (int i = 0; i < json.size(); i++) { - results.add(toAccountGroup(json.get(i)).get()); - } - } - } else { - log.error(statusLine.getReasonPhrase()); - } - final List<InternalGroup> r = Collections.unmodifiableList(results); - return new ResultSet<InternalGroup>() { - @Override - public Iterator<InternalGroup> iterator() { - return r.iterator(); - } - - @Override - public List<InternalGroup> toList() { - return r; - } - - @Override - public void close() { - // Do nothing. - } - }; - } catch (IOException e) { - throw new OrmException(e); - } - } - - private Optional<InternalGroup> toAccountGroup(JsonElement json) { - JsonElement source = json.getAsJsonObject().get("_source"); - if (source == null) { - source = json.getAsJsonObject().get("fields"); - } - - AccountGroup.UUID uuid = - new AccountGroup.UUID( - source.getAsJsonObject().get(GroupField.UUID.getName()).getAsString()); - // Use the GroupCache rather than depending on any stored fields in the - // document (of which there shouldn't be any). - return groupCache.get().get(uuid); - } - } -}
diff --git a/gerrit-elasticsearch/src/main/java/com/google/gerrit/elasticsearch/AbstractElasticIndex.java b/java/com/google/gerrit/elasticsearch/AbstractElasticIndex.java similarity index 63% rename from gerrit-elasticsearch/src/main/java/com/google/gerrit/elasticsearch/AbstractElasticIndex.java rename to java/com/google/gerrit/elasticsearch/AbstractElasticIndex.java index a5db030..d206e88 100644 --- a/gerrit-elasticsearch/src/main/java/com/google/gerrit/elasticsearch/AbstractElasticIndex.java +++ b/java/com/google/gerrit/elasticsearch/AbstractElasticIndex.java
@@ -14,45 +14,70 @@ package com.google.gerrit.elasticsearch; +import static com.google.common.base.Preconditions.checkArgument; import static com.google.gson.FieldNamingPolicy.LOWER_CASE_WITH_UNDERSCORES; import static java.nio.charset.StandardCharsets.UTF_8; import static org.apache.commons.codec.binary.Base64.decodeBase64; +import com.google.common.collect.ArrayListMultimap; import com.google.common.collect.FluentIterable; import com.google.common.collect.ImmutableMap; +import com.google.common.collect.ListMultimap; +import com.google.common.collect.Lists; +import com.google.common.flogger.FluentLogger; import com.google.common.io.CharStreams; import com.google.gerrit.common.Nullable; import com.google.gerrit.elasticsearch.ElasticMapping.MappingProperties; +import com.google.gerrit.elasticsearch.builders.QueryBuilder; import com.google.gerrit.elasticsearch.builders.SearchSourceBuilder; import com.google.gerrit.elasticsearch.bulk.DeleteRequest; +import com.google.gerrit.index.FieldDef; +import com.google.gerrit.index.FieldType; import com.google.gerrit.index.Index; +import com.google.gerrit.index.QueryOptions; import com.google.gerrit.index.Schema; +import com.google.gerrit.index.query.DataSource; +import com.google.gerrit.index.query.FieldBundle; +import com.google.gerrit.index.query.Predicate; +import com.google.gerrit.index.query.QueryParseException; import com.google.gerrit.server.config.SitePaths; import com.google.gerrit.server.index.IndexUtils; import com.google.gson.Gson; import com.google.gson.GsonBuilder; import com.google.gson.JsonArray; +import com.google.gson.JsonElement; import com.google.gson.JsonObject; import com.google.gson.JsonParser; import com.google.gwtorm.protobuf.ProtobufCodec; +import com.google.gwtorm.server.OrmException; +import com.google.gwtorm.server.ResultSet; import java.io.IOException; import java.io.InputStream; import java.io.InputStreamReader; import java.io.Reader; import java.io.UnsupportedEncodingException; import java.net.URLEncoder; +import java.sql.Timestamp; import java.util.Collections; import java.util.HashMap; +import java.util.Iterator; import java.util.List; import java.util.Map; +import java.util.Set; +import java.util.function.Function; +import org.apache.commons.codec.binary.Base64; import org.apache.http.HttpEntity; import org.apache.http.HttpStatus; +import org.apache.http.StatusLine; +import org.apache.http.client.methods.HttpPost; import org.apache.http.entity.ContentType; import org.apache.http.nio.entity.NStringEntity; import org.elasticsearch.client.Request; import org.elasticsearch.client.Response; abstract class AbstractElasticIndex<K, V> implements Index<K, V> { + private static final FluentLogger logger = FluentLogger.forEnclosingClass(); + protected static final String BULK = "_bulk"; protected static final String MAPPINGS = "mappings"; protected static final String ORDER = "order"; @@ -204,6 +229,50 @@ return new DeleteRequest(id.toString(), indexName, type, client.adapter()).toString(); } + protected abstract V fromDocument(JsonObject doc, Set<String> fields); + + protected FieldBundle toFieldBundle(JsonObject doc) { + Map<String, FieldDef<V, ?>> allFields = getSchema().getFields(); + ListMultimap<String, Object> rawFields = ArrayListMultimap.create(); + for (Map.Entry<String, JsonElement> element : + doc.get(client.adapter().rawFieldsKey()).getAsJsonObject().entrySet()) { + checkArgument( + allFields.containsKey(element.getKey()), "Unrecognized field " + element.getKey()); + FieldType<?> type = allFields.get(element.getKey()).getType(); + Iterable<JsonElement> innerItems = + element.getValue().isJsonArray() + ? element.getValue().getAsJsonArray() + : Collections.singleton(element.getValue()); + for (JsonElement inner : innerItems) { + if (type == FieldType.EXACT || type == FieldType.FULL_TEXT || type == FieldType.PREFIX) { + rawFields.put(element.getKey(), inner.getAsString()); + } else if (type == FieldType.INTEGER || type == FieldType.INTEGER_RANGE) { + rawFields.put(element.getKey(), inner.getAsInt()); + } else if (type == FieldType.LONG) { + rawFields.put(element.getKey(), inner.getAsLong()); + } else if (type == FieldType.TIMESTAMP) { + rawFields.put(element.getKey(), new Timestamp(inner.getAsLong())); + } else if (type == FieldType.STORED_ONLY) { + rawFields.put(element.getKey(), Base64.decodeBase64(inner.getAsString())); + } else { + throw FieldType.badFieldType(type); + } + } + } + return new FieldBundle(rawFields); + } + + protected String toAction(String type, String id, String action) { + JsonObject properties = new JsonObject(); + properties.addProperty("_id", id); + properties.addProperty("_index", indexName); + properties.addProperty("_type", type); + + JsonObject jsonAction = new JsonObject(); + jsonAction.add(action, properties); + return jsonAction.toString() + System.lineSeparator(); + } + protected void addNamedElement(String name, JsonObject element, JsonArray array) { JsonObject arrayElement = new JsonObject(); arrayElement.add(name, element); @@ -276,4 +345,85 @@ } return client.get().performRequest(request); } + + protected class ElasticQuerySource implements DataSource<V> { + private final QueryOptions opts; + private final String search; + private final String index; + + ElasticQuerySource(Predicate<V> p, QueryOptions opts, String index, JsonArray sortArray) + throws QueryParseException { + this.opts = opts; + this.index = index; + QueryBuilder qb = queryBuilder.toQueryBuilder(p); + SearchSourceBuilder searchSource = + new SearchSourceBuilder(client.adapter()) + .query(qb) + .from(opts.start()) + .size(opts.limit()) + .fields(Lists.newArrayList(opts.fields())); + search = getSearch(searchSource, sortArray); + } + + @Override + public int getCardinality() { + return 10; + } + + @Override + public ResultSet<V> read() throws OrmException { + return readImpl((doc) -> AbstractElasticIndex.this.fromDocument(doc, opts.fields())); + } + + @Override + public ResultSet<FieldBundle> readRaw() throws OrmException { + return readImpl(AbstractElasticIndex.this::toFieldBundle); + } + + private <T> ResultSet<T> readImpl(Function<JsonObject, T> mapper) throws OrmException { + try { + List<T> results = Collections.emptyList(); + String uri = getURI(index, SEARCH); + Response response = + performRequest(HttpPost.METHOD_NAME, uri, search, Collections.emptyMap()); + StatusLine statusLine = response.getStatusLine(); + if (statusLine.getStatusCode() == HttpStatus.SC_OK) { + String content = getContent(response); + JsonObject obj = + new JsonParser().parse(content).getAsJsonObject().getAsJsonObject("hits"); + if (obj.get("hits") != null) { + JsonArray json = obj.getAsJsonArray("hits"); + results = Lists.newArrayListWithCapacity(json.size()); + for (int i = 0; i < json.size(); i++) { + T mapperResult = mapper.apply(json.get(i).getAsJsonObject()); + if (mapperResult != null) { + results.add(mapperResult); + } + } + } + } else { + logger.atSevere().log(statusLine.getReasonPhrase()); + } + final List<T> r = Collections.unmodifiableList(results); + return new ResultSet<T>() { + @Override + public Iterator<T> iterator() { + return r.iterator(); + } + + @Override + public List<T> toList() { + return r; + } + + @Override + public void close() { + // Do nothing. + } + }; + } catch (IOException e) { + throw new OrmException(e); + } + } + } }
diff --git a/java/com/google/gerrit/elasticsearch/BUILD b/java/com/google/gerrit/elasticsearch/BUILD new file mode 100644 index 0000000..8d23051 --- /dev/null +++ b/java/com/google/gerrit/elasticsearch/BUILD
@@ -0,0 +1,31 @@ +java_library( + name = "elasticsearch", + srcs = glob(["**/*.java"]), + visibility = ["//visibility:public"], + deps = [ + "//java/com/google/gerrit/common:annotations", + "//java/com/google/gerrit/extensions:api", + "//java/com/google/gerrit/index", + "//java/com/google/gerrit/index:query_exception", + "//java/com/google/gerrit/index/project", + "//java/com/google/gerrit/lifecycle", + "//java/com/google/gerrit/reviewdb:server", + "//java/com/google/gerrit/server", + "//lib:gson", + "//lib:guava", + "//lib:gwtorm", + "//lib:protobuf", + "//lib/commons:codec", + "//lib/commons:lang", + "//lib/elasticsearch-rest-client", + "//lib/flogger:api", + "//lib/guice", + "//lib/guice:guice-assistedinject", + "//lib/httpcomponents:httpasyncclient", + "//lib/httpcomponents:httpclient", + "//lib/httpcomponents:httpcore", + "//lib/httpcomponents:httpcore-nio", + "//lib/jackson:jackson-core", + "//lib/jgit/org.eclipse.jgit:jgit", + ], +)
diff --git a/java/com/google/gerrit/elasticsearch/ElasticAccountIndex.java b/java/com/google/gerrit/elasticsearch/ElasticAccountIndex.java new file mode 100644 index 0000000..d0b70ae --- /dev/null +++ b/java/com/google/gerrit/elasticsearch/ElasticAccountIndex.java
@@ -0,0 +1,129 @@ +// Copyright (C) 2016 The Android Open Source Project +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +package com.google.gerrit.elasticsearch; + +import static com.google.gerrit.server.index.account.AccountField.ID; + +import com.google.gerrit.elasticsearch.ElasticMapping.MappingProperties; +import com.google.gerrit.elasticsearch.bulk.BulkRequest; +import com.google.gerrit.elasticsearch.bulk.IndexRequest; +import com.google.gerrit.elasticsearch.bulk.UpdateRequest; +import com.google.gerrit.index.QueryOptions; +import com.google.gerrit.index.Schema; +import com.google.gerrit.index.query.DataSource; +import com.google.gerrit.index.query.Predicate; +import com.google.gerrit.index.query.QueryParseException; +import com.google.gerrit.reviewdb.client.Account; +import com.google.gerrit.server.account.AccountCache; +import com.google.gerrit.server.account.AccountState; +import com.google.gerrit.server.config.SitePaths; +import com.google.gerrit.server.index.IndexUtils; +import com.google.gerrit.server.index.account.AccountField; +import com.google.gerrit.server.index.account.AccountIndex; +import com.google.gson.JsonArray; +import com.google.gson.JsonElement; +import com.google.gson.JsonObject; +import com.google.inject.Inject; +import com.google.inject.Provider; +import com.google.inject.assistedinject.Assisted; +import java.io.IOException; +import java.util.Set; +import org.apache.http.HttpStatus; +import org.elasticsearch.client.Response; + +public class ElasticAccountIndex extends AbstractElasticIndex<Account.Id, AccountState> + implements AccountIndex { + static class AccountMapping { + final MappingProperties accounts; + + AccountMapping(Schema<AccountState> schema, ElasticQueryAdapter adapter) { + this.accounts = ElasticMapping.createMapping(schema, adapter); + } + } + + private static final String ACCOUNTS = "accounts"; + + private final AccountMapping mapping; + private final Provider<AccountCache> accountCache; + private final Schema<AccountState> schema; + + @Inject + ElasticAccountIndex( + ElasticConfiguration cfg, + SitePaths sitePaths, + Provider<AccountCache> accountCache, + ElasticRestClientProvider client, + @Assisted Schema<AccountState> schema) { + super(cfg, sitePaths, schema, client, ACCOUNTS); + this.accountCache = accountCache; + this.mapping = new AccountMapping(schema, client.adapter()); + this.schema = schema; + } + + @Override + public void replace(AccountState as) throws IOException { + BulkRequest bulk = + new IndexRequest(getId(as), indexName, type, client.adapter()) + .add(new UpdateRequest<>(schema, as)); + + String uri = getURI(type, BULK); + Response response = postRequest(uri, bulk, getRefreshParam()); + int statusCode = response.getStatusLine().getStatusCode(); + if (statusCode != HttpStatus.SC_OK) { + throw new IOException( + String.format( + "Failed to replace account %s in index %s: %s", + as.getAccount().getId(), indexName, statusCode)); + } + } + + @Override + public DataSource<AccountState> getSource(Predicate<AccountState> p, QueryOptions opts) + throws QueryParseException { + JsonArray sortArray = getSortArray(AccountField.ID.getName()); + return new ElasticQuerySource(p, opts.filterFields(IndexUtils::accountFields), type, sortArray); + } + + @Override + protected String getDeleteActions(Account.Id a) { + return delete(type, a); + } + + @Override + protected String getMappings() { + return getMappingsForSingleType(ACCOUNTS, mapping.accounts); + } + + @Override + protected String getId(AccountState as) { + return as.getAccount().getId().toString(); + } + + @Override + protected AccountState fromDocument(JsonObject json, Set<String> fields) { + JsonElement source = json.get("_source"); + if (source == null) { + source = json.getAsJsonObject().get("fields"); + } + + Account.Id id = new Account.Id(source.getAsJsonObject().get(ID.getName()).getAsInt()); + // Use the AccountCache rather than depending on any stored fields in the document (of which + // there shouldn't be any). The most expensive part to compute anyway is the effective group + // IDs, and we don't have a good way to reindex when those change. + // If the account doesn't exist return an empty AccountState to represent the missing account + // to account the fact that the account exists in the index. + return accountCache.get().getEvenIfMissing(id); + } +}
diff --git a/java/com/google/gerrit/elasticsearch/ElasticChangeIndex.java b/java/com/google/gerrit/elasticsearch/ElasticChangeIndex.java new file mode 100644 index 0000000..5a43ba8 --- /dev/null +++ b/java/com/google/gerrit/elasticsearch/ElasticChangeIndex.java
@@ -0,0 +1,435 @@ +// Copyright (C) 2014 The Android Open Source Project +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +package com.google.gerrit.elasticsearch; + +import static com.google.gerrit.reviewdb.server.ReviewDbCodecs.APPROVAL_CODEC; +import static com.google.gerrit.reviewdb.server.ReviewDbCodecs.CHANGE_CODEC; +import static com.google.gerrit.reviewdb.server.ReviewDbCodecs.PATCH_SET_CODEC; +import static com.google.gerrit.server.index.change.ChangeIndexRewriter.CLOSED_STATUSES; +import static com.google.gerrit.server.index.change.ChangeIndexRewriter.OPEN_STATUSES; +import static java.nio.charset.StandardCharsets.UTF_8; +import static java.util.Objects.requireNonNull; +import static org.apache.commons.codec.binary.Base64.decodeBase64; + +import com.google.common.collect.FluentIterable; +import com.google.common.collect.ImmutableListMultimap; +import com.google.common.collect.ImmutableMap; +import com.google.common.collect.Iterables; +import com.google.common.collect.ListMultimap; +import com.google.common.collect.Lists; +import com.google.common.collect.MultimapBuilder; +import com.google.common.collect.Sets; +import com.google.gerrit.elasticsearch.ElasticMapping.MappingProperties; +import com.google.gerrit.elasticsearch.bulk.BulkRequest; +import com.google.gerrit.elasticsearch.bulk.DeleteRequest; +import com.google.gerrit.elasticsearch.bulk.IndexRequest; +import com.google.gerrit.elasticsearch.bulk.UpdateRequest; +import com.google.gerrit.index.QueryOptions; +import com.google.gerrit.index.Schema; +import com.google.gerrit.index.query.DataSource; +import com.google.gerrit.index.query.Predicate; +import com.google.gerrit.index.query.QueryParseException; +import com.google.gerrit.reviewdb.client.Account; +import com.google.gerrit.reviewdb.client.Change; +import com.google.gerrit.reviewdb.client.Change.Id; +import com.google.gerrit.reviewdb.client.Project; +import com.google.gerrit.reviewdb.server.ReviewDb; +import com.google.gerrit.server.ReviewerByEmailSet; +import com.google.gerrit.server.ReviewerSet; +import com.google.gerrit.server.StarredChangesUtil; +import com.google.gerrit.server.config.SitePaths; +import com.google.gerrit.server.index.IndexUtils; +import com.google.gerrit.server.index.change.ChangeField; +import com.google.gerrit.server.index.change.ChangeIndex; +import com.google.gerrit.server.index.change.ChangeIndexRewriter; +import com.google.gerrit.server.project.SubmitRuleOptions; +import com.google.gerrit.server.query.change.ChangeData; +import com.google.gson.JsonArray; +import com.google.gson.JsonElement; +import com.google.gson.JsonObject; +import com.google.gwtorm.server.OrmException; +import com.google.inject.Inject; +import com.google.inject.Provider; +import com.google.inject.assistedinject.Assisted; +import java.io.IOException; +import java.util.Collections; +import java.util.List; +import java.util.Optional; +import java.util.Set; +import org.apache.commons.codec.binary.Base64; +import org.apache.http.HttpStatus; +import org.elasticsearch.client.Response; + +/** Secondary index implementation using Elasticsearch. */ +class ElasticChangeIndex extends AbstractElasticIndex<Change.Id, ChangeData> + implements ChangeIndex { + static class ChangeMapping { + final MappingProperties changes; + final MappingProperties openChanges; + final MappingProperties closedChanges; + + ChangeMapping(Schema<ChangeData> schema, ElasticQueryAdapter adapter) { + MappingProperties mapping = ElasticMapping.createMapping(schema, adapter); + this.changes = mapping; + this.openChanges = mapping; + this.closedChanges = mapping; + } + } + + private static final String CHANGES = "changes"; + private static final String OPEN_CHANGES = "open_" + CHANGES; + private static final String CLOSED_CHANGES = "closed_" + CHANGES; + + private final ChangeMapping mapping; + private final Provider<ReviewDb> db; + private final ChangeData.Factory changeDataFactory; + private final Schema<ChangeData> schema; + + @Inject + ElasticChangeIndex( + ElasticConfiguration cfg, + Provider<ReviewDb> db, + ChangeData.Factory changeDataFactory, + SitePaths sitePaths, + ElasticRestClientProvider clientBuilder, + @Assisted Schema<ChangeData> schema) { + super(cfg, sitePaths, schema, clientBuilder, CHANGES); + this.db = db; + this.changeDataFactory = changeDataFactory; + this.schema = schema; + mapping = new ChangeMapping(schema, client.adapter()); + } + + @Override + public void replace(ChangeData cd) throws IOException { + String deleteIndex; + String insertIndex; + + try { + if (cd.change().getStatus().isOpen()) { + insertIndex = OPEN_CHANGES; + deleteIndex = CLOSED_CHANGES; + } else { + insertIndex = CLOSED_CHANGES; + deleteIndex = OPEN_CHANGES; + } + } catch (OrmException e) { + throw new IOException(e); + } + + ElasticQueryAdapter adapter = client.adapter(); + BulkRequest bulk = + new IndexRequest(getId(cd), indexName, adapter.getType(insertIndex), adapter) + .add(new UpdateRequest<>(schema, cd)); + if (adapter.deleteToReplace()) { + bulk.add(new DeleteRequest(cd.getId().toString(), indexName, deleteIndex, adapter)); + } + + String uri = getURI(type, BULK); + Response response = postRequest(uri, bulk, getRefreshParam()); + int statusCode = response.getStatusLine().getStatusCode(); + if (statusCode != HttpStatus.SC_OK) { + throw new IOException( + String.format( + "Failed to replace change %s in index %s: %s", cd.getId(), indexName, statusCode)); + } + } + + @Override + public DataSource<ChangeData> getSource(Predicate<ChangeData> p, QueryOptions opts) + throws QueryParseException { + Set<Change.Status> statuses = ChangeIndexRewriter.getPossibleStatus(p); + List<String> indexes = Lists.newArrayListWithCapacity(2); + if (!client.adapter().omitType()) { + if (client.adapter().useV6Type()) { + if (!Sets.intersection(statuses, OPEN_STATUSES).isEmpty() + || !Sets.intersection(statuses, CLOSED_STATUSES).isEmpty()) { + indexes.add(ElasticQueryAdapter.V6_TYPE); + } + } else { + if (!Sets.intersection(statuses, OPEN_STATUSES).isEmpty()) { + indexes.add(OPEN_CHANGES); + } + if (!Sets.intersection(statuses, CLOSED_STATUSES).isEmpty()) { + indexes.add(CLOSED_CHANGES); + } + } + } + + QueryOptions filteredOpts = opts.filterFields(IndexUtils::changeFields); + return new ElasticQuerySource(p, filteredOpts, getURI(indexes), getSortArray()); + } + + private JsonArray getSortArray() { + JsonObject properties = new JsonObject(); + properties.addProperty(ORDER, "desc"); + + JsonArray sortArray = new JsonArray(); + addNamedElement(ChangeField.UPDATED.getName(), properties, sortArray); + addNamedElement(ChangeField.LEGACY_ID.getName(), properties, sortArray); + return sortArray; + } + + private String getURI(List<String> types) { + return String.join(",", types); + } + + @Override + protected String getDeleteActions(Id c) { + if (!client.adapter().useV5Type()) { + return delete(client.adapter().getType(), c); + } + return delete(OPEN_CHANGES, c) + delete(CLOSED_CHANGES, c); + } + + @Override + protected String getMappings() { + if (!client.adapter().useV5Type()) { + return getMappingsFor(client.adapter().getType(), mapping.changes); + } + return gson.toJson(ImmutableMap.of(MAPPINGS, mapping)); + } + + @Override + protected String getId(ChangeData cd) { + return cd.getId().toString(); + } + + @Override + protected ChangeData fromDocument(JsonObject json, Set<String> fields) { + JsonElement sourceElement = json.get("_source"); + if (sourceElement == null) { + sourceElement = json.getAsJsonObject().get("fields"); + } + JsonObject source = sourceElement.getAsJsonObject(); + JsonElement c = source.get(ChangeField.CHANGE.getName()); + + if (c == null) { + int id = source.get(ChangeField.LEGACY_ID.getName()).getAsInt(); + // IndexUtils#changeFields ensures either CHANGE or PROJECT is always present. + String projectName = requireNonNull(source.get(ChangeField.PROJECT.getName()).getAsString()); + return changeDataFactory.create( + db.get(), new Project.NameKey(projectName), new Change.Id(id)); + } + + ChangeData cd = + changeDataFactory.create( + db.get(), CHANGE_CODEC.decode(Base64.decodeBase64(c.getAsString()))); + + // Any decoding that is done here must also be done in {@link LuceneChangeIndex}. + + // Patch sets. + cd.setPatchSets(decodeProtos(source, ChangeField.PATCH_SET.getName(), PATCH_SET_CODEC)); + + // Approvals. + if (source.get(ChangeField.APPROVAL.getName()) != null) { + cd.setCurrentApprovals(decodeProtos(source, ChangeField.APPROVAL.getName(), APPROVAL_CODEC)); + } else if (fields.contains(ChangeField.APPROVAL.getName())) { + cd.setCurrentApprovals(Collections.emptyList()); + } + + // Added & Deleted. + JsonElement addedElement = source.get(ChangeField.ADDED.getName()); + JsonElement deletedElement = source.get(ChangeField.DELETED.getName()); + if (addedElement != null && deletedElement != null) { + // Changed lines. + int added = addedElement.getAsInt(); + int deleted = deletedElement.getAsInt(); + cd.setChangedLines(added, deleted); + } + + // Star. + JsonElement starredElement = source.get(ChangeField.STAR.getName()); + if (starredElement != null) { + ListMultimap<Account.Id, String> stars = MultimapBuilder.hashKeys().arrayListValues().build(); + JsonArray starBy = starredElement.getAsJsonArray(); + if (starBy.size() > 0) { + for (int i = 0; i < starBy.size(); i++) { + String[] indexableFields = starBy.get(i).getAsString().split(":"); + Optional<Account.Id> id = Account.Id.tryParse(indexableFields[0]); + if (id.isPresent()) { + stars.put(id.get(), indexableFields[1]); + } + } + } + cd.setStars(stars); + } + + // Mergeable. + JsonElement mergeableElement = source.get(ChangeField.MERGEABLE.getName()); + if (mergeableElement != null) { + String mergeable = mergeableElement.getAsString(); + if ("1".equals(mergeable)) { + cd.setMergeable(true); + } else if ("0".equals(mergeable)) { + cd.setMergeable(false); + } + } + + // Reviewed-by. + if (source.get(ChangeField.REVIEWEDBY.getName()) != null) { + JsonArray reviewedBy = source.get(ChangeField.REVIEWEDBY.getName()).getAsJsonArray(); + if (reviewedBy.size() > 0) { + Set<Account.Id> accounts = Sets.newHashSetWithExpectedSize(reviewedBy.size()); + for (int i = 0; i < reviewedBy.size(); i++) { + int aId = reviewedBy.get(i).getAsInt(); + if (reviewedBy.size() == 1 && aId == ChangeField.NOT_REVIEWED) { + break; + } + accounts.add(new Account.Id(aId)); + } + cd.setReviewedBy(accounts); + } + } else if (fields.contains(ChangeField.REVIEWEDBY.getName())) { + cd.setReviewedBy(Collections.emptySet()); + } + + // Hashtag. + if (source.get(ChangeField.HASHTAG.getName()) != null) { + JsonArray hashtagArray = source.get(ChangeField.HASHTAG.getName()).getAsJsonArray(); + if (hashtagArray.size() > 0) { + Set<String> hashtags = Sets.newHashSetWithExpectedSize(hashtagArray.size()); + for (int i = 0; i < hashtagArray.size(); i++) { + hashtags.add(hashtagArray.get(i).getAsString()); + } + cd.setHashtags(hashtags); + } + } else if (fields.contains(ChangeField.HASHTAG.getName())) { + cd.setHashtags(Collections.emptySet()); + } + + // Star. + if (source.get(ChangeField.STAR.getName()) != null) { + JsonArray starArray = source.get(ChangeField.STAR.getName()).getAsJsonArray(); + if (starArray.size() > 0) { + ListMultimap<Account.Id, String> stars = + MultimapBuilder.hashKeys().arrayListValues().build(); + for (int i = 0; i < starArray.size(); i++) { + StarredChangesUtil.StarField starField = + StarredChangesUtil.StarField.parse(starArray.get(i).getAsString()); + stars.put(starField.accountId(), starField.label()); + } + cd.setStars(stars); + } + } else if (fields.contains(ChangeField.STAR.getName())) { + cd.setStars(ImmutableListMultimap.of()); + } + + // Reviewer. + if (source.get(ChangeField.REVIEWER.getName()) != null) { + cd.setReviewers( + ChangeField.parseReviewerFieldValues( + cd.getId(), + FluentIterable.from(source.get(ChangeField.REVIEWER.getName()).getAsJsonArray()) + .transform(JsonElement::getAsString))); + } else if (fields.contains(ChangeField.REVIEWER.getName())) { + cd.setReviewers(ReviewerSet.empty()); + } + + // Reviewer-by-email. + if (source.get(ChangeField.REVIEWER_BY_EMAIL.getName()) != null) { + cd.setReviewersByEmail( + ChangeField.parseReviewerByEmailFieldValues( + cd.getId(), + FluentIterable.from( + source.get(ChangeField.REVIEWER_BY_EMAIL.getName()).getAsJsonArray()) + .transform(JsonElement::getAsString))); + } else if (fields.contains(ChangeField.REVIEWER_BY_EMAIL.getName())) { + cd.setReviewersByEmail(ReviewerByEmailSet.empty()); + } + + // Pending-reviewer. + if (source.get(ChangeField.PENDING_REVIEWER.getName()) != null) { + cd.setPendingReviewers( + ChangeField.parseReviewerFieldValues( + cd.getId(), + FluentIterable.from( + source.get(ChangeField.PENDING_REVIEWER.getName()).getAsJsonArray()) + .transform(JsonElement::getAsString))); + } else if (fields.contains(ChangeField.PENDING_REVIEWER.getName())) { + cd.setPendingReviewers(ReviewerSet.empty()); + } + + // Pending-reviewer-by-email. + if (source.get(ChangeField.PENDING_REVIEWER_BY_EMAIL.getName()) != null) { + cd.setPendingReviewersByEmail( + ChangeField.parseReviewerByEmailFieldValues( + cd.getId(), + FluentIterable.from( + source.get(ChangeField.PENDING_REVIEWER_BY_EMAIL.getName()).getAsJsonArray()) + .transform(JsonElement::getAsString))); + } else if (fields.contains(ChangeField.PENDING_REVIEWER_BY_EMAIL.getName())) { + cd.setPendingReviewersByEmail(ReviewerByEmailSet.empty()); + } + + // Stored-submit-record-strict. + decodeSubmitRecords( + source, + ChangeField.STORED_SUBMIT_RECORD_STRICT.getName(), + ChangeField.SUBMIT_RULE_OPTIONS_STRICT, + cd); + + // Stored-submit-record-leniant. + decodeSubmitRecords( + source, + ChangeField.STORED_SUBMIT_RECORD_LENIENT.getName(), + ChangeField.SUBMIT_RULE_OPTIONS_LENIENT, + cd); + + // Ref-state. + if (fields.contains(ChangeField.REF_STATE.getName())) { + cd.setRefStates(getByteArray(source, ChangeField.REF_STATE.getName())); + } + + // Ref-state-pattern. + if (fields.contains(ChangeField.REF_STATE_PATTERN.getName())) { + cd.setRefStatePatterns(getByteArray(source, ChangeField.REF_STATE_PATTERN.getName())); + } + + // Unresolved-comment-count. + decodeUnresolvedCommentCount(source, ChangeField.UNRESOLVED_COMMENT_COUNT.getName(), cd); + + return cd; + } + + private Iterable<byte[]> getByteArray(JsonObject source, String name) { + JsonElement element = source.get(name); + return element != null + ? Iterables.transform(element.getAsJsonArray(), e -> Base64.decodeBase64(e.getAsString())) + : Collections.emptyList(); + } + + private void decodeSubmitRecords( + JsonObject doc, String fieldName, SubmitRuleOptions opts, ChangeData out) { + JsonArray records = doc.getAsJsonArray(fieldName); + if (records == null) { + return; + } + ChangeField.parseSubmitRecords( + FluentIterable.from(records) + .transform(i -> new String(decodeBase64(i.toString()), UTF_8)) + .toList(), + opts, + out); + } + + private void decodeUnresolvedCommentCount(JsonObject doc, String fieldName, ChangeData out) { + JsonElement count = doc.get(fieldName); + if (count == null) { + return; + } + out.setUnresolvedCommentCount(count.getAsInt()); + } +}
diff --git a/gerrit-elasticsearch/src/main/java/com/google/gerrit/elasticsearch/ElasticConfiguration.java b/java/com/google/gerrit/elasticsearch/ElasticConfiguration.java similarity index 92% rename from gerrit-elasticsearch/src/main/java/com/google/gerrit/elasticsearch/ElasticConfiguration.java rename to java/com/google/gerrit/elasticsearch/ElasticConfiguration.java index 4ec5feb..cbe9bc7 100644 --- a/gerrit-elasticsearch/src/main/java/com/google/gerrit/elasticsearch/ElasticConfiguration.java +++ b/java/com/google/gerrit/elasticsearch/ElasticConfiguration.java
@@ -17,6 +17,7 @@ import static com.google.common.base.MoreObjects.firstNonNull; import com.google.common.base.Strings; +import com.google.common.flogger.FluentLogger; import com.google.gerrit.server.config.GerritServerConfig; import com.google.inject.Inject; import com.google.inject.ProvisionException; @@ -27,12 +28,10 @@ import java.util.List; import org.apache.http.HttpHost; import org.eclipse.jgit.lib.Config; -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; @Singleton class ElasticConfiguration { - private static final Logger log = LoggerFactory.getLogger(ElasticConfiguration.class); + private static final FluentLogger logger = FluentLogger.forEnclosingClass(); static final String SECTION_ELASTICSEARCH = "elasticsearch"; static final String KEY_PASSWORD = "password"; @@ -79,7 +78,7 @@ uri.getHost(), port == -1 ? Integer.valueOf(DEFAULT_PORT) : port, uri.getScheme()); this.hosts.add(httpHost); } catch (URISyntaxException | IllegalArgumentException e) { - log.error("Invalid server URI {}: {}", server, e.getMessage()); + logger.atSevere().log("Invalid server URI %s: %s", server, e.getMessage()); } } @@ -87,7 +86,7 @@ throw new ProvisionException("No valid Elasticsearch servers configured"); } - log.info("Elasticsearch servers: {}", hosts); + logger.atInfo().log("Elasticsearch servers: %s", hosts); } Config getConfig() {
diff --git a/gerrit-elasticsearch/src/main/java/com/google/gerrit/elasticsearch/ElasticException.java b/java/com/google/gerrit/elasticsearch/ElasticException.java similarity index 100% rename from gerrit-elasticsearch/src/main/java/com/google/gerrit/elasticsearch/ElasticException.java rename to java/com/google/gerrit/elasticsearch/ElasticException.java
diff --git a/java/com/google/gerrit/elasticsearch/ElasticGroupIndex.java b/java/com/google/gerrit/elasticsearch/ElasticGroupIndex.java new file mode 100644 index 0000000..e74f208 --- /dev/null +++ b/java/com/google/gerrit/elasticsearch/ElasticGroupIndex.java
@@ -0,0 +1,126 @@ +// Copyright (C) 2017 The Android Open Source Project +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +package com.google.gerrit.elasticsearch; + +import com.google.gerrit.elasticsearch.ElasticMapping.MappingProperties; +import com.google.gerrit.elasticsearch.bulk.BulkRequest; +import com.google.gerrit.elasticsearch.bulk.IndexRequest; +import com.google.gerrit.elasticsearch.bulk.UpdateRequest; +import com.google.gerrit.index.QueryOptions; +import com.google.gerrit.index.Schema; +import com.google.gerrit.index.query.DataSource; +import com.google.gerrit.index.query.Predicate; +import com.google.gerrit.index.query.QueryParseException; +import com.google.gerrit.reviewdb.client.AccountGroup; +import com.google.gerrit.server.account.GroupCache; +import com.google.gerrit.server.config.SitePaths; +import com.google.gerrit.server.group.InternalGroup; +import com.google.gerrit.server.index.IndexUtils; +import com.google.gerrit.server.index.group.GroupField; +import com.google.gerrit.server.index.group.GroupIndex; +import com.google.gson.JsonArray; +import com.google.gson.JsonElement; +import com.google.gson.JsonObject; +import com.google.inject.Inject; +import com.google.inject.Provider; +import com.google.inject.assistedinject.Assisted; +import java.io.IOException; +import java.util.Set; +import org.apache.http.HttpStatus; +import org.elasticsearch.client.Response; + +public class ElasticGroupIndex extends AbstractElasticIndex<AccountGroup.UUID, InternalGroup> + implements GroupIndex { + static class GroupMapping { + final MappingProperties groups; + + GroupMapping(Schema<InternalGroup> schema, ElasticQueryAdapter adapter) { + this.groups = ElasticMapping.createMapping(schema, adapter); + } + } + + private static final String GROUPS = "groups"; + + private final GroupMapping mapping; + private final Provider<GroupCache> groupCache; + private final Schema<InternalGroup> schema; + + @Inject + ElasticGroupIndex( + ElasticConfiguration cfg, + SitePaths sitePaths, + Provider<GroupCache> groupCache, + ElasticRestClientProvider client, + @Assisted Schema<InternalGroup> schema) { + super(cfg, sitePaths, schema, client, GROUPS); + this.groupCache = groupCache; + this.mapping = new GroupMapping(schema, client.adapter()); + this.schema = schema; + } + + @Override + public void replace(InternalGroup group) throws IOException { + BulkRequest bulk = + new IndexRequest(getId(group), indexName, type, client.adapter()) + .add(new UpdateRequest<>(schema, group)); + + String uri = getURI(type, BULK); + Response response = postRequest(uri, bulk, getRefreshParam()); + int statusCode = response.getStatusLine().getStatusCode(); + if (statusCode != HttpStatus.SC_OK) { + throw new IOException( + String.format( + "Failed to replace group %s in index %s: %s", + group.getGroupUUID().get(), indexName, statusCode)); + } + } + + @Override + public DataSource<InternalGroup> getSource(Predicate<InternalGroup> p, QueryOptions opts) + throws QueryParseException { + JsonArray sortArray = getSortArray(GroupField.UUID.getName()); + return new ElasticQuerySource(p, opts.filterFields(IndexUtils::groupFields), type, sortArray); + } + + @Override + protected String getDeleteActions(AccountGroup.UUID g) { + return delete(type, g); + } + + @Override + protected String getMappings() { + return getMappingsForSingleType(GROUPS, mapping.groups); + } + + @Override + protected String getId(InternalGroup group) { + return group.getGroupUUID().get(); + } + + @Override + protected InternalGroup fromDocument(JsonObject json, Set<String> fields) { + JsonElement source = json.get("_source"); + if (source == null) { + source = json.getAsJsonObject().get("fields"); + } + + AccountGroup.UUID uuid = + new AccountGroup.UUID( + source.getAsJsonObject().get(GroupField.UUID.getName()).getAsString()); + // Use the GroupCache rather than depending on any stored fields in the + // document (of which there shouldn't be any). + return groupCache.get().get(uuid).orElse(null); + } +}
diff --git a/gerrit-elasticsearch/src/main/java/com/google/gerrit/elasticsearch/ElasticIndexModule.java b/java/com/google/gerrit/elasticsearch/ElasticIndexModule.java similarity index 78% rename from gerrit-elasticsearch/src/main/java/com/google/gerrit/elasticsearch/ElasticIndexModule.java rename to java/com/google/gerrit/elasticsearch/ElasticIndexModule.java index 6bc51ce..15d6126 100644 --- a/gerrit-elasticsearch/src/main/java/com/google/gerrit/elasticsearch/ElasticIndexModule.java +++ b/java/com/google/gerrit/elasticsearch/ElasticIndexModule.java
@@ -14,6 +14,7 @@ package com.google.gerrit.elasticsearch; +import com.google.gerrit.index.project.ProjectIndex; import com.google.gerrit.server.index.AbstractIndexModule; import com.google.gerrit.server.index.VersionManager; import com.google.gerrit.server.index.account.AccountIndex; @@ -22,18 +23,17 @@ import java.util.Map; public class ElasticIndexModule extends AbstractIndexModule { - public static ElasticIndexModule singleVersionWithExplicitVersions( - Map<String, Integer> versions, int threads) { - return new ElasticIndexModule(versions, threads); + Map<String, Integer> versions, int threads, boolean slave) { + return new ElasticIndexModule(versions, threads, slave); } - public static ElasticIndexModule latestVersion() { - return new ElasticIndexModule(null, 0); + public static ElasticIndexModule latestVersion(boolean slave) { + return new ElasticIndexModule(null, 0, slave); } - private ElasticIndexModule(Map<String, Integer> singleVersions, int threads) { - super(singleVersions, threads); + private ElasticIndexModule(Map<String, Integer> singleVersions, int threads, boolean slave) { + super(singleVersions, threads, slave); } @Override @@ -58,6 +58,11 @@ } @Override + protected Class<? extends ProjectIndex> getProjectIndex() { + return ElasticProjectIndex.class; + } + + @Override protected Class<? extends VersionManager> getVersionManager() { return ElasticIndexVersionManager.class; }
diff --git a/gerrit-elasticsearch/src/main/java/com/google/gerrit/elasticsearch/ElasticIndexVersionDiscovery.java b/java/com/google/gerrit/elasticsearch/ElasticIndexVersionDiscovery.java similarity index 91% rename from gerrit-elasticsearch/src/main/java/com/google/gerrit/elasticsearch/ElasticIndexVersionDiscovery.java rename to java/com/google/gerrit/elasticsearch/ElasticIndexVersionDiscovery.java index 8b343c2..100022a 100644 --- a/gerrit-elasticsearch/src/main/java/com/google/gerrit/elasticsearch/ElasticIndexVersionDiscovery.java +++ b/java/com/google/gerrit/elasticsearch/ElasticIndexVersionDiscovery.java
@@ -16,6 +16,7 @@ import static java.util.stream.Collectors.toList; +import com.google.common.flogger.FluentLogger; import com.google.gson.JsonParser; import com.google.inject.Inject; import com.google.inject.Singleton; @@ -25,12 +26,10 @@ import org.apache.http.StatusLine; import org.elasticsearch.client.Request; import org.elasticsearch.client.Response; -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; @Singleton class ElasticIndexVersionDiscovery { - private static final Logger log = LoggerFactory.getLogger(ElasticIndexVersionDiscovery.class); + private static final FluentLogger logger = FluentLogger.forEnclosingClass(); private final ElasticRestClientProvider client; @@ -50,7 +49,7 @@ String.format( "Failed to discover index versions for %s: %d: %s", name, statusLine.getStatusCode(), statusLine.getReasonPhrase()); - log.error(message); + logger.atSevere().log(message); throw new IOException(message); }
diff --git a/gerrit-elasticsearch/src/main/java/com/google/gerrit/elasticsearch/ElasticIndexVersionManager.java b/java/com/google/gerrit/elasticsearch/ElasticIndexVersionManager.java similarity index 79% rename from gerrit-elasticsearch/src/main/java/com/google/gerrit/elasticsearch/ElasticIndexVersionManager.java rename to java/com/google/gerrit/elasticsearch/ElasticIndexVersionManager.java index cff1911..8011efa 100644 --- a/gerrit-elasticsearch/src/main/java/com/google/gerrit/elasticsearch/ElasticIndexVersionManager.java +++ b/java/com/google/gerrit/elasticsearch/ElasticIndexVersionManager.java
@@ -14,11 +14,14 @@ package com.google.gerrit.elasticsearch; +import com.google.common.base.Strings; +import com.google.common.flogger.FluentLogger; import com.google.common.primitives.Ints; import com.google.gerrit.extensions.registration.DynamicSet; import com.google.gerrit.index.Index; import com.google.gerrit.index.IndexDefinition; import com.google.gerrit.index.Schema; +import com.google.gerrit.server.config.GerritServerConfig; import com.google.gerrit.server.config.SitePaths; import com.google.gerrit.server.index.GerritIndexStatus; import com.google.gerrit.server.index.OnlineUpgradeListener; @@ -29,26 +32,25 @@ import java.util.Collection; import java.util.List; import java.util.TreeMap; -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; +import org.eclipse.jgit.lib.Config; @Singleton public class ElasticIndexVersionManager extends VersionManager { - private static final Logger log = LoggerFactory.getLogger(ElasticIndexVersionManager.class); + private static final FluentLogger logger = FluentLogger.forEnclosingClass(); private final String prefix; private final ElasticIndexVersionDiscovery versionDiscovery; @Inject ElasticIndexVersionManager( - ElasticConfiguration cfg, + @GerritServerConfig Config cfg, SitePaths sitePaths, DynamicSet<OnlineUpgradeListener> listeners, Collection<IndexDefinition<?, ?, ?>> defs, ElasticIndexVersionDiscovery versionDiscovery) { - super(sitePaths, listeners, defs, VersionManager.getOnlineUpgrade(cfg.getConfig())); + super(sitePaths, listeners, defs, VersionManager.getOnlineUpgrade(cfg)); this.versionDiscovery = versionDiscovery; - prefix = cfg.prefix; + prefix = Strings.nullToEmpty(cfg.getString("elasticsearch", null, "prefix")); } @Override @@ -57,17 +59,17 @@ TreeMap<Integer, Version<V>> versions = new TreeMap<>(); try { List<String> discovered = versionDiscovery.discover(prefix, def.getName()); - log.debug("Discovered versions for {}: {}", def.getName(), discovered); + logger.atFine().log("Discovered versions for %s: %s", def.getName(), discovered); for (String version : discovered) { Integer v = Ints.tryParse(version); if (v == null || version.length() != 4) { - log.warn("Unrecognized version in index {}: {}", def.getName(), version); + logger.atWarning().log("Unrecognized version in index %s: %s", def.getName(), version); continue; } versions.put(v, new Version<>(null, v, true, cfg.getReady(def.getName(), v))); } } catch (IOException e) { - log.error("Error scanning index: " + def.getName(), e); + logger.atSevere().withCause(e).log("Error scanning index: %s", def.getName()); } for (Schema<V> schema : def.getSchemas().values()) {
diff --git a/gerrit-elasticsearch/src/main/java/com/google/gerrit/elasticsearch/ElasticMapping.java b/java/com/google/gerrit/elasticsearch/ElasticMapping.java similarity index 100% rename from gerrit-elasticsearch/src/main/java/com/google/gerrit/elasticsearch/ElasticMapping.java rename to java/com/google/gerrit/elasticsearch/ElasticMapping.java
diff --git a/java/com/google/gerrit/elasticsearch/ElasticProjectIndex.java b/java/com/google/gerrit/elasticsearch/ElasticProjectIndex.java new file mode 100644 index 0000000..8510559 --- /dev/null +++ b/java/com/google/gerrit/elasticsearch/ElasticProjectIndex.java
@@ -0,0 +1,124 @@ +// Copyright (C) 2017 The Android Open Source Project +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +package com.google.gerrit.elasticsearch; + +import com.google.gerrit.elasticsearch.ElasticMapping.MappingProperties; +import com.google.gerrit.elasticsearch.bulk.BulkRequest; +import com.google.gerrit.elasticsearch.bulk.IndexRequest; +import com.google.gerrit.elasticsearch.bulk.UpdateRequest; +import com.google.gerrit.index.QueryOptions; +import com.google.gerrit.index.Schema; +import com.google.gerrit.index.project.ProjectData; +import com.google.gerrit.index.project.ProjectField; +import com.google.gerrit.index.project.ProjectIndex; +import com.google.gerrit.index.query.DataSource; +import com.google.gerrit.index.query.Predicate; +import com.google.gerrit.index.query.QueryParseException; +import com.google.gerrit.reviewdb.client.Project; +import com.google.gerrit.server.config.SitePaths; +import com.google.gerrit.server.index.IndexUtils; +import com.google.gerrit.server.project.ProjectCache; +import com.google.gson.JsonArray; +import com.google.gson.JsonElement; +import com.google.gson.JsonObject; +import com.google.inject.Inject; +import com.google.inject.Provider; +import com.google.inject.assistedinject.Assisted; +import java.io.IOException; +import java.util.Set; +import org.apache.http.HttpStatus; +import org.elasticsearch.client.Response; + +public class ElasticProjectIndex extends AbstractElasticIndex<Project.NameKey, ProjectData> + implements ProjectIndex { + static class ProjectMapping { + MappingProperties projects; + + ProjectMapping(Schema<ProjectData> schema, ElasticQueryAdapter adapter) { + this.projects = ElasticMapping.createMapping(schema, adapter); + } + } + + static final String PROJECTS = "projects"; + + private final ProjectMapping mapping; + private final Provider<ProjectCache> projectCache; + private final Schema<ProjectData> schema; + + @Inject + ElasticProjectIndex( + ElasticConfiguration cfg, + SitePaths sitePaths, + Provider<ProjectCache> projectCache, + ElasticRestClientProvider client, + @Assisted Schema<ProjectData> schema) { + super(cfg, sitePaths, schema, client, PROJECTS); + this.projectCache = projectCache; + this.schema = schema; + this.mapping = new ProjectMapping(schema, client.adapter()); + } + + @Override + public void replace(ProjectData projectState) throws IOException { + BulkRequest bulk = + new IndexRequest(projectState.getProject().getName(), indexName, type, client.adapter()) + .add(new UpdateRequest<>(schema, projectState)); + + String uri = getURI(type, BULK); + Response response = postRequest(uri, bulk, getRefreshParam()); + int statusCode = response.getStatusLine().getStatusCode(); + if (statusCode != HttpStatus.SC_OK) { + throw new IOException( + String.format( + "Failed to replace project %s in index %s: %s", + projectState.getProject().getName(), indexName, statusCode)); + } + } + + @Override + public DataSource<ProjectData> getSource(Predicate<ProjectData> p, QueryOptions opts) + throws QueryParseException { + JsonArray sortArray = getSortArray(ProjectField.NAME.getName()); + return new ElasticQuerySource(p, opts.filterFields(IndexUtils::projectFields), type, sortArray); + } + + @Override + protected String getDeleteActions(Project.NameKey nameKey) { + return delete(type, nameKey); + } + + @Override + protected String getMappings() { + return getMappingsForSingleType(PROJECTS, mapping.projects); + } + + @Override + protected String getId(ProjectData projectState) { + return projectState.getProject().getName(); + } + + @Override + protected ProjectData fromDocument(JsonObject json, Set<String> fields) { + JsonElement source = json.get("_source"); + if (source == null) { + source = json.getAsJsonObject().get("fields"); + } + + Project.NameKey nameKey = + new Project.NameKey( + source.getAsJsonObject().get(ProjectField.NAME.getName()).getAsString()); + return projectCache.get().get(nameKey).toProjectData(); + } +}
diff --git a/gerrit-elasticsearch/src/main/java/com/google/gerrit/elasticsearch/ElasticQueryAdapter.java b/java/com/google/gerrit/elasticsearch/ElasticQueryAdapter.java similarity index 95% rename from gerrit-elasticsearch/src/main/java/com/google/gerrit/elasticsearch/ElasticQueryAdapter.java rename to java/com/google/gerrit/elasticsearch/ElasticQueryAdapter.java index dfb1cbf..72c52b0 100644 --- a/gerrit-elasticsearch/src/main/java/com/google/gerrit/elasticsearch/ElasticQueryAdapter.java +++ b/java/com/google/gerrit/elasticsearch/ElasticQueryAdapter.java
@@ -34,6 +34,7 @@ private final String exactFieldType; private final String stringFieldType; private final String indexProperty; + private final String rawFieldsKey; private final String versionDiscoveryUrl; private final String includeTypeNameParam; @@ -49,6 +50,7 @@ this.exactFieldType = "keyword"; this.stringFieldType = "text"; this.indexProperty = "true"; + this.rawFieldsKey = "_source"; this.includeTypeNameParam = version.isAtLeastMinorVersion(V6_7) ? "?" + INCLUDE_TYPE : ""; } @@ -78,6 +80,10 @@ return indexProperty; } + String rawFieldsKey() { + return rawFieldsKey; + } + boolean deleteToReplace() { return useV5Type; }
diff --git a/gerrit-elasticsearch/src/main/java/com/google/gerrit/elasticsearch/ElasticQueryBuilder.java b/java/com/google/gerrit/elasticsearch/ElasticQueryBuilder.java similarity index 100% rename from gerrit-elasticsearch/src/main/java/com/google/gerrit/elasticsearch/ElasticQueryBuilder.java rename to java/com/google/gerrit/elasticsearch/ElasticQueryBuilder.java
diff --git a/gerrit-elasticsearch/src/main/java/com/google/gerrit/elasticsearch/ElasticRestClientProvider.java b/java/com/google/gerrit/elasticsearch/ElasticRestClientProvider.java similarity index 93% rename from gerrit-elasticsearch/src/main/java/com/google/gerrit/elasticsearch/ElasticRestClientProvider.java rename to java/com/google/gerrit/elasticsearch/ElasticRestClientProvider.java index 1147571..a67de44 100644 --- a/gerrit-elasticsearch/src/main/java/com/google/gerrit/elasticsearch/ElasticRestClientProvider.java +++ b/java/com/google/gerrit/elasticsearch/ElasticRestClientProvider.java
@@ -14,6 +14,7 @@ package com.google.gerrit.elasticsearch; +import com.google.common.flogger.FluentLogger; import com.google.gerrit.extensions.events.LifecycleListener; import com.google.gerrit.lifecycle.LifecycleModule; import com.google.gson.JsonParser; @@ -32,12 +33,10 @@ import org.elasticsearch.client.Response; import org.elasticsearch.client.RestClient; import org.elasticsearch.client.RestClientBuilder; -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; @Singleton class ElasticRestClientProvider implements Provider<RestClient>, LifecycleListener { - private static final Logger log = LoggerFactory.getLogger(ElasticRestClientProvider.class); + private static final FluentLogger logger = FluentLogger.forEnclosingClass(); private final ElasticConfiguration cfg; @@ -65,7 +64,7 @@ if (client == null) { client = build(); ElasticVersion version = getVersion(); - log.info("Elasticsearch integration version {}", version); + logger.atInfo().log("Elasticsearch integration version %s", version); adapter = new ElasticQueryAdapter(version); } } @@ -120,7 +119,7 @@ .getAsJsonObject() .get("number") .getAsString(); - log.info("Connected to Elasticsearch version {}", version); + logger.atInfo().log("Connected to Elasticsearch version %s", version); return ElasticVersion.forVersion(version); } catch (IOException e) { throw new FailedToGetVersion(e);
diff --git a/gerrit-elasticsearch/src/main/java/com/google/gerrit/elasticsearch/ElasticSetting.java b/java/com/google/gerrit/elasticsearch/ElasticSetting.java similarity index 100% rename from gerrit-elasticsearch/src/main/java/com/google/gerrit/elasticsearch/ElasticSetting.java rename to java/com/google/gerrit/elasticsearch/ElasticSetting.java
diff --git a/gerrit-elasticsearch/src/main/java/com/google/gerrit/elasticsearch/ElasticVersion.java b/java/com/google/gerrit/elasticsearch/ElasticVersion.java similarity index 100% rename from gerrit-elasticsearch/src/main/java/com/google/gerrit/elasticsearch/ElasticVersion.java rename to java/com/google/gerrit/elasticsearch/ElasticVersion.java
diff --git a/gerrit-elasticsearch/src/main/java/com/google/gerrit/elasticsearch/builders/BoolQueryBuilder.java b/java/com/google/gerrit/elasticsearch/builders/BoolQueryBuilder.java similarity index 100% rename from gerrit-elasticsearch/src/main/java/com/google/gerrit/elasticsearch/builders/BoolQueryBuilder.java rename to java/com/google/gerrit/elasticsearch/builders/BoolQueryBuilder.java
diff --git a/gerrit-elasticsearch/src/main/java/com/google/gerrit/elasticsearch/builders/ExistsQueryBuilder.java b/java/com/google/gerrit/elasticsearch/builders/ExistsQueryBuilder.java similarity index 100% rename from gerrit-elasticsearch/src/main/java/com/google/gerrit/elasticsearch/builders/ExistsQueryBuilder.java rename to java/com/google/gerrit/elasticsearch/builders/ExistsQueryBuilder.java
diff --git a/gerrit-elasticsearch/src/main/java/com/google/gerrit/elasticsearch/builders/MatchAllQueryBuilder.java b/java/com/google/gerrit/elasticsearch/builders/MatchAllQueryBuilder.java similarity index 100% rename from gerrit-elasticsearch/src/main/java/com/google/gerrit/elasticsearch/builders/MatchAllQueryBuilder.java rename to java/com/google/gerrit/elasticsearch/builders/MatchAllQueryBuilder.java
diff --git a/gerrit-elasticsearch/src/main/java/com/google/gerrit/elasticsearch/builders/MatchQueryBuilder.java b/java/com/google/gerrit/elasticsearch/builders/MatchQueryBuilder.java similarity index 100% rename from gerrit-elasticsearch/src/main/java/com/google/gerrit/elasticsearch/builders/MatchQueryBuilder.java rename to java/com/google/gerrit/elasticsearch/builders/MatchQueryBuilder.java
diff --git a/gerrit-elasticsearch/src/main/java/com/google/gerrit/elasticsearch/builders/QueryBuilder.java b/java/com/google/gerrit/elasticsearch/builders/QueryBuilder.java similarity index 100% rename from gerrit-elasticsearch/src/main/java/com/google/gerrit/elasticsearch/builders/QueryBuilder.java rename to java/com/google/gerrit/elasticsearch/builders/QueryBuilder.java
diff --git a/gerrit-elasticsearch/src/main/java/com/google/gerrit/elasticsearch/builders/QueryBuilders.java b/java/com/google/gerrit/elasticsearch/builders/QueryBuilders.java similarity index 100% rename from gerrit-elasticsearch/src/main/java/com/google/gerrit/elasticsearch/builders/QueryBuilders.java rename to java/com/google/gerrit/elasticsearch/builders/QueryBuilders.java
diff --git a/gerrit-elasticsearch/src/main/java/com/google/gerrit/elasticsearch/builders/QuerySourceBuilder.java b/java/com/google/gerrit/elasticsearch/builders/QuerySourceBuilder.java similarity index 100% rename from gerrit-elasticsearch/src/main/java/com/google/gerrit/elasticsearch/builders/QuerySourceBuilder.java rename to java/com/google/gerrit/elasticsearch/builders/QuerySourceBuilder.java
diff --git a/gerrit-elasticsearch/src/main/java/com/google/gerrit/elasticsearch/builders/RangeQueryBuilder.java b/java/com/google/gerrit/elasticsearch/builders/RangeQueryBuilder.java similarity index 100% rename from gerrit-elasticsearch/src/main/java/com/google/gerrit/elasticsearch/builders/RangeQueryBuilder.java rename to java/com/google/gerrit/elasticsearch/builders/RangeQueryBuilder.java
diff --git a/gerrit-elasticsearch/src/main/java/com/google/gerrit/elasticsearch/builders/RegexpQueryBuilder.java b/java/com/google/gerrit/elasticsearch/builders/RegexpQueryBuilder.java similarity index 100% rename from gerrit-elasticsearch/src/main/java/com/google/gerrit/elasticsearch/builders/RegexpQueryBuilder.java rename to java/com/google/gerrit/elasticsearch/builders/RegexpQueryBuilder.java
diff --git a/gerrit-elasticsearch/src/main/java/com/google/gerrit/elasticsearch/builders/SearchSourceBuilder.java b/java/com/google/gerrit/elasticsearch/builders/SearchSourceBuilder.java similarity index 100% rename from gerrit-elasticsearch/src/main/java/com/google/gerrit/elasticsearch/builders/SearchSourceBuilder.java rename to java/com/google/gerrit/elasticsearch/builders/SearchSourceBuilder.java
diff --git a/gerrit-elasticsearch/src/main/java/com/google/gerrit/elasticsearch/builders/TermQueryBuilder.java b/java/com/google/gerrit/elasticsearch/builders/TermQueryBuilder.java similarity index 100% rename from gerrit-elasticsearch/src/main/java/com/google/gerrit/elasticsearch/builders/TermQueryBuilder.java rename to java/com/google/gerrit/elasticsearch/builders/TermQueryBuilder.java
diff --git a/gerrit-elasticsearch/src/main/java/com/google/gerrit/elasticsearch/builders/XContentBuilder.java b/java/com/google/gerrit/elasticsearch/builders/XContentBuilder.java similarity index 100% rename from gerrit-elasticsearch/src/main/java/com/google/gerrit/elasticsearch/builders/XContentBuilder.java rename to java/com/google/gerrit/elasticsearch/builders/XContentBuilder.java
diff --git a/gerrit-elasticsearch/src/main/java/com/google/gerrit/elasticsearch/bulk/ActionRequest.java b/java/com/google/gerrit/elasticsearch/bulk/ActionRequest.java similarity index 100% rename from gerrit-elasticsearch/src/main/java/com/google/gerrit/elasticsearch/bulk/ActionRequest.java rename to java/com/google/gerrit/elasticsearch/bulk/ActionRequest.java
diff --git a/gerrit-elasticsearch/src/main/java/com/google/gerrit/elasticsearch/bulk/BulkRequest.java b/java/com/google/gerrit/elasticsearch/bulk/BulkRequest.java similarity index 100% rename from gerrit-elasticsearch/src/main/java/com/google/gerrit/elasticsearch/bulk/BulkRequest.java rename to java/com/google/gerrit/elasticsearch/bulk/BulkRequest.java
diff --git a/gerrit-elasticsearch/src/main/java/com/google/gerrit/elasticsearch/bulk/DeleteRequest.java b/java/com/google/gerrit/elasticsearch/bulk/DeleteRequest.java similarity index 100% rename from gerrit-elasticsearch/src/main/java/com/google/gerrit/elasticsearch/bulk/DeleteRequest.java rename to java/com/google/gerrit/elasticsearch/bulk/DeleteRequest.java
diff --git a/gerrit-elasticsearch/src/main/java/com/google/gerrit/elasticsearch/bulk/IndexRequest.java b/java/com/google/gerrit/elasticsearch/bulk/IndexRequest.java similarity index 100% rename from gerrit-elasticsearch/src/main/java/com/google/gerrit/elasticsearch/bulk/IndexRequest.java rename to java/com/google/gerrit/elasticsearch/bulk/IndexRequest.java
diff --git a/gerrit-elasticsearch/src/main/java/com/google/gerrit/elasticsearch/bulk/UpdateRequest.java b/java/com/google/gerrit/elasticsearch/bulk/UpdateRequest.java similarity index 100% rename from gerrit-elasticsearch/src/main/java/com/google/gerrit/elasticsearch/bulk/UpdateRequest.java rename to java/com/google/gerrit/elasticsearch/bulk/UpdateRequest.java
diff --git a/gerrit-pgm/src/main/java/com/google/gerrit/pgm/init/index/elasticsearch/ElasticIndexModuleOnInit.java b/java/com/google/gerrit/pgm/init/index/elasticsearch/ElasticIndexModuleOnInit.java similarity index 100% rename from gerrit-pgm/src/main/java/com/google/gerrit/pgm/init/index/elasticsearch/ElasticIndexModuleOnInit.java rename to java/com/google/gerrit/pgm/init/index/elasticsearch/ElasticIndexModuleOnInit.java
diff --git a/gerrit-acceptance-tests/src/test/java/com/google/gerrit/acceptance/pgm/ElasticReindexIT.java b/javatests/com/google/gerrit/acceptance/pgm/ElasticReindexIT.java similarity index 96% rename from gerrit-acceptance-tests/src/test/java/com/google/gerrit/acceptance/pgm/ElasticReindexIT.java rename to javatests/com/google/gerrit/acceptance/pgm/ElasticReindexIT.java index 0da3198..c18c092 100644 --- a/gerrit-acceptance-tests/src/test/java/com/google/gerrit/acceptance/pgm/ElasticReindexIT.java +++ b/javatests/com/google/gerrit/acceptance/pgm/ElasticReindexIT.java
@@ -18,7 +18,7 @@ import static com.google.gerrit.elasticsearch.ElasticTestUtils.getConfig; import com.google.gerrit.elasticsearch.ElasticVersion; -import com.google.gerrit.testutil.ConfigSuite; +import com.google.gerrit.testing.ConfigSuite; import com.google.inject.Injector; import org.eclipse.jgit.lib.Config; import org.junit.Before;
diff --git a/gerrit-acceptance-tests/src/test/java/com/google/gerrit/acceptance/ssh/ElasticIndexIT.java b/javatests/com/google/gerrit/acceptance/ssh/ElasticIndexIT.java similarity index 96% rename from gerrit-acceptance-tests/src/test/java/com/google/gerrit/acceptance/ssh/ElasticIndexIT.java rename to javatests/com/google/gerrit/acceptance/ssh/ElasticIndexIT.java index 839564f..6ccd9e0 100644 --- a/gerrit-acceptance-tests/src/test/java/com/google/gerrit/acceptance/ssh/ElasticIndexIT.java +++ b/javatests/com/google/gerrit/acceptance/ssh/ElasticIndexIT.java
@@ -18,7 +18,7 @@ import static com.google.gerrit.elasticsearch.ElasticTestUtils.getConfig; import com.google.gerrit.elasticsearch.ElasticVersion; -import com.google.gerrit.testutil.ConfigSuite; +import com.google.gerrit.testing.ConfigSuite; import com.google.inject.Injector; import org.eclipse.jgit.lib.Config;
diff --git a/javatests/com/google/gerrit/elasticsearch/BUILD b/javatests/com/google/gerrit/elasticsearch/BUILD new file mode 100644 index 0000000..c586080 --- /dev/null +++ b/javatests/com/google/gerrit/elasticsearch/BUILD
@@ -0,0 +1,98 @@ +load("//tools/bzl:junit.bzl", "junit_tests") + +java_library( + name = "elasticsearch_test_utils", + testonly = True, + srcs = [ + "ElasticContainer.java", + "ElasticTestUtils.java", + ], + visibility = ["//visibility:public"], + deps = [ + "//java/com/google/gerrit/elasticsearch", + "//java/com/google/gerrit/index", + "//java/com/google/gerrit/server", + "//lib:guava", + "//lib:junit", + "//lib/guice", + "//lib/httpcomponents:httpcore", + "//lib/jgit/org.eclipse.jgit:jgit", + "//lib/testcontainers", + "//lib/testcontainers:testcontainers-elasticsearch", + ], +) + +ELASTICSEARCH_DEPS = [ + ":elasticsearch_test_utils", + "//java/com/google/gerrit/elasticsearch", + "//java/com/google/gerrit/testing:gerrit-test-util", + "//lib/guice", + "//lib/jgit/org.eclipse.jgit:jgit", +] + +QUERY_TESTS_DEP = "//javatests/com/google/gerrit/server/query/%s:abstract_query_tests" + +TYPES = [ + "account", + "change", + "group", + "project", +] + +SUFFIX = "sTest.java" + +ELASTICSEARCH_TESTS_V5 = {i: "ElasticV5Query" + i.capitalize() + SUFFIX for i in TYPES} + +ELASTICSEARCH_TESTS_V6 = {i: "ElasticV6Query" + i.capitalize() + SUFFIX for i in TYPES} + +ELASTICSEARCH_TESTS_V7 = {i: "ElasticV7Query" + i.capitalize() + SUFFIX for i in TYPES} + +ELASTICSEARCH_TAGS = [ + "docker", + "elastic", + "exclusive", +] + +[junit_tests( + name = "elasticsearch_query_%ss_test_V5" % name, + size = "large", + srcs = [src], + tags = ELASTICSEARCH_TAGS, + deps = ELASTICSEARCH_DEPS + [QUERY_TESTS_DEP % name], +) for name, src in ELASTICSEARCH_TESTS_V5.items()] + +[junit_tests( + name = "elasticsearch_query_%ss_test_V6" % name, + size = "large", + srcs = [src], + tags = ELASTICSEARCH_TAGS, + deps = ELASTICSEARCH_DEPS + [QUERY_TESTS_DEP % name], +) for name, src in ELASTICSEARCH_TESTS_V6.items()] + +[junit_tests( + name = "elasticsearch_query_%ss_test_V7" % name, + size = "large", + srcs = [src], + tags = ELASTICSEARCH_TAGS, + deps = ELASTICSEARCH_DEPS + [QUERY_TESTS_DEP % name] + [ + "//lib/httpcomponents:httpasyncclient", + "//lib/httpcomponents:httpclient", + ], +) for name, src in ELASTICSEARCH_TESTS_V7.items()] + +junit_tests( + name = "elasticsearch_tests", + size = "small", + srcs = glob( + ["*Test.java"], + exclude = ["Elastic*Query*" + SUFFIX], + ), + tags = ["elastic"], + deps = [ + "//java/com/google/gerrit/elasticsearch", + "//lib:guava", + "//lib/guice", + "//lib/jgit/org.eclipse.jgit:jgit", + "//lib/truth", + ], +)
diff --git a/gerrit-elasticsearch/src/test/java/com/google/gerrit/elasticsearch/ElasticConfigurationTest.java b/javatests/com/google/gerrit/elasticsearch/ElasticConfigurationTest.java similarity index 100% rename from gerrit-elasticsearch/src/test/java/com/google/gerrit/elasticsearch/ElasticConfigurationTest.java rename to javatests/com/google/gerrit/elasticsearch/ElasticConfigurationTest.java
diff --git a/gerrit-elasticsearch/src/test/java/com/google/gerrit/elasticsearch/ElasticContainer.java b/javatests/com/google/gerrit/elasticsearch/ElasticContainer.java similarity index 97% rename from gerrit-elasticsearch/src/test/java/com/google/gerrit/elasticsearch/ElasticContainer.java rename to javatests/com/google/gerrit/elasticsearch/ElasticContainer.java index 9c00796..299777e 100644 --- a/gerrit-elasticsearch/src/test/java/com/google/gerrit/elasticsearch/ElasticContainer.java +++ b/javatests/com/google/gerrit/elasticsearch/ElasticContainer.java
@@ -15,7 +15,7 @@ package com.google.gerrit.elasticsearch; import org.apache.http.HttpHost; -import org.junit.internal.AssumptionViolatedException; +import org.junit.AssumptionViolatedException; import org.testcontainers.elasticsearch.ElasticsearchContainer; /* Helper class for running ES integration tests in docker container */
diff --git a/gerrit-elasticsearch/src/test/java/com/google/gerrit/elasticsearch/ElasticTestUtils.java b/javatests/com/google/gerrit/elasticsearch/ElasticTestUtils.java similarity index 100% rename from gerrit-elasticsearch/src/test/java/com/google/gerrit/elasticsearch/ElasticTestUtils.java rename to javatests/com/google/gerrit/elasticsearch/ElasticTestUtils.java
diff --git a/gerrit-elasticsearch/src/test/java/com/google/gerrit/elasticsearch/ElasticV5QueryAccountsTest.java b/javatests/com/google/gerrit/elasticsearch/ElasticV5QueryAccountsTest.java similarity index 88% rename from gerrit-elasticsearch/src/test/java/com/google/gerrit/elasticsearch/ElasticV5QueryAccountsTest.java rename to javatests/com/google/gerrit/elasticsearch/ElasticV5QueryAccountsTest.java index 01a523b..c8ce54a 100644 --- a/gerrit-elasticsearch/src/test/java/com/google/gerrit/elasticsearch/ElasticV5QueryAccountsTest.java +++ b/javatests/com/google/gerrit/elasticsearch/ElasticV5QueryAccountsTest.java
@@ -16,7 +16,9 @@ import com.google.gerrit.elasticsearch.ElasticTestUtils.ElasticNodeInfo; import com.google.gerrit.server.query.account.AbstractQueryAccountsTest; -import com.google.gerrit.testutil.InMemoryModule; +import com.google.gerrit.testing.ConfigSuite; +import com.google.gerrit.testing.InMemoryModule; +import com.google.gerrit.testing.IndexConfig; import com.google.inject.Guice; import com.google.inject.Injector; import org.eclipse.jgit.lib.Config; @@ -24,6 +26,11 @@ import org.junit.BeforeClass; public class ElasticV5QueryAccountsTest extends AbstractQueryAccountsTest { + @ConfigSuite.Default + public static Config defaultConfig() { + return IndexConfig.createForElasticsearch(); + } + private static ElasticNodeInfo nodeInfo; private static ElasticContainer container;
diff --git a/gerrit-elasticsearch/src/test/java/com/google/gerrit/elasticsearch/ElasticV5QueryChangesTest.java b/javatests/com/google/gerrit/elasticsearch/ElasticV5QueryChangesTest.java similarity index 88% rename from gerrit-elasticsearch/src/test/java/com/google/gerrit/elasticsearch/ElasticV5QueryChangesTest.java rename to javatests/com/google/gerrit/elasticsearch/ElasticV5QueryChangesTest.java index d60b7ec..cfdfa98 100644 --- a/gerrit-elasticsearch/src/test/java/com/google/gerrit/elasticsearch/ElasticV5QueryChangesTest.java +++ b/javatests/com/google/gerrit/elasticsearch/ElasticV5QueryChangesTest.java
@@ -16,7 +16,9 @@ import com.google.gerrit.elasticsearch.ElasticTestUtils.ElasticNodeInfo; import com.google.gerrit.server.query.change.AbstractQueryChangesTest; -import com.google.gerrit.testutil.InMemoryModule; +import com.google.gerrit.testing.ConfigSuite; +import com.google.gerrit.testing.InMemoryModule; +import com.google.gerrit.testing.IndexConfig; import com.google.inject.Guice; import com.google.inject.Injector; import org.eclipse.jgit.lib.Config; @@ -24,6 +26,11 @@ import org.junit.BeforeClass; public class ElasticV5QueryChangesTest extends AbstractQueryChangesTest { + @ConfigSuite.Default + public static Config defaultConfig() { + return IndexConfig.createForElasticsearch(); + } + private static ElasticNodeInfo nodeInfo; private static ElasticContainer container;
diff --git a/gerrit-elasticsearch/src/test/java/com/google/gerrit/elasticsearch/ElasticV5QueryGroupsTest.java b/javatests/com/google/gerrit/elasticsearch/ElasticV5QueryGroupsTest.java similarity index 88% rename from gerrit-elasticsearch/src/test/java/com/google/gerrit/elasticsearch/ElasticV5QueryGroupsTest.java rename to javatests/com/google/gerrit/elasticsearch/ElasticV5QueryGroupsTest.java index e8d9a78..832a7bd 100644 --- a/gerrit-elasticsearch/src/test/java/com/google/gerrit/elasticsearch/ElasticV5QueryGroupsTest.java +++ b/javatests/com/google/gerrit/elasticsearch/ElasticV5QueryGroupsTest.java
@@ -16,7 +16,9 @@ import com.google.gerrit.elasticsearch.ElasticTestUtils.ElasticNodeInfo; import com.google.gerrit.server.query.group.AbstractQueryGroupsTest; -import com.google.gerrit.testutil.InMemoryModule; +import com.google.gerrit.testing.ConfigSuite; +import com.google.gerrit.testing.InMemoryModule; +import com.google.gerrit.testing.IndexConfig; import com.google.inject.Guice; import com.google.inject.Injector; import org.eclipse.jgit.lib.Config; @@ -24,6 +26,11 @@ import org.junit.BeforeClass; public class ElasticV5QueryGroupsTest extends AbstractQueryGroupsTest { + @ConfigSuite.Default + public static Config defaultConfig() { + return IndexConfig.createForElasticsearch(); + } + private static ElasticNodeInfo nodeInfo; private static ElasticContainer container;
diff --git a/gerrit-elasticsearch/src/test/java/com/google/gerrit/elasticsearch/ElasticV5QueryChangesTest.java b/javatests/com/google/gerrit/elasticsearch/ElasticV5QueryProjectsTest.java similarity index 82% copy from gerrit-elasticsearch/src/test/java/com/google/gerrit/elasticsearch/ElasticV5QueryChangesTest.java copy to javatests/com/google/gerrit/elasticsearch/ElasticV5QueryProjectsTest.java index d60b7ec..29d3fa4 100644 --- a/gerrit-elasticsearch/src/test/java/com/google/gerrit/elasticsearch/ElasticV5QueryChangesTest.java +++ b/javatests/com/google/gerrit/elasticsearch/ElasticV5QueryProjectsTest.java
@@ -15,15 +15,22 @@ package com.google.gerrit.elasticsearch; import com.google.gerrit.elasticsearch.ElasticTestUtils.ElasticNodeInfo; -import com.google.gerrit.server.query.change.AbstractQueryChangesTest; -import com.google.gerrit.testutil.InMemoryModule; +import com.google.gerrit.server.query.project.AbstractQueryProjectsTest; +import com.google.gerrit.testing.ConfigSuite; +import com.google.gerrit.testing.InMemoryModule; +import com.google.gerrit.testing.IndexConfig; import com.google.inject.Guice; import com.google.inject.Injector; import org.eclipse.jgit.lib.Config; import org.junit.AfterClass; import org.junit.BeforeClass; -public class ElasticV5QueryChangesTest extends AbstractQueryChangesTest { +public class ElasticV5QueryProjectsTest extends AbstractQueryProjectsTest { + @ConfigSuite.Default + public static Config defaultConfig() { + return IndexConfig.createForElasticsearch(); + } + private static ElasticNodeInfo nodeInfo; private static ElasticContainer container;
diff --git a/gerrit-elasticsearch/src/test/java/com/google/gerrit/elasticsearch/ElasticV6QueryAccountsTest.java b/javatests/com/google/gerrit/elasticsearch/ElasticV6QueryAccountsTest.java similarity index 88% rename from gerrit-elasticsearch/src/test/java/com/google/gerrit/elasticsearch/ElasticV6QueryAccountsTest.java rename to javatests/com/google/gerrit/elasticsearch/ElasticV6QueryAccountsTest.java index 178aba5..191b83c 100644 --- a/gerrit-elasticsearch/src/test/java/com/google/gerrit/elasticsearch/ElasticV6QueryAccountsTest.java +++ b/javatests/com/google/gerrit/elasticsearch/ElasticV6QueryAccountsTest.java
@@ -16,7 +16,9 @@ import com.google.gerrit.elasticsearch.ElasticTestUtils.ElasticNodeInfo; import com.google.gerrit.server.query.account.AbstractQueryAccountsTest; -import com.google.gerrit.testutil.InMemoryModule; +import com.google.gerrit.testing.ConfigSuite; +import com.google.gerrit.testing.InMemoryModule; +import com.google.gerrit.testing.IndexConfig; import com.google.inject.Guice; import com.google.inject.Injector; import org.eclipse.jgit.lib.Config; @@ -24,6 +26,11 @@ import org.junit.BeforeClass; public class ElasticV6QueryAccountsTest extends AbstractQueryAccountsTest { + @ConfigSuite.Default + public static Config defaultConfig() { + return IndexConfig.createForElasticsearch(); + } + private static ElasticNodeInfo nodeInfo; private static ElasticContainer container;
diff --git a/gerrit-elasticsearch/src/test/java/com/google/gerrit/elasticsearch/ElasticV6QueryChangesTest.java b/javatests/com/google/gerrit/elasticsearch/ElasticV6QueryChangesTest.java similarity index 88% rename from gerrit-elasticsearch/src/test/java/com/google/gerrit/elasticsearch/ElasticV6QueryChangesTest.java rename to javatests/com/google/gerrit/elasticsearch/ElasticV6QueryChangesTest.java index 8a1cde3..08f4839 100644 --- a/gerrit-elasticsearch/src/test/java/com/google/gerrit/elasticsearch/ElasticV6QueryChangesTest.java +++ b/javatests/com/google/gerrit/elasticsearch/ElasticV6QueryChangesTest.java
@@ -16,7 +16,9 @@ import com.google.gerrit.elasticsearch.ElasticTestUtils.ElasticNodeInfo; import com.google.gerrit.server.query.change.AbstractQueryChangesTest; -import com.google.gerrit.testutil.InMemoryModule; +import com.google.gerrit.testing.ConfigSuite; +import com.google.gerrit.testing.InMemoryModule; +import com.google.gerrit.testing.IndexConfig; import com.google.inject.Guice; import com.google.inject.Injector; import org.eclipse.jgit.lib.Config; @@ -24,6 +26,10 @@ import org.junit.BeforeClass; public class ElasticV6QueryChangesTest extends AbstractQueryChangesTest { + @ConfigSuite.Default + public static Config defaultConfig() { + return IndexConfig.createForElasticsearch(); + } private static ElasticNodeInfo nodeInfo; private static ElasticContainer container;
diff --git a/gerrit-elasticsearch/src/test/java/com/google/gerrit/elasticsearch/ElasticV6QueryGroupsTest.java b/javatests/com/google/gerrit/elasticsearch/ElasticV6QueryGroupsTest.java similarity index 88% rename from gerrit-elasticsearch/src/test/java/com/google/gerrit/elasticsearch/ElasticV6QueryGroupsTest.java rename to javatests/com/google/gerrit/elasticsearch/ElasticV6QueryGroupsTest.java index b87a920..8bc5767 100644 --- a/gerrit-elasticsearch/src/test/java/com/google/gerrit/elasticsearch/ElasticV6QueryGroupsTest.java +++ b/javatests/com/google/gerrit/elasticsearch/ElasticV6QueryGroupsTest.java
@@ -16,7 +16,9 @@ import com.google.gerrit.elasticsearch.ElasticTestUtils.ElasticNodeInfo; import com.google.gerrit.server.query.group.AbstractQueryGroupsTest; -import com.google.gerrit.testutil.InMemoryModule; +import com.google.gerrit.testing.ConfigSuite; +import com.google.gerrit.testing.InMemoryModule; +import com.google.gerrit.testing.IndexConfig; import com.google.inject.Guice; import com.google.inject.Injector; import org.eclipse.jgit.lib.Config; @@ -24,6 +26,11 @@ import org.junit.BeforeClass; public class ElasticV6QueryGroupsTest extends AbstractQueryGroupsTest { + @ConfigSuite.Default + public static Config defaultConfig() { + return IndexConfig.createForElasticsearch(); + } + private static ElasticNodeInfo nodeInfo; private static ElasticContainer container;
diff --git a/gerrit-elasticsearch/src/test/java/com/google/gerrit/elasticsearch/ElasticV6QueryAccountsTest.java b/javatests/com/google/gerrit/elasticsearch/ElasticV6QueryProjectsTest.java similarity index 82% copy from gerrit-elasticsearch/src/test/java/com/google/gerrit/elasticsearch/ElasticV6QueryAccountsTest.java copy to javatests/com/google/gerrit/elasticsearch/ElasticV6QueryProjectsTest.java index 178aba5..de8e922 100644 --- a/gerrit-elasticsearch/src/test/java/com/google/gerrit/elasticsearch/ElasticV6QueryAccountsTest.java +++ b/javatests/com/google/gerrit/elasticsearch/ElasticV6QueryProjectsTest.java
@@ -15,15 +15,22 @@ package com.google.gerrit.elasticsearch; import com.google.gerrit.elasticsearch.ElasticTestUtils.ElasticNodeInfo; -import com.google.gerrit.server.query.account.AbstractQueryAccountsTest; -import com.google.gerrit.testutil.InMemoryModule; +import com.google.gerrit.server.query.project.AbstractQueryProjectsTest; +import com.google.gerrit.testing.ConfigSuite; +import com.google.gerrit.testing.InMemoryModule; +import com.google.gerrit.testing.IndexConfig; import com.google.inject.Guice; import com.google.inject.Injector; import org.eclipse.jgit.lib.Config; import org.junit.AfterClass; import org.junit.BeforeClass; -public class ElasticV6QueryAccountsTest extends AbstractQueryAccountsTest { +public class ElasticV6QueryProjectsTest extends AbstractQueryProjectsTest { + @ConfigSuite.Default + public static Config defaultConfig() { + return IndexConfig.createForElasticsearch(); + } + private static ElasticNodeInfo nodeInfo; private static ElasticContainer container;
diff --git a/gerrit-elasticsearch/src/test/java/com/google/gerrit/elasticsearch/ElasticV7QueryAccountsTest.java b/javatests/com/google/gerrit/elasticsearch/ElasticV7QueryAccountsTest.java similarity index 88% rename from gerrit-elasticsearch/src/test/java/com/google/gerrit/elasticsearch/ElasticV7QueryAccountsTest.java rename to javatests/com/google/gerrit/elasticsearch/ElasticV7QueryAccountsTest.java index 4a52107..d998d75 100644 --- a/gerrit-elasticsearch/src/test/java/com/google/gerrit/elasticsearch/ElasticV7QueryAccountsTest.java +++ b/javatests/com/google/gerrit/elasticsearch/ElasticV7QueryAccountsTest.java
@@ -16,7 +16,9 @@ import com.google.gerrit.elasticsearch.ElasticTestUtils.ElasticNodeInfo; import com.google.gerrit.server.query.account.AbstractQueryAccountsTest; -import com.google.gerrit.testutil.InMemoryModule; +import com.google.gerrit.testing.ConfigSuite; +import com.google.gerrit.testing.InMemoryModule; +import com.google.gerrit.testing.IndexConfig; import com.google.inject.Guice; import com.google.inject.Injector; import org.eclipse.jgit.lib.Config; @@ -24,6 +26,11 @@ import org.junit.BeforeClass; public class ElasticV7QueryAccountsTest extends AbstractQueryAccountsTest { + @ConfigSuite.Default + public static Config defaultConfig() { + return IndexConfig.createForElasticsearch(); + } + private static ElasticNodeInfo nodeInfo; private static ElasticContainer container;
diff --git a/gerrit-elasticsearch/src/test/java/com/google/gerrit/elasticsearch/ElasticV7QueryChangesTest.java b/javatests/com/google/gerrit/elasticsearch/ElasticV7QueryChangesTest.java similarity index 91% rename from gerrit-elasticsearch/src/test/java/com/google/gerrit/elasticsearch/ElasticV7QueryChangesTest.java rename to javatests/com/google/gerrit/elasticsearch/ElasticV7QueryChangesTest.java index 2301082..c1af5dd 100644 --- a/gerrit-elasticsearch/src/test/java/com/google/gerrit/elasticsearch/ElasticV7QueryChangesTest.java +++ b/javatests/com/google/gerrit/elasticsearch/ElasticV7QueryChangesTest.java
@@ -16,7 +16,9 @@ import com.google.gerrit.elasticsearch.ElasticTestUtils.ElasticNodeInfo; import com.google.gerrit.server.query.change.AbstractQueryChangesTest; -import com.google.gerrit.testutil.InMemoryModule; +import com.google.gerrit.testing.ConfigSuite; +import com.google.gerrit.testing.InMemoryModule; +import com.google.gerrit.testing.IndexConfig; import com.google.inject.Guice; import com.google.inject.Injector; import org.apache.http.client.methods.HttpPost; @@ -29,6 +31,10 @@ import org.junit.BeforeClass; public class ElasticV7QueryChangesTest extends AbstractQueryChangesTest { + @ConfigSuite.Default + public static Config defaultConfig() { + return IndexConfig.createForElasticsearch(); + } private static ElasticNodeInfo nodeInfo; private static ElasticContainer container;
diff --git a/gerrit-elasticsearch/src/test/java/com/google/gerrit/elasticsearch/ElasticV7QueryGroupsTest.java b/javatests/com/google/gerrit/elasticsearch/ElasticV7QueryGroupsTest.java similarity index 88% rename from gerrit-elasticsearch/src/test/java/com/google/gerrit/elasticsearch/ElasticV7QueryGroupsTest.java rename to javatests/com/google/gerrit/elasticsearch/ElasticV7QueryGroupsTest.java index 82f9abe..ea4c7f1 100644 --- a/gerrit-elasticsearch/src/test/java/com/google/gerrit/elasticsearch/ElasticV7QueryGroupsTest.java +++ b/javatests/com/google/gerrit/elasticsearch/ElasticV7QueryGroupsTest.java
@@ -16,7 +16,9 @@ import com.google.gerrit.elasticsearch.ElasticTestUtils.ElasticNodeInfo; import com.google.gerrit.server.query.group.AbstractQueryGroupsTest; -import com.google.gerrit.testutil.InMemoryModule; +import com.google.gerrit.testing.ConfigSuite; +import com.google.gerrit.testing.InMemoryModule; +import com.google.gerrit.testing.IndexConfig; import com.google.inject.Guice; import com.google.inject.Injector; import org.eclipse.jgit.lib.Config; @@ -24,6 +26,11 @@ import org.junit.BeforeClass; public class ElasticV7QueryGroupsTest extends AbstractQueryGroupsTest { + @ConfigSuite.Default + public static Config defaultConfig() { + return IndexConfig.createForElasticsearch(); + } + private static ElasticNodeInfo nodeInfo; private static ElasticContainer container;
diff --git a/gerrit-elasticsearch/src/test/java/com/google/gerrit/elasticsearch/ElasticV7QueryGroupsTest.java b/javatests/com/google/gerrit/elasticsearch/ElasticV7QueryProjectsTest.java similarity index 82% copy from gerrit-elasticsearch/src/test/java/com/google/gerrit/elasticsearch/ElasticV7QueryGroupsTest.java copy to javatests/com/google/gerrit/elasticsearch/ElasticV7QueryProjectsTest.java index 82f9abe..6cc12f1 100644 --- a/gerrit-elasticsearch/src/test/java/com/google/gerrit/elasticsearch/ElasticV7QueryGroupsTest.java +++ b/javatests/com/google/gerrit/elasticsearch/ElasticV7QueryProjectsTest.java
@@ -15,15 +15,22 @@ package com.google.gerrit.elasticsearch; import com.google.gerrit.elasticsearch.ElasticTestUtils.ElasticNodeInfo; -import com.google.gerrit.server.query.group.AbstractQueryGroupsTest; -import com.google.gerrit.testutil.InMemoryModule; +import com.google.gerrit.server.query.project.AbstractQueryProjectsTest; +import com.google.gerrit.testing.ConfigSuite; +import com.google.gerrit.testing.InMemoryModule; +import com.google.gerrit.testing.IndexConfig; import com.google.inject.Guice; import com.google.inject.Injector; import org.eclipse.jgit.lib.Config; import org.junit.AfterClass; import org.junit.BeforeClass; -public class ElasticV7QueryGroupsTest extends AbstractQueryGroupsTest { +public class ElasticV7QueryProjectsTest extends AbstractQueryProjectsTest { + @ConfigSuite.Default + public static Config defaultConfig() { + return IndexConfig.createForElasticsearch(); + } + private static ElasticNodeInfo nodeInfo; private static ElasticContainer container;
diff --git a/gerrit-elasticsearch/src/test/java/com/google/gerrit/elasticsearch/ElasticVersionTest.java b/javatests/com/google/gerrit/elasticsearch/ElasticVersionTest.java similarity index 100% rename from gerrit-elasticsearch/src/test/java/com/google/gerrit/elasticsearch/ElasticVersionTest.java rename to javatests/com/google/gerrit/elasticsearch/ElasticVersionTest.java