Skip to content

Commit 08af27d

Browse files
authored
Display enterprise license as platinum in /_xpack (#58718)
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. Backport of: #58217
1 parent af6e039 commit 08af27d

File tree

8 files changed

+143
-27
lines changed

8 files changed

+143
-27
lines changed

x-pack/plugin/core/src/main/java/org/elasticsearch/protocol/xpack/XPackInfoRequest.java

+19-1
Original file line numberDiff line numberDiff line change
@@ -5,10 +5,12 @@
55
*/
66
package org.elasticsearch.protocol.xpack;
77

8+
import org.elasticsearch.Version;
89
import org.elasticsearch.action.ActionRequest;
910
import org.elasticsearch.action.ActionRequestValidationException;
1011
import org.elasticsearch.common.io.stream.StreamInput;
1112
import org.elasticsearch.common.io.stream.StreamOutput;
13+
import org.elasticsearch.license.License;
1214

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

4143
private boolean verbose;
4244
private EnumSet<Category> categories = EnumSet.noneOf(Category.class);
45+
private int licenseVersion = License.VERSION_CURRENT;
4346

44-
public XPackInfoRequest() {}
47+
public XPackInfoRequest() {
48+
}
4549

4650
public XPackInfoRequest(StreamInput in) throws IOException {
4751
// NOTE: this does *not* call super, THIS IS A BUG
@@ -52,6 +56,9 @@ public XPackInfoRequest(StreamInput in) throws IOException {
5256
categories.add(Category.valueOf(in.readString()));
5357
}
5458
this.categories = categories;
59+
if (in.getVersion().onOrAfter(Version.V_7_8_1)) {
60+
this.licenseVersion = in.readVInt();
61+
}
5562
}
5663

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

80+
public int getLicenseVersion() {
81+
return licenseVersion;
82+
}
83+
84+
public void setLicenseVersion(int licenseVersion) {
85+
this.licenseVersion = licenseVersion;
86+
}
87+
7388
@Override
7489
public ActionRequestValidationException validate() {
7590
return null;
@@ -82,5 +97,8 @@ public void writeTo(StreamOutput out) throws IOException {
8297
for (Category category : categories) {
8398
out.writeString(category.name());
8499
}
100+
if (out.getVersion().onOrAfter(Version.V_7_8_1)) {
101+
out.writeVInt(this.licenseVersion);
102+
}
85103
}
86104
}

x-pack/plugin/core/src/main/java/org/elasticsearch/xpack/core/action/TransportXPackInfoAction.java

+12-2
Original file line numberDiff line numberDiff line change
@@ -49,8 +49,18 @@ protected void doExecute(Task task, XPackInfoRequest request, ActionListener<XPa
4949
if (request.getCategories().contains(XPackInfoRequest.Category.LICENSE)) {
5050
License license = licenseService.getLicense();
5151
if (license != null) {
52-
licenseInfo = new LicenseInfo(license.uid(), license.type(), license.operationMode().description(),
53-
license.status(), license.expiryDate());
52+
String type = license.type();
53+
License.OperationMode mode = license.operationMode();
54+
if (request.getLicenseVersion() < License.VERSION_ENTERPRISE) {
55+
if (License.LicenseType.ENTERPRISE.getTypeName().equals(type)) {
56+
type = License.LicenseType.PLATINUM.getTypeName();
57+
}
58+
if (mode == License.OperationMode.ENTERPRISE) {
59+
mode = License.OperationMode.PLATINUM;
60+
}
61+
}
62+
licenseInfo = new LicenseInfo(license.uid(), type, mode.description(), license.status(),
63+
license.expiryDate());
5464
}
5565
}
5666

x-pack/plugin/core/src/main/java/org/elasticsearch/xpack/core/action/XPackInfoRequestBuilder.java

+4
Original file line numberDiff line numberDiff line change
@@ -33,4 +33,8 @@ public XPackInfoRequestBuilder setCategories(EnumSet<XPackInfoRequest.Category>
3333
return this;
3434
}
3535

36+
public XPackInfoRequestBuilder setLicenseVersion(int licenseVersion) {
37+
request.setLicenseVersion(licenseVersion);
38+
return this;
39+
}
3640
}

x-pack/plugin/core/src/main/java/org/elasticsearch/xpack/core/rest/action/RestXPackInfoAction.java

+6-1
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@
55
*/
66
package org.elasticsearch.xpack.core.rest.action;
77

8+
import org.elasticsearch.license.License;
89
import org.elasticsearch.protocol.xpack.XPackInfoRequest;
910
import org.elasticsearch.rest.RestRequest;
1011
import org.elasticsearch.rest.action.RestToXContentListener;
@@ -36,16 +37,20 @@ public String getName() {
3637

3738
@Override
3839
public RestChannelConsumer doPrepareRequest(RestRequest request, XPackClient client) throws IOException {
39-
4040
// we piggyback verbosity on "human" output
4141
boolean verbose = request.paramAsBoolean("human", true);
4242

43+
// Hide enterprise licenses by default, there is an opt-in flag to show them
44+
final boolean acceptEnterprise = request.paramAsBoolean("accept_enterprise", false);
45+
final int licenseVersion = acceptEnterprise ? License.VERSION_CURRENT : License.VERSION_CRYPTO_ALGORITHMS;
46+
4347
EnumSet<XPackInfoRequest.Category> categories = XPackInfoRequest.Category
4448
.toSet(request.paramAsStringArray("categories", new String[] { "_all" }));
4549
return channel ->
4650
client.prepareInfo()
4751
.setVerbose(verbose)
4852
.setCategories(categories)
53+
.setLicenseVersion(licenseVersion)
4954
.execute(new RestToXContentListener<>(channel));
5055
}
5156
}

x-pack/plugin/core/src/test/java/org/elasticsearch/xpack/core/action/TransportXPackInfoActionTests.java

+67-23
Original file line numberDiff line numberDiff line change
@@ -29,6 +29,7 @@
2929
import java.util.concurrent.CountDownLatch;
3030
import java.util.concurrent.TimeUnit;
3131
import java.util.concurrent.atomic.AtomicReference;
32+
import java.util.function.Consumer;
3233

3334
import static org.hamcrest.Matchers.equalTo;
3435
import static org.hamcrest.Matchers.hasKey;
@@ -41,7 +42,69 @@
4142
public class TransportXPackInfoActionTests extends ESTestCase {
4243

4344
public void testDoExecute() throws Exception {
45+
EnumSet<XPackInfoRequest.Category> categories = EnumSet.noneOf(XPackInfoRequest.Category.class);
46+
int maxCategoryCount = randomIntBetween(0, XPackInfoRequest.Category.values().length);
47+
for (int i = 0; i < maxCategoryCount; i++) {
48+
categories.add(randomFrom(XPackInfoRequest.Category.values()));
49+
}
50+
51+
License license = mock(License.class);
52+
long expiryDate = randomLong();
53+
when(license.expiryDate()).thenReturn(expiryDate);
54+
LicenseStatus status = randomFrom(LicenseStatus.values());
55+
when(license.status()).thenReturn(status);
56+
String licenseType = randomAlphaOfLength(10);
57+
when(license.type()).thenReturn(licenseType);
58+
License.OperationMode licenseMode = randomFrom(License.OperationMode.values());
59+
when(license.operationMode()).thenReturn(licenseMode);
60+
String uid = randomAlphaOfLength(30);
61+
when(license.uid()).thenReturn(uid);
62+
63+
checkAction(categories, -1, license, (XPackInfoResponse.LicenseInfo licenseInfo) -> {
64+
assertThat(licenseInfo.getExpiryDate(), is(expiryDate));
65+
assertThat(licenseInfo.getStatus(), is(status));
66+
assertThat(licenseInfo.getType(), is(licenseType));
67+
assertThat(licenseInfo.getMode(), is(licenseMode.name().toLowerCase(Locale.ROOT)));
68+
assertThat(licenseInfo.getUid(), is(uid));
69+
});
70+
}
71+
72+
public void testDoExecuteWithEnterpriseLicenseWithoutBackwardsCompat() throws Exception {
73+
EnumSet<XPackInfoRequest.Category> categories = EnumSet.allOf(XPackInfoRequest.Category.class);
74+
75+
License license = mock(License.class);
76+
when(license.expiryDate()).thenReturn(randomLong());
77+
when(license.status()).thenReturn(LicenseStatus.ACTIVE);
78+
when(license.type()).thenReturn("enterprise");
79+
when(license.operationMode()).thenReturn(License.OperationMode.ENTERPRISE);
80+
when(license.uid()).thenReturn(randomAlphaOfLength(30));
81+
82+
checkAction(categories, randomFrom(License.VERSION_ENTERPRISE, License.VERSION_CURRENT, -1), license,
83+
licenseInfo -> {
84+
assertThat(licenseInfo.getType(), is("enterprise"));
85+
assertThat(licenseInfo.getMode(), is("enterprise"));
86+
});
87+
}
4488

89+
public void testDoExecuteWithEnterpriseLicenseWithBackwardsCompat() throws Exception {
90+
EnumSet<XPackInfoRequest.Category> categories = EnumSet.allOf(XPackInfoRequest.Category.class);
91+
92+
License license = mock(License.class);
93+
when(license.expiryDate()).thenReturn(randomLong());
94+
when(license.status()).thenReturn(LicenseStatus.ACTIVE);
95+
when(license.type()).thenReturn("enterprise");
96+
when(license.operationMode()).thenReturn(License.OperationMode.ENTERPRISE);
97+
when(license.uid()).thenReturn(randomAlphaOfLength(30));
98+
99+
checkAction(categories, randomFrom(License.VERSION_START_DATE, License.VERSION_CRYPTO_ALGORITHMS), license,
100+
licenseInfo -> {
101+
assertThat(licenseInfo.getType(), is("platinum"));
102+
assertThat(licenseInfo.getMode(), is("platinum"));
103+
});
104+
}
105+
106+
private void checkAction(EnumSet<XPackInfoRequest.Category> categories, int licenseVersion, License license,
107+
Consumer<XPackInfoResponse.LicenseInfo> licenseVerifier) throws InterruptedException {
45108
LicenseService licenseService = mock(LicenseService.class);
46109

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

62-
License license = mock(License.class);
63-
long expiryDate = randomLong();
64-
when(license.expiryDate()).thenReturn(expiryDate);
65-
LicenseStatus status = randomFrom(LicenseStatus.values());
66-
when(license.status()).thenReturn(status);
67-
String type = randomAlphaOfLength(10);
68-
when(license.type()).thenReturn(type);
69-
License.OperationMode mode = randomFrom(License.OperationMode.values());
70-
when(license.operationMode()).thenReturn(mode);
71-
String uid = randomAlphaOfLength(30);
72-
when(license.uid()).thenReturn(uid);
73125
when(licenseService.getLicense()).thenReturn(license);
74126

75127
XPackInfoRequest request = new XPackInfoRequest();
76128
request.setVerbose(randomBoolean());
77-
78-
EnumSet<XPackInfoRequest.Category> categories = EnumSet.noneOf(XPackInfoRequest.Category.class);
79-
int maxCategoryCount = randomIntBetween(0, XPackInfoRequest.Category.values().length);
80-
for (int i = 0; i < maxCategoryCount; i++) {
81-
categories.add(randomFrom(XPackInfoRequest.Category.values()));
82-
}
83129
request.setCategories(categories);
130+
if (licenseVersion != -1) {
131+
request.setLicenseVersion(licenseVersion);
132+
}
84133

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

115164
if (request.getCategories().contains(XPackInfoRequest.Category.LICENSE)) {
116165
assertThat(response.get().getLicenseInfo(), notNullValue());
117-
assertThat(response.get().getLicenseInfo().getExpiryDate(), is(expiryDate));
118-
assertThat(response.get().getLicenseInfo().getStatus(), is(status));
119-
assertThat(response.get().getLicenseInfo().getType(), is(type));
120-
assertThat(response.get().getLicenseInfo().getMode(), is(mode.name().toLowerCase(Locale.ROOT)));
121-
assertThat(response.get().getLicenseInfo().getUid(), is(uid));
166+
licenseVerifier.accept(response.get().getLicenseInfo());
122167
} else {
123168
assertThat(response.get().getLicenseInfo(), nullValue());
124169
}
@@ -136,7 +181,6 @@ public void onFailure(Exception e) {
136181
} else {
137182
assertThat(response.get().getFeatureSetsInfo(), nullValue());
138183
}
139-
140184
}
141185

142186
}

x-pack/plugin/src/test/resources/rest-api-spec/api/xpack.info.json

+4
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,10 @@
1919
"categories":{
2020
"type":"list",
2121
"description":"Comma-separated list of info categories. Can be any of: build, license, features"
22+
},
23+
"accept_enterprise":{
24+
"type":"boolean",
25+
"description":"If an enterprise license is installed, return the type and mode as 'enterprise' (default: false)"
2226
}
2327
}
2428
}

