Skip to content

Commit

Permalink
Display enterprise license as platinum in /_xpack (elastic#58217)
Browse files Browse the repository at this point in the history
The GET /_license endpoint displays "enterprise" licenses as
"platinum" by default so that old clients (including beats, kibana and
logstash) know to interpret this new license type as if it were a
platinum license.

However, this compatibility layer was not applied to the GET /_xpack/
endpoint which also displays a license type & mode.

This commit causes the _xpack API to mimic the _license API and treat
enterprise as platinum by default, with a new accept_enterprise
parameter that will cause the API to return the correct "enterprise"
value.

This BWC layer exists only for the 7.x branch.
This is a breaking change because, since 7.6, the _xpack API has
returned "enterprise" for enterprise licenses, but this has been found
to break old versions of beats and logstash so needs to be corrected.
  • Loading branch information
tvernum authored Jun 30, 2020
1 parent 8341ebc commit dcc5a06
Show file tree
Hide file tree
Showing 9 changed files with 149 additions and 27 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -5,10 +5,12 @@
*/
package org.elasticsearch.protocol.xpack;

import org.elasticsearch.Version;
import org.elasticsearch.action.ActionRequest;
import org.elasticsearch.action.ActionRequestValidationException;
import org.elasticsearch.common.io.stream.StreamInput;
import org.elasticsearch.common.io.stream.StreamOutput;
import org.elasticsearch.license.License;

import java.io.IOException;
import java.util.EnumSet;
Expand Down Expand Up @@ -40,8 +42,10 @@ public static EnumSet<Category> toSet(String... categories) {

private boolean verbose;
private EnumSet<Category> categories = EnumSet.noneOf(Category.class);
private int licenseVersion = License.VERSION_CURRENT;

public XPackInfoRequest() {}
public XPackInfoRequest() {
}

public XPackInfoRequest(StreamInput in) throws IOException {
// NOTE: this does *not* call super, THIS IS A BUG
Expand All @@ -52,6 +56,9 @@ public XPackInfoRequest(StreamInput in) throws IOException {
categories.add(Category.valueOf(in.readString()));
}
this.categories = categories;
if (in.getVersion().onOrAfter(Version.V_7_8_1)) {
this.licenseVersion = in.readVInt();
}
}

public void setVerbose(boolean verbose) {
Expand All @@ -70,6 +77,14 @@ public EnumSet<Category> getCategories() {
return categories;
}

public int getLicenseVersion() {
return licenseVersion;
}

public void setLicenseVersion(int licenseVersion) {
this.licenseVersion = licenseVersion;
}

@Override
public ActionRequestValidationException validate() {
return null;
Expand All @@ -82,5 +97,8 @@ public void writeTo(StreamOutput out) throws IOException {
for (Category category : categories) {
out.writeString(category.name());
}
if (out.getVersion().onOrAfter(Version.V_7_8_1)) {
out.writeVInt(this.licenseVersion);
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -49,8 +49,18 @@ protected void doExecute(Task task, XPackInfoRequest request, ActionListener<XPa
if (request.getCategories().contains(XPackInfoRequest.Category.LICENSE)) {
License license = licenseService.getLicense();
if (license != null) {
licenseInfo = new LicenseInfo(license.uid(), license.type(), license.operationMode().description(),
license.status(), license.expiryDate());
String type = license.type();
License.OperationMode mode = license.operationMode();
if (request.getLicenseVersion() < License.VERSION_ENTERPRISE) {
if (License.LicenseType.ENTERPRISE.getTypeName().equals(type)) {
type = License.LicenseType.PLATINUM.getTypeName();
}
if (mode == License.OperationMode.ENTERPRISE) {
mode = License.OperationMode.PLATINUM;
}
}
licenseInfo = new LicenseInfo(license.uid(), type, mode.description(), license.status(),
license.expiryDate());
}
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -33,4 +33,8 @@ public XPackInfoRequestBuilder setCategories(EnumSet<XPackInfoRequest.Category>
return this;
}

public XPackInfoRequestBuilder setLicenseVersion(int licenseVersion) {
request.setLicenseVersion(licenseVersion);
return this;
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@
*/
package org.elasticsearch.xpack.core.rest.action;

import org.elasticsearch.license.License;
import org.elasticsearch.protocol.xpack.XPackInfoRequest;
import org.elasticsearch.rest.RestRequest;
import org.elasticsearch.rest.action.RestToXContentListener;
Expand Down Expand Up @@ -36,16 +37,20 @@ public String getName() {

@Override
public RestChannelConsumer doPrepareRequest(RestRequest request, XPackClient client) throws IOException {

// we piggyback verbosity on "human" output
boolean verbose = request.paramAsBoolean("human", true);

// Hide enterprise licenses by default, there is an opt-in flag to show them
final boolean acceptEnterprise = request.paramAsBoolean("accept_enterprise", false);
final int licenseVersion = acceptEnterprise ? License.VERSION_CURRENT : License.VERSION_CRYPTO_ALGORITHMS;

EnumSet<XPackInfoRequest.Category> categories = XPackInfoRequest.Category
.toSet(request.paramAsStringArray("categories", new String[] { "_all" }));
return channel ->
client.prepareInfo()
.setVerbose(verbose)
.setCategories(categories)
.setLicenseVersion(licenseVersion)
.execute(new RestToXContentListener<>(channel));
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,7 @@
import java.util.concurrent.CountDownLatch;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.atomic.AtomicReference;
import java.util.function.Consumer;

import static org.hamcrest.Matchers.equalTo;
import static org.hamcrest.Matchers.hasKey;
Expand All @@ -41,7 +42,69 @@
public class TransportXPackInfoActionTests extends ESTestCase {

public void testDoExecute() throws Exception {
EnumSet<XPackInfoRequest.Category> categories = EnumSet.noneOf(XPackInfoRequest.Category.class);
int maxCategoryCount = randomIntBetween(0, XPackInfoRequest.Category.values().length);
for (int i = 0; i < maxCategoryCount; i++) {
categories.add(randomFrom(XPackInfoRequest.Category.values()));
}

License license = mock(License.class);
long expiryDate = randomLong();
when(license.expiryDate()).thenReturn(expiryDate);
LicenseStatus status = randomFrom(LicenseStatus.values());
when(license.status()).thenReturn(status);
String licenseType = randomAlphaOfLength(10);
when(license.type()).thenReturn(licenseType);
License.OperationMode licenseMode = randomFrom(License.OperationMode.values());
when(license.operationMode()).thenReturn(licenseMode);
String uid = randomAlphaOfLength(30);
when(license.uid()).thenReturn(uid);

checkAction(categories, -1, license, (XPackInfoResponse.LicenseInfo licenseInfo) -> {
assertThat(licenseInfo.getExpiryDate(), is(expiryDate));
assertThat(licenseInfo.getStatus(), is(status));
assertThat(licenseInfo.getType(), is(licenseType));
assertThat(licenseInfo.getMode(), is(licenseMode.name().toLowerCase(Locale.ROOT)));
assertThat(licenseInfo.getUid(), is(uid));
});
}

public void testDoExecuteWithEnterpriseLicenseWithoutBackwardsCompat() throws Exception {
EnumSet<XPackInfoRequest.Category> categories = EnumSet.allOf(XPackInfoRequest.Category.class);

License license = mock(License.class);
when(license.expiryDate()).thenReturn(randomLong());
when(license.status()).thenReturn(LicenseStatus.ACTIVE);
when(license.type()).thenReturn("enterprise");
when(license.operationMode()).thenReturn(License.OperationMode.ENTERPRISE);
when(license.uid()).thenReturn(randomAlphaOfLength(30));

checkAction(categories, randomFrom(License.VERSION_ENTERPRISE, License.VERSION_CURRENT, -1), license,
licenseInfo -> {
assertThat(licenseInfo.getType(), is("enterprise"));
assertThat(licenseInfo.getMode(), is("enterprise"));
});
}

public void testDoExecuteWithEnterpriseLicenseWithBackwardsCompat() throws Exception {
EnumSet<XPackInfoRequest.Category> categories = EnumSet.allOf(XPackInfoRequest.Category.class);

License license = mock(License.class);
when(license.expiryDate()).thenReturn(randomLong());
when(license.status()).thenReturn(LicenseStatus.ACTIVE);
when(license.type()).thenReturn("enterprise");
when(license.operationMode()).thenReturn(License.OperationMode.ENTERPRISE);
when(license.uid()).thenReturn(randomAlphaOfLength(30));

checkAction(categories, randomFrom(License.VERSION_START_DATE, License.VERSION_CRYPTO_ALGORITHMS), license,
licenseInfo -> {
assertThat(licenseInfo.getType(), is("platinum"));
assertThat(licenseInfo.getMode(), is("platinum"));
});
}

private void checkAction(EnumSet<XPackInfoRequest.Category> categories, int licenseVersion, License license,
Consumer<XPackInfoResponse.LicenseInfo> licenseVerifier) throws InterruptedException {
LicenseService licenseService = mock(LicenseService.class);

final Set<XPackFeatureSet> featureSets = new HashSet<>();
Expand All @@ -59,28 +122,14 @@ public void testDoExecute() throws Exception {
TransportXPackInfoAction action = new TransportXPackInfoAction(transportService, mock(ActionFilters.class),
licenseService, featureSets);

License license = mock(License.class);
long expiryDate = randomLong();
when(license.expiryDate()).thenReturn(expiryDate);
LicenseStatus status = randomFrom(LicenseStatus.values());
when(license.status()).thenReturn(status);
String type = randomAlphaOfLength(10);
when(license.type()).thenReturn(type);
License.OperationMode mode = randomFrom(License.OperationMode.values());
when(license.operationMode()).thenReturn(mode);
String uid = randomAlphaOfLength(30);
when(license.uid()).thenReturn(uid);
when(licenseService.getLicense()).thenReturn(license);

XPackInfoRequest request = new XPackInfoRequest();
request.setVerbose(randomBoolean());

EnumSet<XPackInfoRequest.Category> categories = EnumSet.noneOf(XPackInfoRequest.Category.class);
int maxCategoryCount = randomIntBetween(0, XPackInfoRequest.Category.values().length);
for (int i = 0; i < maxCategoryCount; i++) {
categories.add(randomFrom(XPackInfoRequest.Category.values()));
}
request.setCategories(categories);
if (licenseVersion != -1) {
request.setLicenseVersion(licenseVersion);
}

final CountDownLatch latch = new CountDownLatch(1);
final AtomicReference<XPackInfoResponse> response = new AtomicReference<>();
Expand Down Expand Up @@ -114,11 +163,7 @@ public void onFailure(Exception e) {

if (request.getCategories().contains(XPackInfoRequest.Category.LICENSE)) {
assertThat(response.get().getLicenseInfo(), notNullValue());
assertThat(response.get().getLicenseInfo().getExpiryDate(), is(expiryDate));
assertThat(response.get().getLicenseInfo().getStatus(), is(status));
assertThat(response.get().getLicenseInfo().getType(), is(type));
assertThat(response.get().getLicenseInfo().getMode(), is(mode.name().toLowerCase(Locale.ROOT)));
assertThat(response.get().getLicenseInfo().getUid(), is(uid));
licenseVerifier.accept(response.get().getLicenseInfo());
} else {
assertThat(response.get().getLicenseInfo(), nullValue());
}
Expand All @@ -136,7 +181,6 @@ public void onFailure(Exception e) {
} else {
assertThat(response.get().getFeatureSetsInfo(), nullValue());
}

}

}
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,10 @@
"categories":{
"type":"list",
"description":"Comma-separated list of info categories. Can be any of: build, license, features"
},
"accept_enterprise":{
"type":"boolean",
"description":"If an enterprise license is installed, return the type and mode as 'enterprise' (default: false)"
}
}
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,13 @@ teardown:
- match: { license.type: "platinum" }
- match: { license.max_nodes: 50 }

## Check the xpack info API as well
- do:
xpack.info: {}
- match: { license.type: "platinum" }
- match: { license.mode: "platinum" }

## Check the opt-in v5 license type
- do:
license.get:
accept_enterprise: true
Expand All @@ -39,6 +46,13 @@ teardown:
- match: { license.max_resource_units: 50 }
- match: { license.max_nodes: null }

## Check the xpack info API as well
- do:
xpack.info:
accept_enterprise: true
- match: { license.type: "enterprise" }
- match: { license.mode: "enterprise" }

- do:
license.get:
accept_enterprise: false
Expand All @@ -49,3 +63,11 @@ teardown:
## opt-out of real enterprise type
- match: { license.type: "platinum" }
- match: { license.max_nodes: 50 }

## Check the xpack info API as well
- do:
xpack.info:
accept_enterprise: false
- match: { license.type: "platinum" }
- match: { license.mode: "platinum" }

Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
"XPack Info API":
- do:
xpack.info: {}

- match: { license.type: "trial" }
- match: { license.mode: "trial" }
- match: { license.status: "active" }


Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@
import org.elasticsearch.common.settings.Settings;
import org.elasticsearch.common.xcontent.ObjectPath;
import org.elasticsearch.common.xcontent.support.XContentMapValues;
import org.junit.Before;

import java.io.IOException;
import java.util.Map;
Expand All @@ -23,6 +24,11 @@

public class CcrRollingUpgradeIT extends AbstractMultiClusterUpgradeTestCase {

@Before
public void skipForBackportOf58217() {
assumeFalse("Skip while back-porting #58217", UPGRADE_FROM_VERSION.equals(Version.V_7_8_1));
}

public void testUniDirectionalIndexFollowing() throws Exception {
logger.info("clusterName={}, upgradeState={}", clusterName, upgradeState);

Expand Down

0 comments on commit dcc5a06

Please sign in to comment.