Skip to content

Commit 592481e

Browse files
authored
Require acknowledgement to start_trial license (#30135)
This is related to #30134. It modifies the start_trial action to require an acknowledgement parameter in the rest request to actually start the trial license. There are backwards compatibility issues as prior ES versions did not support this parameter. To handle this, it is assumed that a request coming from a node prior to 6.3 is acknowledged. And attempts to write a non-acknowledged request to a prior to 6.3 node will throw an exception. Additionally this PR adds messages about the trial license the user is generating.
1 parent 0d8aed8 commit 592481e

File tree

12 files changed

+206
-39
lines changed

12 files changed

+206
-39
lines changed

x-pack/docs/build.gradle

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -61,7 +61,6 @@ buildRestTests.expectedUnconvertedCandidates = [
6161
'en/watcher/trigger/schedule/yearly.asciidoc',
6262
'en/watcher/troubleshooting.asciidoc',
6363
'en/rest-api/license/delete-license.asciidoc',
64-
'en/rest-api/license/start-trial.asciidoc',
6564
'en/rest-api/license/update-license.asciidoc',
6665
'en/ml/api-quickref.asciidoc',
6766
'en/rest-api/ml/delete-calendar-event.asciidoc',

x-pack/docs/en/rest-api/license/start-trial.asciidoc

Lines changed: 23 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -40,7 +40,7 @@ The following example checks whether you are eligible to start a trial:
4040

4141
[source,js]
4242
------------------------------------------------------------
43-
POST _xpack/license/start_trial
43+
GET _xpack/license/start_trial
4444
------------------------------------------------------------
4545
// CONSOLE
4646
// TEST[skip:license testing issues]
@@ -49,6 +49,27 @@ Example response:
4949
[source,js]
5050
------------------------------------------------------------
5151
{
52-
"trial_was_started": true
52+
"eligible_to_start_trial": true
5353
}
5454
------------------------------------------------------------
55+
// NOTCONSOLE
56+
57+
The following example starts a 30-day trial license. The acknowledge
58+
parameter is required as you are initiating a license that will expire.
59+
60+
[source,js]
61+
------------------------------------------------------------
62+
POST _xpack/license/start_trial?acknowledge=true
63+
------------------------------------------------------------
64+
// CONSOLE
65+
// TEST[skip:license testing issues]
66+
67+
Example response:
68+
[source,js]
69+
------------------------------------------------------------
70+
{
71+
"trial_was_started": true,
72+
"acknowledged": true
73+
}
74+
------------------------------------------------------------
75+
// NOTCONSOLE

x-pack/plugin/core/src/main/java/org/elasticsearch/license/LicensingClient.java

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -41,11 +41,11 @@ public void deleteLicense(DeleteLicenseRequest request, ActionListener<DeleteLic
4141
client.execute(DeleteLicenseAction.INSTANCE, request, listener);
4242
}
4343

44-
public PostStartTrialRequestBuilder preparePostUpgradeToTrial() {
44+
public PostStartTrialRequestBuilder preparePostStartTrial() {
4545
return new PostStartTrialRequestBuilder(client, PostStartTrialAction.INSTANCE);
4646
}
4747

48-
public GetTrialStatusRequestBuilder prepareGetUpgradeToTrial() {
48+
public GetTrialStatusRequestBuilder prepareGetStartTrial() {
4949
return new GetTrialStatusRequestBuilder(client, GetTrialStatusAction.INSTANCE);
5050
}
5151

x-pack/plugin/core/src/main/java/org/elasticsearch/license/PostStartTrialRequest.java

Lines changed: 27 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,7 @@
1515

1616
public class PostStartTrialRequest extends MasterNodeRequest<PostStartTrialRequest> {
1717

18+
private boolean acknowledge = false;
1819
private String type;
1920

2021
@Override
@@ -31,25 +32,47 @@ public String getType() {
3132
return type;
3233
}
3334

35+
public PostStartTrialRequest acknowledge(boolean acknowledge) {
36+
this.acknowledge = acknowledge;
37+
return this;
38+
}
39+
40+
public boolean isAcknowledged() {
41+
return acknowledge;
42+
}
43+
3444
@Override
3545
public void readFrom(StreamInput in) throws IOException {
3646
super.readFrom(in);
3747
if (in.getVersion().onOrAfter(Version.V_6_3_0)) {
3848
type = in.readString();
49+
acknowledge = in.readBoolean();
3950
} else {
4051
type = "trial";
52+
acknowledge = true;
4153
}
4254
}
4355

4456
@Override
4557
public void writeTo(StreamOutput out) throws IOException {
46-
super.writeTo(out);
47-
Version version = Version.V_6_3_0;
58+
// TODO: Change to 6.3 after backport
59+
Version version = Version.V_7_0_0_alpha1;
4860
if (out.getVersion().onOrAfter(version)) {
61+
super.writeTo(out);
4962
out.writeString(type);
63+
out.writeBoolean(acknowledge);
5064
} else {
51-
throw new IllegalArgumentException("All nodes in cluster must be version [" + version
52-
+ "] or newer to use `type` parameter. Attempting to write to node with version [" + out.getVersion() + "].");
65+
if ("trial".equals(type) == false) {
66+
throw new IllegalArgumentException("All nodes in cluster must be version [" + version
67+
+ "] or newer to start trial with a different type than 'trial'. Attempting to write to " +
68+
"a node with version [" + out.getVersion() + "] with trial type [" + type + "].");
69+
} else if (acknowledge == false) {
70+
throw new IllegalArgumentException("Request must be acknowledged to send to a node with a version " +
71+
"prior to [" + version + "]. Attempting to send request to node with version [" + out.getVersion() + "] " +
72+
"without acknowledgement.");
73+
} else {
74+
super.writeTo(out);
75+
}
5376
}
5477
}
5578
}

x-pack/plugin/core/src/main/java/org/elasticsearch/license/PostStartTrialRequestBuilder.java

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -14,4 +14,9 @@ class PostStartTrialRequestBuilder extends ActionRequestBuilder<PostStartTrialRe
1414
PostStartTrialRequestBuilder(ElasticsearchClient client, PostStartTrialAction action) {
1515
super(client, action, new PostStartTrialRequest());
1616
}
17+
18+
public PostStartTrialRequestBuilder setAcknowledge(boolean acknowledge) {
19+
request.acknowledge(acknowledge);
20+
return this;
21+
}
1722
}

x-pack/plugin/core/src/main/java/org/elasticsearch/license/PostStartTrialResponse.java

Lines changed: 70 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -5,23 +5,33 @@
55
*/
66
package org.elasticsearch.license;
77

8+
import org.elasticsearch.Version;
89
import org.elasticsearch.action.ActionResponse;
910
import org.elasticsearch.common.io.stream.StreamInput;
1011
import org.elasticsearch.common.io.stream.StreamOutput;
1112
import org.elasticsearch.rest.RestStatus;
1213

1314
import java.io.IOException;
15+
import java.util.Collections;
16+
import java.util.HashMap;
17+
import java.util.Map;
1418

1519
class PostStartTrialResponse extends ActionResponse {
1620

21+
// Nodes Prior to 6.3 did not have NEED_ACKNOWLEDGEMENT as part of status
22+
enum Pre63Status {
23+
UPGRADED_TO_TRIAL,
24+
TRIAL_ALREADY_ACTIVATED;
25+
}
1726
enum Status {
1827
UPGRADED_TO_TRIAL(true, null, RestStatus.OK),
19-
TRIAL_ALREADY_ACTIVATED(false, "Operation failed: Trial was already activated.", RestStatus.FORBIDDEN);
28+
TRIAL_ALREADY_ACTIVATED(false, "Operation failed: Trial was already activated.", RestStatus.FORBIDDEN),
29+
NEED_ACKNOWLEDGEMENT(false,"Operation failed: Needs acknowledgement.", RestStatus.OK);
2030

2131
private final boolean isTrialStarted;
32+
2233
private final String errorMessage;
2334
private final RestStatus restStatus;
24-
2535
Status(boolean isTrialStarted, String errorMessage, RestStatus restStatus) {
2636
this.isTrialStarted = isTrialStarted;
2737
this.errorMessage = errorMessage;
@@ -39,15 +49,24 @@ String getErrorMessage() {
3949
RestStatus getRestStatus() {
4050
return restStatus;
4151
}
52+
4253
}
4354

4455
private Status status;
56+
private Map<String, String[]> acknowledgeMessages;
57+
private String acknowledgeMessage;
4558

4659
PostStartTrialResponse() {
4760
}
4861

4962
PostStartTrialResponse(Status status) {
63+
this(status, Collections.emptyMap(), null);
64+
}
65+
66+
PostStartTrialResponse(Status status, Map<String, String[]> acknowledgeMessages, String acknowledgeMessage) {
5067
this.status = status;
68+
this.acknowledgeMessages = acknowledgeMessages;
69+
this.acknowledgeMessage = acknowledgeMessage;
5170
}
5271

5372
public Status getStatus() {
@@ -57,10 +76,58 @@ public Status getStatus() {
5776
@Override
5877
public void readFrom(StreamInput in) throws IOException {
5978
status = in.readEnum(Status.class);
79+
// TODO: Change to 6.3 after backport
80+
if (in.getVersion().onOrAfter(Version.V_7_0_0_alpha1)) {
81+
acknowledgeMessage = in.readOptionalString();
82+
int size = in.readVInt();
83+
Map<String, String[]> acknowledgeMessages = new HashMap<>(size);
84+
for (int i = 0; i < size; i++) {
85+
String feature = in.readString();
86+
int nMessages = in.readVInt();
87+
String[] messages = new String[nMessages];
88+
for (int j = 0; j < nMessages; j++) {
89+
messages[j] = in.readString();
90+
}
91+
acknowledgeMessages.put(feature, messages);
92+
}
93+
this.acknowledgeMessages = acknowledgeMessages;
94+
} else {
95+
this.acknowledgeMessages = Collections.emptyMap();
96+
}
6097
}
6198

6299
@Override
63100
public void writeTo(StreamOutput out) throws IOException {
64-
out.writeEnum(status);
101+
// TODO: Change to 6.3 after backport
102+
Version version = Version.V_7_0_0_alpha1;
103+
if (out.getVersion().onOrAfter(version)) {
104+
out.writeEnum(status);
105+
out.writeOptionalString(acknowledgeMessage);
106+
out.writeVInt(acknowledgeMessages.size());
107+
for (Map.Entry<String, String[]> entry : acknowledgeMessages.entrySet()) {
108+
out.writeString(entry.getKey());
109+
out.writeVInt(entry.getValue().length);
110+
for (String message : entry.getValue()) {
111+
out.writeString(message);
112+
}
113+
}
114+
} else {
115+
if (status == Status.UPGRADED_TO_TRIAL) {
116+
out.writeEnum(Pre63Status.UPGRADED_TO_TRIAL);
117+
} else if (status == Status.TRIAL_ALREADY_ACTIVATED) {
118+
out.writeEnum(Pre63Status.TRIAL_ALREADY_ACTIVATED);
119+
} else {
120+
throw new IllegalArgumentException("Starting trial on node with version [" + Version.CURRENT + "] requires " +
121+
"acknowledgement parameter.");
122+
}
123+
}
124+
}
125+
126+
Map<String, String[]> getAcknowledgementMessages() {
127+
return acknowledgeMessages;
128+
}
129+
130+
String getAcknowledgementMessage() {
131+
return acknowledgeMessage;
65132
}
66133
}

x-pack/plugin/core/src/main/java/org/elasticsearch/license/RestGetTrialStatus.java

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -29,7 +29,7 @@ public class RestGetTrialStatus extends XPackRestHandler {
2929

3030
@Override
3131
protected RestChannelConsumer doPrepareRequest(RestRequest request, XPackClient client) throws IOException {
32-
return channel -> client.licensing().prepareGetUpgradeToTrial().execute(
32+
return channel -> client.licensing().prepareGetStartTrial().execute(
3333
new RestBuilderListener<GetTrialStatusResponse>(channel) {
3434
@Override
3535
public RestResponse buildResponse(GetTrialStatusResponse response, XContentBuilder builder) throws Exception {

x-pack/plugin/core/src/main/java/org/elasticsearch/license/RestPostStartTrialLicense.java

Lines changed: 22 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,7 @@
1616
import org.elasticsearch.xpack.core.rest.XPackRestHandler;
1717

1818
import java.io.IOException;
19+
import java.util.Map;
1920

2021
import static org.elasticsearch.rest.RestRequest.Method.POST;
2122

@@ -30,23 +31,36 @@ public class RestPostStartTrialLicense extends XPackRestHandler {
3031
protected RestChannelConsumer doPrepareRequest(RestRequest request, XPackClient client) throws IOException {
3132
PostStartTrialRequest startTrialRequest = new PostStartTrialRequest();
3233
startTrialRequest.setType(request.param("type", "trial"));
34+
startTrialRequest.acknowledge(request.paramAsBoolean("acknowledge", false));
3335
return channel -> client.licensing().postStartTrial(startTrialRequest,
3436
new RestBuilderListener<PostStartTrialResponse>(channel) {
3537
@Override
3638
public RestResponse buildResponse(PostStartTrialResponse response, XContentBuilder builder) throws Exception {
3739
PostStartTrialResponse.Status status = response.getStatus();
40+
builder.startObject();
41+
builder.field("acknowledged", startTrialRequest.isAcknowledged());
3842
if (status.isTrialStarted()) {
39-
builder.startObject()
40-
.field("trial_was_started", true)
41-
.field("type", startTrialRequest.getType())
42-
.endObject();
43+
builder.field("trial_was_started", true);
44+
builder.field("type", startTrialRequest.getType());
4345
} else {
44-
builder.startObject()
45-
.field("trial_was_started", false)
46-
.field("error_message", status.getErrorMessage())
47-
.endObject();
46+
builder.field("trial_was_started", false);
47+
builder.field("error_message", status.getErrorMessage());
48+
}
4849

50+
Map<String, String[]> acknowledgementMessages = response.getAcknowledgementMessages();
51+
if (acknowledgementMessages.isEmpty() == false) {
52+
builder.startObject("acknowledge");
53+
builder.field("message", response.getAcknowledgementMessage());
54+
for (Map.Entry<String, String[]> entry : acknowledgementMessages.entrySet()) {
55+
builder.startArray(entry.getKey());
56+
for (String message : entry.getValue()) {
57+
builder.value(message);
58+
}
59+
builder.endArray();
60+
}
61+
builder.endObject();
4962
}
63+
builder.endObject();
5064
return new BytesRestResponse(status.getRestStatus(), builder);
5165
}
5266
});

x-pack/plugin/core/src/main/java/org/elasticsearch/license/StartTrialClusterTask.java

Lines changed: 20 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -15,10 +15,23 @@
1515
import org.elasticsearch.common.Nullable;
1616

1717
import java.time.Clock;
18+
import java.util.Collections;
19+
import java.util.HashMap;
20+
import java.util.Map;
1821
import java.util.UUID;
22+
import java.util.concurrent.atomic.AtomicBoolean;
23+
import java.util.concurrent.atomic.AtomicReference;
1924

2025
public class StartTrialClusterTask extends ClusterStateUpdateTask {
2126

27+
private static final String ACKNOWLEDGEMENT_HEADER = "This API initiates a free 30-day trial for all platinum features. " +
28+
"By starting this trial, you agree that it is subject to the terms and conditions at" +
29+
" https://www.elastic.co/legal/trial_license/. To begin your free trial, call /start_trial again and specify " +
30+
"the \"acknowledge=true\" parameter.";
31+
32+
private static final Map<String, String[]> ACK_MESSAGES = Collections.singletonMap("security",
33+
new String[] {"With a trial license, X-Pack security features are available, but are not enabled by default."});
34+
2235
private final Logger logger;
2336
private final String clusterName;
2437
private final PostStartTrialRequest request;
@@ -39,7 +52,10 @@ public void clusterStateProcessed(String source, ClusterState oldState, ClusterS
3952
LicensesMetaData oldLicensesMetaData = oldState.metaData().custom(LicensesMetaData.TYPE);
4053
logger.debug("started self generated trial license: {}", oldLicensesMetaData);
4154

42-
if (oldLicensesMetaData == null || oldLicensesMetaData.isEligibleForTrial()) {
55+
if (request.isAcknowledged() == false) {
56+
listener.onResponse(new PostStartTrialResponse(PostStartTrialResponse.Status.NEED_ACKNOWLEDGEMENT,
57+
ACK_MESSAGES, ACKNOWLEDGEMENT_HEADER));
58+
} else if (oldLicensesMetaData == null || oldLicensesMetaData.isEligibleForTrial()) {
4359
listener.onResponse(new PostStartTrialResponse(PostStartTrialResponse.Status.UPGRADED_TO_TRIAL));
4460
} else {
4561
listener.onResponse(new PostStartTrialResponse(PostStartTrialResponse.Status.TRIAL_ALREADY_ACTIVATED));
@@ -50,7 +66,9 @@ public void clusterStateProcessed(String source, ClusterState oldState, ClusterS
5066
public ClusterState execute(ClusterState currentState) throws Exception {
5167
LicensesMetaData currentLicensesMetaData = currentState.metaData().custom(LicensesMetaData.TYPE);
5268

53-
if (currentLicensesMetaData == null || currentLicensesMetaData.isEligibleForTrial()) {
69+
if (request.isAcknowledged() == false) {
70+
return currentState;
71+
} else if (currentLicensesMetaData == null || currentLicensesMetaData.isEligibleForTrial()) {
5472
long issueDate = clock.millis();
5573
MetaData.Builder mdBuilder = MetaData.builder(currentState.metaData());
5674
long expiryDate = issueDate + LicenseService.NON_BASIC_SELF_GENERATED_LICENSE_DURATION.getMillis();

0 commit comments

Comments
 (0)