x-pack/plugin/src/test/resources/rest-api-spec/test/license/30_enterprise_license.yml

+22
Original file line numberDiff line numberDiff line change
@@ -27,6 +27,13 @@ teardown:
2727
- match: { license.type: "platinum" }
2828
- match: { license.max_nodes: 50 }
2929

30+
## Check the xpack info API as well
31+
- do:
32+
xpack.info: {}
33+
- match: { license.type: "platinum" }
34+
- match: { license.mode: "platinum" }
35+
36+
## Check the opt-in v5 license type
3037
- do:
3138
license.get:
3239
accept_enterprise: true
@@ -39,6 +46,13 @@ teardown:
3946
- match: { license.max_resource_units: 50 }
4047
- match: { license.max_nodes: null }
4148

49+
## Check the xpack info API as well
50+
- do:
51+
xpack.info:
52+
accept_enterprise: true
53+
- match: { license.type: "enterprise" }
54+
- match: { license.mode: "enterprise" }
55+
4256
- do:
4357
license.get:
4458
accept_enterprise: false
@@ -49,3 +63,11 @@ teardown:
4963
## opt-out of real enterprise type
5064
- match: { license.type: "platinum" }
5165
- match: { license.max_nodes: 50 }
66+
67+
## Check the xpack info API as well
68+
- do:
69+
xpack.info:
70+
accept_enterprise: false
71+
- match: { license.type: "platinum" }
72+
- match: { license.mode: "platinum" }
73+
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,9 @@
1+
"XPack Info API":
2+
- do:
3+
xpack.info: {}
4+
5+
- match: { license.type: "trial" }
6+
- match: { license.mode: "trial" }
7+
- match: { license.status: "active" }
8+
9+

0 commit comments

Comments
 (0)