diff --git a/docs/en/rest-api/security/authenticate.asciidoc b/docs/en/rest-api/security/authenticate.asciidoc index 2f0c24a979b..ba837ddfd2c 100644 --- a/docs/en/rest-api/security/authenticate.asciidoc +++ b/docs/en/rest-api/security/authenticate.asciidoc @@ -4,9 +4,24 @@ The Authenticate API enables you to submit a request with a basic auth header to authenticate a user and retrieve information about the authenticated user. -Returns a 401 status code if the user cannot be authenticated. -To authenticate a user, submit a GET request to the `_xpack/security/_authenticate` endpoint: + +==== Request + +`GET _xpack/security/_authenticate` + + +==== Description + +A successful call returns a JSON structure that shows what roles are assigned +to the user as well as any assigned metadata. + +If the user cannot be authenticated, this API returns a 401 status code. + +==== Examples + +To authenticate a user, submit a GET request to the +`_xpack/security/_authenticate` endpoint: [source,js] -------------------------------------------------- @@ -14,8 +29,7 @@ GET _xpack/security/_authenticate -------------------------------------------------- // CONSOLE -A successful call returns a JSON structure that shows what roles are assigned -to the user as well as any assigned metadata. +The following example output provides information about the "rdeniro" user: [source,js] -------------------------------------------------- diff --git a/docs/en/rest-api/security/change-password.asciidoc b/docs/en/rest-api/security/change-password.asciidoc index 87fc27bf916..7dee98480e7 100644 --- a/docs/en/rest-api/security/change-password.asciidoc +++ b/docs/en/rest-api/security/change-password.asciidoc @@ -3,11 +3,37 @@ === Change Password API The Change Password API enables you to submit a request to change the password -of a user. Every user can change their own password and users with the -`manage_security` privilege can change passwords of other users. +of a user. -To change the password of the logged in user, submit a POST request to the -`_xpack/security/user/_password` endpoint: +==== Request + +`POST _xpack/security/user/_password` + + +`POST _xpack/security/user//_password` + + +==== Path Parameters + +`username`:: + (string) The user whose password you want to change. If you do not specify + this parameter, the password is changed for the current user. + + +==== Request Body + +`password` (required):: + (string) The new password value. + + +==== Authorization + +Every user can change their own password. Users with the `manage_security` +privilege can change passwords of other users. + + +==== Examples + +The following example updates the password for the `elastic` user: [source,js] -------------------------------------------------- diff --git a/docs/en/rest-api/security/clear-cache.asciidoc b/docs/en/rest-api/security/clear-cache.asciidoc index c4e06eeff23..882ae362bdb 100644 --- a/docs/en/rest-api/security/clear-cache.asciidoc +++ b/docs/en/rest-api/security/clear-cache.asciidoc @@ -5,6 +5,31 @@ The Clear Cache API evicts users from the user cache. You can completely clear the cache or evict specific users. +==== Request + +`POST _xpack/security/realm//_clear_cache` + + +`POST _xpack/security/realm//_clear_cache?usernames=` + + +==== Description + +User credentials are cached in memory on each node to avoid connecting to a +remote authentication service or hitting the disk for every incoming request. +There are realm settings that you can use to configure the user cache. For more +information, see {xpack-ref}/controlling-user-cache.html[Controlling the User Cache]. + +==== Path Parameters + +`realms` (required):: + (list) A comma-separated list of the realms to clear. + +`usernames`:: + (list) A comma-separated list of the users to clear from the cache. If you + do not specify this parameter, the API evicts all users from the user cache. + +==== Examples + For example, to evict all users cached by the `file` realm: [source,js] @@ -29,6 +54,3 @@ list: POST _xpack/security/realm/default_file,ldap1/_clear_cache ------------------------------------------------------------ // CONSOLE - -For more information, see -{xpack-ref}/controlling-user-cache.html[Controlling the User Cache]. diff --git a/docs/en/rest-api/security/privileges.asciidoc b/docs/en/rest-api/security/privileges.asciidoc index e1b42bc9536..4ec192d633b 100644 --- a/docs/en/rest-api/security/privileges.asciidoc +++ b/docs/en/rest-api/security/privileges.asciidoc @@ -7,13 +7,41 @@ The `has_privileges` API allows you to determine whether the logged in user has a specified list of privileges. +==== Request + +`GET _xpack/security/user/_has_privileges` + + +==== Description + +For a list of the privileges that you can specify in this API, +see {xpack-ref}/security-privileges.html[Security Privileges]. + +A successful call returns a JSON structure that shows whether each specified +privilege is assigned to the user. + + +==== Request Body + +`cluster`:: (list) A list of the cluster privileges that you want to check. + +`index`:: +`names`::: (list) A list of indices. +`privileges`::: (list) A list of the privileges that you want to check for the +specified indices. + +==== Authorization + All users can use this API, but only to determine their own privileges. To check the privileges of other users, you must use the run as feature. For -more information, see +more information, see {xpack-ref}/run-as-privilege.html[Submitting Requests on Behalf of Other Users]. -To check you privileges, submit a GET request to the -`_xpack/security/user/_has_privileges` endpoint: + +==== Examples + +The following example checks whether the current user has a specific set of +cluster and indices privileges: [source,js] -------------------------------------------------- @@ -34,8 +62,7 @@ GET _xpack/security/user/_has_privileges -------------------------------------------------- // CONSOLE -A successful call returns a JSON structure that shows whether each specified -privilege is assigned to the user +The following example output indicates which privileges the "rdeniro" user has: [source,js] -------------------------------------------------- diff --git a/docs/en/rest-api/security/role-mapping.asciidoc b/docs/en/rest-api/security/role-mapping.asciidoc index 81ac7e4f1e4..d3dcb94e259 100644 --- a/docs/en/rest-api/security/role-mapping.asciidoc +++ b/docs/en/rest-api/security/role-mapping.asciidoc @@ -2,15 +2,69 @@ [[security-api-role-mapping]] === Role Mapping APIs -The Role Mapping API enables you to add, remove, and retrieve role-mappings. -To use this API, you must have at least the `manage_security` cluster privilege. +The Role Mapping API enables you to add, remove, and retrieve role mappings. -NOTE: The API requires that each role-mapping have a distinct name. The name is +==== Request + +`GET /_xpack/security/role_mapping` + + +`GET /_xpack/security/role_mapping/` + + +`DELETE /_xpack/security/role_mapping/` + + +`POST /_xpack/security/role_mapping/` + + +`PUT /_xpack/security/role_mapping/` + +==== Description + +NOTE: The API requires that each role mapping have a distinct name. The name is used solely as an identifier to facilitate interaction via the API, and does not affect the behavior of the mapping in any way. +For more information, see +{xpack-ref}/mapping-roles.html[Mapping Users and Groups to Roles]. + +==== Path Parameters + +`name`:: + (string) The distinct name that identifies the role mapping. If you do not + specify this parameter, the Get Role Mappings API returns information about all + role mappings. + + +==== Request Body + +The following parameters can be specified in the body of a PUT or POST request +and pertain to adding a role mapping: + +`enabled` (required):: +(boolean) Mappings that have `enabled` set to `false` are ignored when role +mapping is performed. + +`metadata`:: +(object) Additional metadata that helps define which roles are assigned to each +user. Within the `metadata` object, keys beginning with `_` are reserved for +system usage. + +`roles` (required):: +(list) A list of roles that are granted to the users that match the role-mapping +rules. + +`rules` (required):: +(object) The rules that determine which users should be matched by the mapping. +A rule is a logical condition that is expressed by using a JSON DSL. + + +==== Authorization + +To use this API, you must have at least the `manage_security` cluster privilege. + + +==== Examples + [[security-api-put-role-mapping]] -To add a role-mapping, submit a PUT or POST request to the `/_xpack/security/role_mapping/` +To add a role mapping, submit a PUT or POST request to the `/_xpack/security/role_mapping/` endpoint: [source,js] @@ -28,14 +82,10 @@ POST /_xpack/security/role_mapping/administrators } -------------------------------------------------- // CONSOLE -<1> Mappings that have `enabled` set to `false` will be ignored when role-mapping +<1> Mappings that have `enabled` set to `false` will be ignored when role mapping is performed. <2> Metadata is optional -The `roles`, `enabled`, and `rules` fields are required at the top-level. -Within the `metadata` object, keys beginning with `_` are reserved for system -usage. - A successful call returns a JSON structure that shows whether the mapping has been created or updated. @@ -51,7 +101,7 @@ been created or updated. <1> When an existing mapping is updated, `created` is set to false. [[security-api-get-role-mapping]] -To retrieve a role-mapping, issue a GET request to the +To retrieve a role mapping, issue a GET request to the `/_xpack/security/role_mapping/` endpoint: [source,js] @@ -61,7 +111,7 @@ GET /_xpack/security/role_mapping/administrators // CONSOLE // TEST[continued] -A successful call an object, where the keys are the +A successful call retrieves an object, where the keys are the names of the request mappings, and the values are the JSON representation of those mappings. If there is no mapping with the requested name, the @@ -107,7 +157,7 @@ GET /_xpack/security/role_mapping // TEST[continued] [[security-api-delete-role-mapping]] -To delete a role-mapping, submit a DELETE request to the +To delete a role mapping, submit a DELETE request to the `/_xpack/security/role_mapping/` endpoint: [source,js] diff --git a/docs/en/rest-api/security/roles.asciidoc b/docs/en/rest-api/security/roles.asciidoc index 219b111d54a..7fd7337983a 100644 --- a/docs/en/rest-api/security/roles.asciidoc +++ b/docs/en/rest-api/security/roles.asciidoc @@ -3,10 +3,72 @@ === Role Management APIs The Roles API enables you to add, remove, and retrieve roles in the `native` -realm. To use this API, you must have at least the `manage_security` cluster +realm. + +==== Request + +`GET /_xpack/security/role` + + +`GET /_xpack/security/role/` + + +`POST /_xpack/security/role//_clear_cache` + + +`POST /_xpack/security/role/` + + +`PUT /_xpack/security/role/` + + +==== Description + +The Roles API is generally the preferred way to manage roles, rather than using +file-based role management. For more information, see +{xpack-ref}/authorization.html[Configuring Role-based Access Control]. + + +==== Path Parameters + +`name`:: + (string) The name of the role. If you do not specify this parameter, the + Get Roles API returns information about all roles. + + +==== Request Body + +The following parameters can be specified in the body of a PUT or POST request +and pertain to adding a role: + +`cluster`:: (list) A list of cluster privileges. These privileges define the +cluster level actions that users with this role are able to execute. + +`indices`:: (list) A list of indices permissions entries. +`field_security`::: (list) The document fields that the owners of the role have +read access to. For more information, see +{xpack-ref}/field-and-document-access-control.html[Setting Up Field and Document Level Security]. +`names` (required)::: (list) A list of indices (or index name patterns) to which the +permissions in this entry apply. +`privileges`(required)::: (list) The index level privileges that the owners of the role +have on the specified indices. +`query`::: A search query that defines the documents the owners of the role have +read access to. A document within the specified indices must match this query in +order for it to be accessible by the owners of the role. + +`metadata`:: (object) Optional meta-data. Within the `metadata` object, keys +that begin with `_` are reserved for system usage. + +`run_as`:: (list) A list of users that the owners of this role can impersonate. +For more information, see +{xpack-ref}/run-as-privilege.html[Submitting Requests on Behalf of Other Users]. + +For more information, see {xpack-ref}/defining-roles.html[Defining Roles]. + + +==== Authorization + +To use this API, you must have at least the `manage_security` cluster privilege. -NOTE: The Roles API is now the preferred way to manage roles. + +==== Examples [[security-api-put-role]] To add a role, submit a PUT or POST request to the `/_xpack/security/role/` @@ -35,15 +97,6 @@ POST /_xpack/security/role/my_admin_role -------------------------------------------------- // CONSOLE -The `name`, `cluster`, and `indices` fields are required at the top-level. -Within the `indices` array, the `names` and `privileges` fields are required. -Within the `metadata` object, keys beginning with `_` are reserved for system -usage. - -The `field_security` and `query` fields are both optional. They are used to -implement field and document level security. For more information, see -{xpack-ref}/field-and-document-access-control.html[Setting Up Field and Document Level Security]. - A successful call returns a JSON structure that shows whether the role has been created or updated. diff --git a/docs/en/rest-api/security/tokens.asciidoc b/docs/en/rest-api/security/tokens.asciidoc index af8386891aa..571cc3fc623 100644 --- a/docs/en/rest-api/security/tokens.asciidoc +++ b/docs/en/rest-api/security/tokens.asciidoc @@ -3,10 +3,54 @@ === Token Management APIs The `token` API enables you to create and invalidate bearer tokens for access -without requiring basic authentication. The get token API takes the same -parameters as a typical OAuth 2.0 token API except for the use of a JSON -request body. +without requiring basic authentication. +==== Request + +`POST /_xpack/security/oauth2/token` + + +`DELETE /_xpack/security/oauth2/token` + +==== Description + +The Get Token API takes the same parameters as a typical OAuth 2.0 token API +except for the use of a JSON request body. + +A successful Get Token API call returns a JSON structure that contains the access +token, the amount of time (seconds) that the token expires in, the type, and the +scope if available. + +The tokens returned by the Get Token API have a finite period of time for which +they are valid and after that time period, they can no longer be used. However, +if you want to invalidate a token immediately, you can do so by using the Delete +Token API. + + +==== Request Body + +The following parameters can be specified in the body of a POST request and +pertain to creating a token: + +`grant_type`:: +(string) The type of grant. Currently only the `password` grant type is supported. + +`password` (required):: +(string) The user's password. + +`scope`:: +(string) The scope of the token. Currently tokens are only issued for a scope of +`FULL` regardless of the value sent with the request. + +`username` (required):: +(string) The username that identifies the user. + +The following parameters can be specified in the body of a DELETE request and +pertain to deleting a token: + +`token`:: +(string) An access token. + +==== Examples [[security-api-get-token]] To obtain a token, submit a POST request to the `/_xpack/security/oauth2/token` endpoint. @@ -22,22 +66,8 @@ POST /_xpack/security/oauth2/token -------------------------------------------------- // CONSOLE -.Token Request Fields -[cols="4,^2,10"] -|======================= -| Name | Required | Description -| `username` | yes | The username that identifies the user. -| `password` | yes | The user's password. -| `grant_type`| yes | The type of grant. Currently only the `password` - grant type is supported. -| `scope` | no | The scope of the token. Currently tokens are only - issued for a scope of `FULL` regardless of the value - sent with the request. -|======================= - -A successful call returns a JSON structure that contains the access token, the -amount of time (seconds) that the token expires in, the type, and the scope if -available. +The following example output contains the access token, the amount of time (in +seconds) that the token expires in, and the type: [source,js] -------------------------------------------------- @@ -49,9 +79,6 @@ available. -------------------------------------------------- // TESTRESPONSE[s/dGhpcyBpcyBub3QgYSByZWFsIHRva2VuIGJ1dCBpdCBpcyBvbmx5IHRlc3QgZGF0YS4gZG8gbm90IHRyeSB0byByZWFkIHRva2VuIQ==/$body.access_token/] -A successful call returns a JSON structure that shows whether the user has been -created or updated. - The token returned by this API can be used by sending a request with a `Authorization` header with a value having the prefix `Bearer ` followed by the value of the `access_token`. @@ -62,10 +89,8 @@ curl -H "Authorization: Bearer dGhpcyBpcyBub3QgYSByZWFsIHRva2VuIGJ1dCBpdCBpcyBvb -------------------------------------------------- [[security-api-invalidate-token]] -The tokens returned from this API have a finite period of time for which they -are valid and after that time period, they can no longer be used. However, if -a token must be invalidated immediately, you can do so by submitting a DELETE -request to `/_xpack/security/oauth2/token`. +If a token must be invalidated immediately, you can do so by submitting a DELETE +request to `/_xpack/security/oauth2/token`. For example: [source,js] -------------------------------------------------- diff --git a/docs/en/rest-api/security/users.asciidoc b/docs/en/rest-api/security/users.asciidoc index 80d08fbe9df..8aa2e1dc28e 100644 --- a/docs/en/rest-api/security/users.asciidoc +++ b/docs/en/rest-api/security/users.asciidoc @@ -4,18 +4,78 @@ The `user` API enables you to create, read, update, and delete users from the `native` realm. These users are commonly referred to as *native users*. + + +==== Request + +`GET /_xpack/security/user` + + +`GET /_xpack/security/user/` + + +`DELETE /_xpack/security/user/` + + +`POST /_xpack/security/user/` + + +`PUT /_xpack/security/user/` + + +`PUT /_xpack/security/user//_disable` + + +`PUT /_xpack/security/user//_enable` + + +`PUT /_xpack/security/user//_password` + + +==== Description + +You can use the PUT user API to create or update users. When updating a user, +you can update everything but its `username` and `password`. To change a user's +password, use the <>. + +[[username-validation]] +NOTE: Usernames must be at least 1 and no more than 1024 characters. They can +contain alphanumeric characters (`a-z`, `A-Z`, `0-9`), spaces, punctuation, and +printable symbols in the https://en.wikipedia.org/wiki/Basic_Latin_(Unicode_block)[Basic Latin (ASCII) block]. +Leading or trailing whitespace is not allowed. + +==== Path Parameters + +`username`:: + (string) An identifier for the user. If you omit this parameter from a Get + User API request, it retrieves information about all users. + + +==== Request Body + +The following parameters can be specified in the body of a POST or PUT request +and pertain to creating a user: + +`email`:: +(string) The email of the user. + +`full_name`:: +(string) The full name of the user. + +`metadata`:: +(object) Arbitrary metadata that you want to associate with the user. + +`password` (required):: +(string) The user's password. Passwords must be at least 6 characters long. + +`roles` (required):: +(list) A set of roles the user has. The roles determine the user's access +permissions. + +==== Authorization + To use this API, you must have at least the `manage_security` cluster privilege. + +==== Examples + [[security-api-put-user]] To add a user, submit a PUT or POST request to the `/_xpack/security/user/` endpoint. -[[username-validation]] -NOTE: Usernames must be at least 1 and no more than 1024 characters. They can - contain alphanumeric characters (`a-z`, `A-Z`, `0-9`), spaces, - punctuation, and printable symbols in the https://en.wikipedia.org/wiki/Basic_Latin_(Unicode_block)[Basic Latin (ASCII) block]. - Leading or trailing whitespace is not allowed. - [source,js] -------------------------------------------------- POST /_xpack/security/user/jacknich @@ -31,20 +91,6 @@ POST /_xpack/security/user/jacknich -------------------------------------------------- // CONSOLE -.User Fields -[cols="4,^2,10"] -|======================= -| Name | Required | Description -| `password` | yes | The user's password. Passwords must be at least 6 - characters long. -| `roles` | yes | A set of roles the user has. The roles determine - the user's access permissions -| `full_name` | no | The full name of the user -| `email` | no | The email of the user -| `metadata` | no | Arbitrary metadata that you want to associate with - the user. -|======================= - A successful call returns a JSON structure that shows whether the user has been created or updated. @@ -59,11 +105,7 @@ created or updated. // TESTRESPONSE <1> When an existing user is updated, `created` is set to false. -NOTE: You also use the PUT user API to update users. When updating a user, you - can update everything but its `username` and `password`. To change a user's - password, use the <>. - -Once you add a user through the Users API, requests from that user can be +After you add a user through the Users API, requests from that user can be authenticated. [source,shell] diff --git a/docs/en/security/authorization.asciidoc b/docs/en/security/authorization.asciidoc index 0fd941414f1..52f3b55799d 100644 --- a/docs/en/security/authorization.asciidoc +++ b/docs/en/security/authorization.asciidoc @@ -85,6 +85,9 @@ change between releases. Grants the minimum privileges required for any user of {kib}. This role grants access to the {kib} indices and grants monitoring privileges for the cluster. +[[built-in-roles-logstash-admin]] `logstash_admin` :: +Grants access to the `.logstash*` indices for managing configurations. + [[built-in-roles-logstash-system]] `logstash_system` :: Grants access necessary for the Logstash system user to send system-level data (such as monitoring) to {es}. For more information, see diff --git a/plugin/src/main/java/org/elasticsearch/license/GetTrialStatusAction.java b/plugin/src/main/java/org/elasticsearch/license/GetTrialStatusAction.java new file mode 100644 index 00000000000..2f690a35fc5 --- /dev/null +++ b/plugin/src/main/java/org/elasticsearch/license/GetTrialStatusAction.java @@ -0,0 +1,29 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License; + * you may not use this file except in compliance with the Elastic License. + */ +package org.elasticsearch.license; + +import org.elasticsearch.action.Action; +import org.elasticsearch.client.ElasticsearchClient; + +public class GetTrialStatusAction extends Action { + + public static final GetTrialStatusAction INSTANCE = new GetTrialStatusAction(); + public static final String NAME = "cluster:admin/xpack/license/trial_status"; + + private GetTrialStatusAction() { + super(NAME); + } + + @Override + public GetTrialStatusRequestBuilder newRequestBuilder(ElasticsearchClient client) { + return new GetTrialStatusRequestBuilder(client, this); + } + + @Override + public GetTrialStatusResponse newResponse() { + return new GetTrialStatusResponse(); + } +} diff --git a/plugin/src/main/java/org/elasticsearch/license/GetTrialStatusRequest.java b/plugin/src/main/java/org/elasticsearch/license/GetTrialStatusRequest.java new file mode 100644 index 00000000000..a94ae697d78 --- /dev/null +++ b/plugin/src/main/java/org/elasticsearch/license/GetTrialStatusRequest.java @@ -0,0 +1,27 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License; + * you may not use this file except in compliance with the Elastic License. + */ +package org.elasticsearch.license; + +import org.elasticsearch.action.ActionRequestValidationException; +import org.elasticsearch.action.support.master.MasterNodeReadRequest; +import org.elasticsearch.common.io.stream.StreamInput; + +import java.io.IOException; + +public class GetTrialStatusRequest extends MasterNodeReadRequest { + + public GetTrialStatusRequest() { + } + + public GetTrialStatusRequest(StreamInput in) throws IOException { + super(in); + } + + @Override + public ActionRequestValidationException validate() { + return null; + } +} diff --git a/plugin/src/main/java/org/elasticsearch/license/GetTrialStatusRequestBuilder.java b/plugin/src/main/java/org/elasticsearch/license/GetTrialStatusRequestBuilder.java new file mode 100644 index 00000000000..c9786418b63 --- /dev/null +++ b/plugin/src/main/java/org/elasticsearch/license/GetTrialStatusRequestBuilder.java @@ -0,0 +1,17 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License; + * you may not use this file except in compliance with the Elastic License. + */ +package org.elasticsearch.license; + +import org.elasticsearch.action.ActionRequestBuilder; +import org.elasticsearch.client.ElasticsearchClient; + +class GetTrialStatusRequestBuilder extends ActionRequestBuilder { + + GetTrialStatusRequestBuilder(ElasticsearchClient client, GetTrialStatusAction action) { + super(client, action, new GetTrialStatusRequest()); + } +} diff --git a/plugin/src/main/java/org/elasticsearch/license/GetTrialStatusResponse.java b/plugin/src/main/java/org/elasticsearch/license/GetTrialStatusResponse.java new file mode 100644 index 00000000000..6712e68efae --- /dev/null +++ b/plugin/src/main/java/org/elasticsearch/license/GetTrialStatusResponse.java @@ -0,0 +1,38 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License; + * you may not use this file except in compliance with the Elastic License. + */ +package org.elasticsearch.license; + +import org.elasticsearch.action.ActionResponse; +import org.elasticsearch.common.io.stream.StreamInput; +import org.elasticsearch.common.io.stream.StreamOutput; + +import java.io.IOException; + +class GetTrialStatusResponse extends ActionResponse { + + private boolean eligibleToStartTrial; + + GetTrialStatusResponse() { + } + + GetTrialStatusResponse(boolean eligibleToStartTrial) { + this.eligibleToStartTrial = eligibleToStartTrial; + } + + boolean isEligibleToStartTrial() { + return eligibleToStartTrial; + } + + @Override + public void readFrom(StreamInput in) throws IOException { + eligibleToStartTrial = in.readBoolean(); + } + + @Override + public void writeTo(StreamOutput out) throws IOException { + out.writeBoolean(eligibleToStartTrial); + } +} diff --git a/plugin/src/main/java/org/elasticsearch/license/LicenseService.java b/plugin/src/main/java/org/elasticsearch/license/LicenseService.java index 227293886a9..811a634e76d 100644 --- a/plugin/src/main/java/org/elasticsearch/license/LicenseService.java +++ b/plugin/src/main/java/org/elasticsearch/license/LicenseService.java @@ -8,6 +8,7 @@ package org.elasticsearch.license; import org.apache.logging.log4j.message.ParameterizedMessage; import org.apache.logging.log4j.util.Supplier; import org.elasticsearch.ElasticsearchException; +import org.elasticsearch.Version; import org.elasticsearch.action.ActionListener; import org.elasticsearch.cluster.AckedClusterStateUpdateTask; import org.elasticsearch.cluster.ClusterChangedEvent; @@ -227,8 +228,14 @@ public class LicenseService extends AbstractLifecycleComponent implements Cluste @Override public ClusterState execute(ClusterState currentState) throws Exception { - MetaData.Builder mdBuilder = MetaData.builder(currentState.metaData()); - mdBuilder.putCustom(LicensesMetaData.TYPE, new LicensesMetaData(newLicense)); + MetaData currentMetadata = currentState.metaData(); + LicensesMetaData licensesMetaData = currentMetadata.custom(LicensesMetaData.TYPE); + Version trialVersion = null; + if (licensesMetaData != null) { + trialVersion = licensesMetaData.getMostRecentTrialVersion(); + } + MetaData.Builder mdBuilder = MetaData.builder(currentMetadata); + mdBuilder.putCustom(LicensesMetaData.TYPE, new LicensesMetaData(newLicense, trialVersion)); return ClusterState.builder(currentState).metaData(mdBuilder).build(); } }); @@ -237,7 +244,7 @@ public class LicenseService extends AbstractLifecycleComponent implements Cluste } - static TimeValue days(int days) { + private static TimeValue days(int days) { return TimeValue.timeValueHours(days * 24); } @@ -273,7 +280,9 @@ public class LicenseService extends AbstractLifecycleComponent implements Cluste final LicensesMetaData currentLicenses = metaData.custom(LicensesMetaData.TYPE); if (currentLicenses.getLicense() != LicensesMetaData.LICENSE_TOMBSTONE) { MetaData.Builder mdBuilder = MetaData.builder(currentState.metaData()); - mdBuilder.putCustom(LicensesMetaData.TYPE, new LicensesMetaData(LicensesMetaData.LICENSE_TOMBSTONE)); + LicensesMetaData newMetadata = new LicensesMetaData(LicensesMetaData.LICENSE_TOMBSTONE, + currentLicenses.getMostRecentTrialVersion()); + mdBuilder.putCustom(LicensesMetaData.TYPE, newMetadata); return ClusterState.builder(currentState).metaData(mdBuilder).build(); } else { return currentState; @@ -287,6 +296,40 @@ public class LicenseService extends AbstractLifecycleComponent implements Cluste return license == LicensesMetaData.LICENSE_TOMBSTONE ? null : license; } + void upgradeSelfGeneratedLicense(final ActionListener listener) { + clusterService.submitStateUpdateTask("upgrade self generated license", + new ClusterStateUpdateTask() { + @Override + public void clusterStateProcessed(String source, ClusterState oldState, ClusterState newState) { + LicensesMetaData licensesMetaData = oldState.metaData().custom(LicensesMetaData.TYPE); + logger.debug("upgraded self generated license: {}", licensesMetaData); + + if (licensesMetaData == null || licensesMetaData.isEligibleForTrial()) { + listener.onResponse(new PostStartTrialResponse(PostStartTrialResponse.STATUS.UPGRADED_TO_TRIAL)); + } else { + listener.onResponse(new PostStartTrialResponse(PostStartTrialResponse.STATUS.TRIAL_ALREADY_ACTIVATED)); + } + } + + @Override + public ClusterState execute(ClusterState currentState) throws Exception { + LicensesMetaData licensesMetaData = currentState.metaData().custom(LicensesMetaData.TYPE); + + if (licensesMetaData == null || licensesMetaData.isEligibleForTrial()) { + return updateWithLicense(currentState, "trial"); + } else { + return currentState; + } + } + + @Override + public void onFailure(String source, @Nullable Exception e) { + logger.error(new ParameterizedMessage("unexpected failure during [{}]", source), e); + listener.onFailure(e); + } + }); + } + /** * Master-only operation to generate a one-time global trial license. * The trial license is only generated and stored if the current cluster state metaData @@ -299,7 +342,7 @@ public class LicenseService extends AbstractLifecycleComponent implements Cluste public void clusterStateProcessed(String source, ClusterState oldState, ClusterState newState) { LicensesMetaData licensesMetaData = newState.metaData().custom(LicensesMetaData.TYPE); if (logger.isDebugEnabled()) { - logger.debug("registered trial license: {}", licensesMetaData); + logger.debug("registered self generated license: {}", licensesMetaData); } } @@ -307,27 +350,18 @@ public class LicenseService extends AbstractLifecycleComponent implements Cluste public ClusterState execute(ClusterState currentState) throws Exception { final MetaData metaData = currentState.metaData(); final LicensesMetaData currentLicensesMetaData = metaData.custom(LicensesMetaData.TYPE); - MetaData.Builder mdBuilder = MetaData.builder(currentState.metaData()); // do not generate a trial license if any license is present if (currentLicensesMetaData == null) { - long issueDate = clock.millis(); String type = SELF_GENERATED_LICENSE_TYPE.get(settings); if (validSelfGeneratedType(type) == false) { throw new IllegalArgumentException("Illegal self generated license type [" + type + "]. Must be trial or basic."); } - License.Builder specBuilder = License.builder() - .uid(UUID.randomUUID().toString()) - .issuedTo(clusterService.getClusterName().value()) - .maxNodes(selfGeneratedLicenseMaxNodes) - .issueDate(issueDate) - .type(type) - .expiryDate(issueDate + SELF_GENERATED_LICENSE_DURATION.getMillis()); - License selfGeneratedLicense = SelfGeneratedLicense.create(specBuilder); - mdBuilder.putCustom(LicensesMetaData.TYPE, new LicensesMetaData(selfGeneratedLicense)); - return ClusterState.builder(currentState).metaData(mdBuilder).build(); + + return updateWithLicense(currentState, type); + } else { + return currentState; } - return currentState; } @Override @@ -338,6 +372,27 @@ public class LicenseService extends AbstractLifecycleComponent implements Cluste }); } + private ClusterState updateWithLicense(ClusterState currentState, String type) { + long issueDate = clock.millis(); + MetaData.Builder mdBuilder = MetaData.builder(currentState.metaData()); + License.Builder specBuilder = License.builder() + .uid(UUID.randomUUID().toString()) + .issuedTo(clusterService.getClusterName().value()) + .maxNodes(selfGeneratedLicenseMaxNodes) + .issueDate(issueDate) + .type(type) + .expiryDate(issueDate + SELF_GENERATED_LICENSE_DURATION.getMillis()); + License selfGeneratedLicense = SelfGeneratedLicense.create(specBuilder); + LicensesMetaData licensesMetaData; + if ("trial".equals(type)) { + licensesMetaData = new LicensesMetaData(selfGeneratedLicense, Version.CURRENT); + } else { + licensesMetaData = new LicensesMetaData(selfGeneratedLicense, null); + } + mdBuilder.putCustom(LicensesMetaData.TYPE, licensesMetaData); + return ClusterState.builder(currentState).metaData(mdBuilder).build(); + } + @Override protected void doStart() throws ElasticsearchException { clusterService.addListener(this); @@ -436,7 +491,7 @@ public class LicenseService extends AbstractLifecycleComponent implements Cluste * Additionally schedules license expiry notifications and event callbacks * relative to the current license's expiry */ - void onUpdate(final LicensesMetaData currentLicensesMetaData) { + private void onUpdate(final LicensesMetaData currentLicensesMetaData) { final License license = getLicense(currentLicensesMetaData); // license can be null if the trial license is yet to be auto-generated // in this case, it is a no-op diff --git a/plugin/src/main/java/org/elasticsearch/license/LicensesMetaData.java b/plugin/src/main/java/org/elasticsearch/license/LicensesMetaData.java index fad87f0713b..ff83de1e58a 100644 --- a/plugin/src/main/java/org/elasticsearch/license/LicensesMetaData.java +++ b/plugin/src/main/java/org/elasticsearch/license/LicensesMetaData.java @@ -5,15 +5,17 @@ */ package org.elasticsearch.license; +import org.elasticsearch.Version; import org.elasticsearch.cluster.AbstractNamedDiffable; +import org.elasticsearch.cluster.MergableCustomMetaData; import org.elasticsearch.cluster.NamedDiff; import org.elasticsearch.cluster.metadata.MetaData; +import org.elasticsearch.common.inject.internal.Nullable; import org.elasticsearch.common.io.stream.StreamInput; import org.elasticsearch.common.io.stream.StreamOutput; import org.elasticsearch.common.xcontent.XContentBuilder; import org.elasticsearch.common.xcontent.XContentParser; import org.elasticsearch.license.License.OperationMode; -import org.elasticsearch.cluster.MergableCustomMetaData; import java.io.IOException; import java.util.EnumSet; @@ -48,33 +50,56 @@ class LicensesMetaData extends AbstractNamedDiffable implements private License license; - LicensesMetaData(License license) { + // This field describes the version of x-pack for which this cluster has exercised a trial. If the field + // is null, then no trial has been exercised. We keep the version to leave open the possibility that we + // may eventually allow a cluster to exercise a trial every time they upgrade to a new major version. + @Nullable + private Version trialVersion; + + LicensesMetaData(License license, Version trialVersion) { this.license = license; + this.trialVersion = trialVersion; } public License getLicense() { return license; } + boolean isEligibleForTrial() { + if (trialVersion == null) { + return true; + } + return Version.CURRENT.major > trialVersion.major; + } + + Version getMostRecentTrialVersion() { + return trialVersion; + } + @Override public String toString() { - if (license != null) { - return license.toString(); - } - return ""; + return "LicensesMetaData{" + + "license=" + license + + ", trialVersion=" + trialVersion + + '}'; } @Override public boolean equals(Object o) { if (this == o) return true; if (o == null || getClass() != o.getClass()) return false; + LicensesMetaData that = (LicensesMetaData) o; - return !(license != null ? !license.equals(that.license) : that.license != null); + + if (license != null ? !license.equals(that.license) : that.license != null) return false; + return trialVersion != null ? trialVersion.equals(that.trialVersion) : that.trialVersion == null; } @Override public int hashCode() { - return license != null ? license.hashCode() : 0; + int result = license != null ? license.hashCode() : 0; + result = 31 * result + (trialVersion != null ? trialVersion.hashCode() : 0); + return result; } @Override @@ -89,6 +114,7 @@ class LicensesMetaData extends AbstractNamedDiffable implements public static LicensesMetaData fromXContent(XContentParser parser) throws IOException { License license = LICENSE_TOMBSTONE; + Version trialLicense = null; XContentParser.Token token; while ((token = parser.nextToken()) != XContentParser.Token.END_OBJECT) { if (token == XContentParser.Token.FIELD_NAME) { @@ -101,11 +127,14 @@ class LicensesMetaData extends AbstractNamedDiffable implements } else if (token == XContentParser.Token.VALUE_NULL) { license = LICENSE_TOMBSTONE; } + } else if (fieldName.equals(Fields.TRIAL_LICENSE)) { + parser.nextToken(); + trialLicense = Version.fromString(parser.text()); } } } } - return new LicensesMetaData(license); + return new LicensesMetaData(license, trialLicense); } @Override @@ -117,6 +146,9 @@ class LicensesMetaData extends AbstractNamedDiffable implements license.toInnerXContent(builder, params); builder.endObject(); } + if (trialVersion != null) { + builder.field(Fields.TRIAL_LICENSE, trialVersion.toString()); + } return builder; } @@ -128,6 +160,15 @@ class LicensesMetaData extends AbstractNamedDiffable implements streamOutput.writeBoolean(true); // has a license license.writeTo(streamOutput); } + // TODO Eventually this should be 6.0. But it is 7.0 temporarily for bwc + if (streamOutput.getVersion().onOrAfter(Version.V_7_0_0_alpha1)) { + if (trialVersion == null) { + streamOutput.writeBoolean(false); + } else { + streamOutput.writeBoolean(true); + Version.writeVersion(trialVersion, streamOutput); + } + } } LicensesMetaData(StreamInput streamInput) throws IOException { @@ -136,6 +177,13 @@ class LicensesMetaData extends AbstractNamedDiffable implements } else { license = LICENSE_TOMBSTONE; } + // TODO Eventually this should be 6.0. But it is 7.0 temporarily for bwc + if (streamInput.getVersion().onOrAfter(Version.V_7_0_0_alpha1)) { + boolean hasExercisedTrial = streamInput.readBoolean(); + if (hasExercisedTrial) { + this.trialVersion = Version.readVersion(streamInput); + } + } } public static NamedDiff readDiffFrom(StreamInput streamInput) throws IOException { @@ -155,5 +203,6 @@ class LicensesMetaData extends AbstractNamedDiffable implements private static final class Fields { private static final String LICENSE = "license"; + private static final String TRIAL_LICENSE = "trial_license"; } } diff --git a/plugin/src/main/java/org/elasticsearch/license/Licensing.java b/plugin/src/main/java/org/elasticsearch/license/Licensing.java index 4be3c22c233..cb82bbbe081 100644 --- a/plugin/src/main/java/org/elasticsearch/license/Licensing.java +++ b/plugin/src/main/java/org/elasticsearch/license/Licensing.java @@ -36,7 +36,7 @@ public class Licensing implements ActionPlugin { public static final String NAME = "license"; protected final Settings settings; - protected final boolean isTransportClient; + private final boolean isTransportClient; private final boolean isTribeNode; public List getNamedWriteables() { @@ -53,6 +53,7 @@ public class Licensing implements ActionPlugin { LicensesMetaData::fromXContent)); return entries; } + public Licensing(Settings settings) { this.settings = settings; isTransportClient = transportClientMode(settings); @@ -66,7 +67,9 @@ public class Licensing implements ActionPlugin { } return Arrays.asList(new ActionHandler<>(PutLicenseAction.INSTANCE, TransportPutLicenseAction.class), new ActionHandler<>(GetLicenseAction.INSTANCE, TransportGetLicenseAction.class), - new ActionHandler<>(DeleteLicenseAction.INSTANCE, TransportDeleteLicenseAction.class)); + new ActionHandler<>(DeleteLicenseAction.INSTANCE, TransportDeleteLicenseAction.class), + new ActionHandler<>(PostStartTrialAction.INSTANCE, TransportPostStartTrialAction.class), + new ActionHandler<>(GetTrialStatusAction.INSTANCE, TransportGetTrialStatusAction.class)); } @Override @@ -78,6 +81,8 @@ public class Licensing implements ActionPlugin { if (false == isTribeNode) { handlers.add(new RestPutLicenseAction(settings, restController)); handlers.add(new RestDeleteLicenseAction(settings, restController)); + handlers.add(new RestGetTrialStatus(settings, restController)); + handlers.add(new RestPostStartTrialLicense(settings, restController)); } return handlers; } diff --git a/plugin/src/main/java/org/elasticsearch/license/LicensingClient.java b/plugin/src/main/java/org/elasticsearch/license/LicensingClient.java index 59f9262c940..cfc568bf4c4 100644 --- a/plugin/src/main/java/org/elasticsearch/license/LicensingClient.java +++ b/plugin/src/main/java/org/elasticsearch/license/LicensingClient.java @@ -39,4 +39,16 @@ public class LicensingClient { public void deleteLicense(DeleteLicenseRequest request, ActionListener listener) { client.execute(DeleteLicenseAction.INSTANCE, request, listener); } + + public PostStartTrialRequestBuilder preparePutUpgradeToTrial() { + return new PostStartTrialRequestBuilder(client, PostStartTrialAction.INSTANCE); + } + + public GetTrialStatusRequestBuilder prepareGetUpgradeToTrial() { + return new GetTrialStatusRequestBuilder(client, GetTrialStatusAction.INSTANCE); + } + + public void putUpgradeToTrial(PostStartTrialRequest request, ActionListener listener) { + client.execute(PostStartTrialAction.INSTANCE, request, listener); + } } diff --git a/plugin/src/main/java/org/elasticsearch/license/PostStartTrialAction.java b/plugin/src/main/java/org/elasticsearch/license/PostStartTrialAction.java new file mode 100644 index 00000000000..b0634ef22a9 --- /dev/null +++ b/plugin/src/main/java/org/elasticsearch/license/PostStartTrialAction.java @@ -0,0 +1,29 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License; + * you may not use this file except in compliance with the Elastic License. + */ +package org.elasticsearch.license; + +import org.elasticsearch.action.Action; +import org.elasticsearch.client.ElasticsearchClient; + +public class PostStartTrialAction extends Action { + + public static final PostStartTrialAction INSTANCE = new PostStartTrialAction(); + public static final String NAME = "cluster:admin/xpack/license/start_trial"; + + private PostStartTrialAction() { + super(NAME); + } + + @Override + public PostStartTrialRequestBuilder newRequestBuilder(ElasticsearchClient client) { + return new PostStartTrialRequestBuilder(client, this); + } + + @Override + public PostStartTrialResponse newResponse() { + return new PostStartTrialResponse(); + } +} diff --git a/plugin/src/main/java/org/elasticsearch/license/PostStartTrialRequest.java b/plugin/src/main/java/org/elasticsearch/license/PostStartTrialRequest.java new file mode 100644 index 00000000000..70377d6a2a7 --- /dev/null +++ b/plugin/src/main/java/org/elasticsearch/license/PostStartTrialRequest.java @@ -0,0 +1,17 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License; + * you may not use this file except in compliance with the Elastic License. + */ +package org.elasticsearch.license; + +import org.elasticsearch.action.ActionRequestValidationException; +import org.elasticsearch.action.support.master.MasterNodeRequest; + +public class PostStartTrialRequest extends MasterNodeRequest { + + @Override + public ActionRequestValidationException validate() { + return null; + } +} diff --git a/plugin/src/main/java/org/elasticsearch/license/PostStartTrialRequestBuilder.java b/plugin/src/main/java/org/elasticsearch/license/PostStartTrialRequestBuilder.java new file mode 100644 index 00000000000..af381e13517 --- /dev/null +++ b/plugin/src/main/java/org/elasticsearch/license/PostStartTrialRequestBuilder.java @@ -0,0 +1,17 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License; + * you may not use this file except in compliance with the Elastic License. + */ +package org.elasticsearch.license; + +import org.elasticsearch.action.ActionRequestBuilder; +import org.elasticsearch.client.ElasticsearchClient; + +class PostStartTrialRequestBuilder extends ActionRequestBuilder { + + PostStartTrialRequestBuilder(ElasticsearchClient client, PostStartTrialAction action) { + super(client, action, new PostStartTrialRequest()); + } +} diff --git a/plugin/src/main/java/org/elasticsearch/license/PostStartTrialResponse.java b/plugin/src/main/java/org/elasticsearch/license/PostStartTrialResponse.java new file mode 100644 index 00000000000..4235a303ec2 --- /dev/null +++ b/plugin/src/main/java/org/elasticsearch/license/PostStartTrialResponse.java @@ -0,0 +1,43 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License; + * you may not use this file except in compliance with the Elastic License. + */ +package org.elasticsearch.license; + +import org.elasticsearch.action.ActionResponse; +import org.elasticsearch.common.io.stream.StreamInput; +import org.elasticsearch.common.io.stream.StreamOutput; + +import java.io.IOException; + +class PostStartTrialResponse extends ActionResponse { + + enum STATUS { + UPGRADED_TO_TRIAL, + TRIAL_ALREADY_ACTIVATED + } + + private STATUS status; + + PostStartTrialResponse() { + } + + PostStartTrialResponse(STATUS status) { + this.status = status; + } + + public STATUS getStatus() { + return status; + } + + @Override + public void readFrom(StreamInput in) throws IOException { + status = in.readEnum(STATUS.class); + } + + @Override + public void writeTo(StreamOutput out) throws IOException { + out.writeEnum(status); + } +} diff --git a/plugin/src/main/java/org/elasticsearch/license/RestGetTrialStatus.java b/plugin/src/main/java/org/elasticsearch/license/RestGetTrialStatus.java new file mode 100644 index 00000000000..d4f90bbb037 --- /dev/null +++ b/plugin/src/main/java/org/elasticsearch/license/RestGetTrialStatus.java @@ -0,0 +1,48 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License; + * you may not use this file except in compliance with the Elastic License. + */ +package org.elasticsearch.license; + +import org.elasticsearch.common.settings.Settings; +import org.elasticsearch.common.xcontent.XContentBuilder; +import org.elasticsearch.rest.BytesRestResponse; +import org.elasticsearch.rest.RestController; +import org.elasticsearch.rest.RestRequest; +import org.elasticsearch.rest.RestResponse; +import org.elasticsearch.rest.RestStatus; +import org.elasticsearch.rest.action.RestBuilderListener; +import org.elasticsearch.xpack.XPackClient; +import org.elasticsearch.xpack.rest.XPackRestHandler; + +import java.io.IOException; + +import static org.elasticsearch.rest.RestRequest.Method.GET; + +public class RestGetTrialStatus extends XPackRestHandler { + + RestGetTrialStatus(Settings settings, RestController controller) { + super(settings); + controller.registerHandler(GET, URI_BASE + "/license/trial_status", this); + } + + @Override + protected RestChannelConsumer doPrepareRequest(RestRequest request, XPackClient client) throws IOException { + return channel -> client.licensing().prepareGetUpgradeToTrial().execute( + new RestBuilderListener(channel) { + @Override + public RestResponse buildResponse(GetTrialStatusResponse response, XContentBuilder builder) throws Exception { + builder.startObject(); + builder.field("eligible_to_start_trial", response.isEligibleToStartTrial()); + builder.endObject(); + return new BytesRestResponse(RestStatus.OK, builder); + } + }); + } + + @Override + public String getName() { + return "xpack_trial_status_action"; + } +} diff --git a/plugin/src/main/java/org/elasticsearch/license/RestPostStartTrialLicense.java b/plugin/src/main/java/org/elasticsearch/license/RestPostStartTrialLicense.java new file mode 100644 index 00000000000..6131d4f9a16 --- /dev/null +++ b/plugin/src/main/java/org/elasticsearch/license/RestPostStartTrialLicense.java @@ -0,0 +1,58 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License; + * you may not use this file except in compliance with the Elastic License. + */ +package org.elasticsearch.license; + +import org.elasticsearch.common.settings.Settings; +import org.elasticsearch.common.xcontent.XContentBuilder; +import org.elasticsearch.rest.BytesRestResponse; +import org.elasticsearch.rest.RestController; +import org.elasticsearch.rest.RestRequest; +import org.elasticsearch.rest.RestResponse; +import org.elasticsearch.rest.RestStatus; +import org.elasticsearch.rest.action.RestBuilderListener; +import org.elasticsearch.xpack.XPackClient; +import org.elasticsearch.xpack.rest.XPackRestHandler; + +import java.io.IOException; + +import static org.elasticsearch.rest.RestRequest.Method.POST; +import static org.elasticsearch.rest.RestRequest.Method.PUT; + +public class RestPostStartTrialLicense extends XPackRestHandler { + + RestPostStartTrialLicense(Settings settings, RestController controller) { + super(settings); + controller.registerHandler(POST, URI_BASE + "/license/start_trial", this); + } + + @Override + protected RestChannelConsumer doPrepareRequest(RestRequest request, XPackClient client) throws IOException { + return channel -> client.licensing().preparePutUpgradeToTrial().execute( + new RestBuilderListener(channel) { + @Override + public RestResponse buildResponse(PostStartTrialResponse response, XContentBuilder builder) throws Exception { + PostStartTrialResponse.STATUS status = response.getStatus(); + if (status == PostStartTrialResponse.STATUS.TRIAL_ALREADY_ACTIVATED) { + builder.startObject() + .field("trial_was_started", false) + .field("error_message", "Operation failed: Trial was already activated.") + .endObject(); + return new BytesRestResponse(RestStatus.FORBIDDEN, builder); + } else if (status == PostStartTrialResponse.STATUS.UPGRADED_TO_TRIAL) { + builder.startObject().field("trial_was_started", true).endObject(); + return new BytesRestResponse(RestStatus.OK, builder); + } else { + throw new IllegalArgumentException("Unexpected status for PostStartTrialResponse: [" + status + "]"); + } + } + }); + } + + @Override + public String getName() { + return "xpack_upgrade_to_trial_action"; + } +} diff --git a/plugin/src/main/java/org/elasticsearch/license/TransportGetTrialStatusAction.java b/plugin/src/main/java/org/elasticsearch/license/TransportGetTrialStatusAction.java new file mode 100644 index 00000000000..028bf45d873 --- /dev/null +++ b/plugin/src/main/java/org/elasticsearch/license/TransportGetTrialStatusAction.java @@ -0,0 +1,53 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License; + * you may not use this file except in compliance with the Elastic License. + */ +package org.elasticsearch.license; + +import org.elasticsearch.action.ActionListener; +import org.elasticsearch.action.support.ActionFilters; +import org.elasticsearch.action.support.master.TransportMasterNodeReadAction; +import org.elasticsearch.cluster.ClusterState; +import org.elasticsearch.cluster.block.ClusterBlockException; +import org.elasticsearch.cluster.block.ClusterBlockLevel; +import org.elasticsearch.cluster.metadata.IndexNameExpressionResolver; +import org.elasticsearch.cluster.service.ClusterService; +import org.elasticsearch.common.inject.Inject; +import org.elasticsearch.common.settings.Settings; +import org.elasticsearch.threadpool.ThreadPool; +import org.elasticsearch.transport.TransportService; + +public class TransportGetTrialStatusAction extends TransportMasterNodeReadAction { + + @Inject + public TransportGetTrialStatusAction(Settings settings, TransportService transportService, ClusterService clusterService, + ThreadPool threadPool, ActionFilters actionFilters, + IndexNameExpressionResolver indexNameExpressionResolver) { + super(settings, GetTrialStatusAction.NAME, transportService, clusterService, threadPool, actionFilters, + GetTrialStatusRequest::new, indexNameExpressionResolver); + } + + @Override + protected String executor() { + return ThreadPool.Names.SAME; + } + + @Override + protected GetTrialStatusResponse newResponse() { + return new GetTrialStatusResponse(); + } + + @Override + protected void masterOperation(GetTrialStatusRequest request, ClusterState state, + ActionListener listener) throws Exception { + LicensesMetaData licensesMetaData = state.metaData().custom(LicensesMetaData.TYPE); + listener.onResponse(new GetTrialStatusResponse(licensesMetaData == null || licensesMetaData.isEligibleForTrial())); + + } + + @Override + protected ClusterBlockException checkBlock(GetTrialStatusRequest request, ClusterState state) { + return state.blocks().globalBlockedException(ClusterBlockLevel.METADATA_READ); + } +} diff --git a/plugin/src/main/java/org/elasticsearch/license/TransportPostStartTrialAction.java b/plugin/src/main/java/org/elasticsearch/license/TransportPostStartTrialAction.java new file mode 100644 index 00000000000..b2a2680a332 --- /dev/null +++ b/plugin/src/main/java/org/elasticsearch/license/TransportPostStartTrialAction.java @@ -0,0 +1,54 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License; + * you may not use this file except in compliance with the Elastic License. + */ +package org.elasticsearch.license; + +import org.elasticsearch.action.ActionListener; +import org.elasticsearch.action.support.ActionFilters; +import org.elasticsearch.action.support.master.TransportMasterNodeAction; +import org.elasticsearch.cluster.ClusterState; +import org.elasticsearch.cluster.block.ClusterBlockException; +import org.elasticsearch.cluster.block.ClusterBlockLevel; +import org.elasticsearch.cluster.metadata.IndexNameExpressionResolver; +import org.elasticsearch.cluster.service.ClusterService; +import org.elasticsearch.common.inject.Inject; +import org.elasticsearch.common.settings.Settings; +import org.elasticsearch.threadpool.ThreadPool; +import org.elasticsearch.transport.TransportService; + +public class TransportPostStartTrialAction extends TransportMasterNodeAction { + + private final LicenseService licenseService; + + @Inject + public TransportPostStartTrialAction(Settings settings, TransportService transportService, ClusterService clusterService, + LicenseService licenseService, ThreadPool threadPool, ActionFilters actionFilters, + IndexNameExpressionResolver indexNameExpressionResolver) { + super(settings, PostStartTrialAction.NAME, transportService, clusterService, threadPool, actionFilters, + indexNameExpressionResolver, PostStartTrialRequest::new); + this.licenseService = licenseService; + } + + @Override + protected String executor() { + return ThreadPool.Names.SAME; + } + + @Override + protected PostStartTrialResponse newResponse() { + return new PostStartTrialResponse(); + } + + @Override + protected void masterOperation(PostStartTrialRequest request, ClusterState state, + ActionListener listener) throws Exception { + licenseService.upgradeSelfGeneratedLicense(listener); + } + + @Override + protected ClusterBlockException checkBlock(PostStartTrialRequest request, ClusterState state) { + return state.blocks().globalBlockedException(ClusterBlockLevel.METADATA_WRITE); + } +} diff --git a/plugin/src/main/java/org/elasticsearch/xpack/monitoring/MonitoredSystem.java b/plugin/src/main/java/org/elasticsearch/xpack/monitoring/MonitoredSystem.java index 2ccfbf5301b..88ee5a8fbbf 100644 --- a/plugin/src/main/java/org/elasticsearch/xpack/monitoring/MonitoredSystem.java +++ b/plugin/src/main/java/org/elasticsearch/xpack/monitoring/MonitoredSystem.java @@ -13,8 +13,8 @@ public enum MonitoredSystem { ES("es"), KIBANA("kibana"), + // TODO: when "BEATS" is re-added, add it to tests where we randomly select "LOGSTASH" LOGSTASH("logstash"), - BEATS("beats"), UNKNOWN("unknown"); private final String system; @@ -35,8 +35,6 @@ public enum MonitoredSystem { return KIBANA; case "logstash": return LOGSTASH; - case "beats": - return BEATS; default: // Return an "unknown" monitored system // that can easily be filtered out if diff --git a/plugin/src/main/java/org/elasticsearch/xpack/monitoring/exporter/MonitoringTemplateUtils.java b/plugin/src/main/java/org/elasticsearch/xpack/monitoring/exporter/MonitoringTemplateUtils.java index d92e233810e..bbea6af068f 100644 --- a/plugin/src/main/java/org/elasticsearch/xpack/monitoring/exporter/MonitoringTemplateUtils.java +++ b/plugin/src/main/java/org/elasticsearch/xpack/monitoring/exporter/MonitoringTemplateUtils.java @@ -41,7 +41,7 @@ public final class MonitoringTemplateUtils { /** * IDs of templates that can be used with {@linkplain #loadTemplate(String) loadTemplate}. */ - public static final String[] TEMPLATE_IDS = { "alerts", "es", "kibana", "logstash", "beats" }; + public static final String[] TEMPLATE_IDS = { "alerts", "es", "kibana", "logstash" }; /** * IDs of templates that can be used with {@linkplain #createEmptyTemplate(String) createEmptyTemplate} that are not managed by a diff --git a/plugin/src/main/java/org/elasticsearch/xpack/monitoring/rest/action/RestMonitoringBulkAction.java b/plugin/src/main/java/org/elasticsearch/xpack/monitoring/rest/action/RestMonitoringBulkAction.java index 8ae1ed74ade..fdb86531587 100644 --- a/plugin/src/main/java/org/elasticsearch/xpack/monitoring/rest/action/RestMonitoringBulkAction.java +++ b/plugin/src/main/java/org/elasticsearch/xpack/monitoring/rest/action/RestMonitoringBulkAction.java @@ -56,8 +56,7 @@ public class RestMonitoringBulkAction extends MonitoringRestHandler { final Map> versionsMap = new HashMap<>(); versionsMap.put(MonitoredSystem.KIBANA, allVersions); versionsMap.put(MonitoredSystem.LOGSTASH, allVersions); - // Beats did not report data in the 5.x timeline, so it should never send the original version - versionsMap.put(MonitoredSystem.BEATS, Collections.singletonList(MonitoringTemplateUtils.TEMPLATE_VERSION)); + // Beats did not report data in the 5.x timeline, so it should never send the original version [when we add it!] supportedApiVersions = Collections.unmodifiableMap(versionsMap); } diff --git a/plugin/src/main/java/org/elasticsearch/xpack/notification/jira/JiraAccount.java b/plugin/src/main/java/org/elasticsearch/xpack/notification/jira/JiraAccount.java index ff29ba7ff92..e6eab2a87ec 100644 --- a/plugin/src/main/java/org/elasticsearch/xpack/notification/jira/JiraAccount.java +++ b/plugin/src/main/java/org/elasticsearch/xpack/notification/jira/JiraAccount.java @@ -9,6 +9,10 @@ import org.elasticsearch.common.Booleans; import org.elasticsearch.common.Strings; import org.elasticsearch.common.settings.Settings; import org.elasticsearch.common.settings.SettingsException; +import org.elasticsearch.common.xcontent.NamedXContentRegistry; +import org.elasticsearch.common.xcontent.ToXContent; +import org.elasticsearch.common.xcontent.XContentBuilder; +import org.elasticsearch.common.xcontent.XContentType; import org.elasticsearch.xpack.common.http.HttpClient; import org.elasticsearch.xpack.common.http.HttpMethod; import org.elasticsearch.xpack.common.http.HttpProxy; @@ -18,6 +22,7 @@ import org.elasticsearch.xpack.common.http.Scheme; import org.elasticsearch.xpack.common.http.auth.basic.BasicAuth; import java.io.IOException; +import java.io.UncheckedIOException; import java.net.URI; import java.net.URISyntaxException; import java.util.Collections; @@ -68,7 +73,15 @@ public class JiraAccount { if (Strings.isEmpty(this.password)) { throw requiredSettingException(name, PASSWORD_SETTING); } - this.issueDefaults = Collections.unmodifiableMap(settings.getAsSettings(ISSUE_DEFAULTS_SETTING).getAsStructuredMap()); + try (XContentBuilder builder = XContentBuilder.builder(XContentType.JSON.xContent())) { + builder.startObject(); + settings.getAsSettings(ISSUE_DEFAULTS_SETTING).toXContent(builder, ToXContent.EMPTY_PARAMS); + builder.endObject(); + this.issueDefaults = Collections.unmodifiableMap(XContentType.JSON.xContent() + .createParser(new NamedXContentRegistry(Collections.emptyList()), builder.bytes()).map()); + } catch (IOException ex) { + throw new UncheckedIOException(ex); + } } public String getName() { diff --git a/plugin/src/main/java/org/elasticsearch/xpack/security/authc/support/DnRoleMapper.java b/plugin/src/main/java/org/elasticsearch/xpack/security/authc/support/DnRoleMapper.java index 24f1edc1325..04c5eaff6f4 100644 --- a/plugin/src/main/java/org/elasticsearch/xpack/security/authc/support/DnRoleMapper.java +++ b/plugin/src/main/java/org/elasticsearch/xpack/security/authc/support/DnRoleMapper.java @@ -32,6 +32,7 @@ import org.elasticsearch.action.ActionListener; import org.elasticsearch.bootstrap.BootstrapCheck; import org.elasticsearch.common.settings.Setting; import org.elasticsearch.common.settings.Settings; +import org.elasticsearch.common.settings.SettingsException; import org.elasticsearch.env.Environment; import org.elasticsearch.watcher.FileChangesListener; import org.elasticsearch.watcher.FileWatcher; @@ -127,8 +128,8 @@ public class DnRoleMapper implements UserRoleMapper { } } - try (InputStream in = Files.newInputStream(path)) { - Settings settings = Settings.builder().loadFromStream(path.toString(), in).build(); + try { + Settings settings = Settings.builder().loadFromPath(path).build(); Map> dnToRoles = new HashMap<>(); Set roles = settings.names(); @@ -163,7 +164,7 @@ public class DnRoleMapper implements UserRoleMapper { logger.debug("[{}] role mappings found in file [{}] for realm [{}/{}]", dnToRoles.size(), path.toAbsolutePath(), realmType, realmName); return unmodifiableMap(dnToRoles); - } catch (IOException | YAMLException e) { + } catch (IOException | SettingsException e) { throw new ElasticsearchException("could not read realm [" + realmType + "/" + realmName + "] role mappings file [" + path.toAbsolutePath() + "]", e); } diff --git a/plugin/src/main/java/org/elasticsearch/xpack/security/authz/privilege/SystemPrivilege.java b/plugin/src/main/java/org/elasticsearch/xpack/security/authz/privilege/SystemPrivilege.java index 2a75f33cb1b..bd106bd925c 100644 --- a/plugin/src/main/java/org/elasticsearch/xpack/security/authz/privilege/SystemPrivilege.java +++ b/plugin/src/main/java/org/elasticsearch/xpack/security/authz/privilege/SystemPrivilege.java @@ -22,7 +22,8 @@ public final class SystemPrivilege extends Privilege { "cluster:admin/reroute", // added for DiskThresholdDecider.DiskListener "indices:admin/mapping/put", // needed for recovery and shrink api "indices:admin/template/put", // needed for the TemplateUpgradeService - "indices:admin/template/delete" // needed for the TemplateUpgradeService + "indices:admin/template/delete", // needed for the TemplateUpgradeService + "indices:admin/seq_no/global_checkpoint_sync*" // needed for global checkpoint syncs ), Automatons.patterns("internal:transport/proxy/*"))); // no proxy actions for system user! private SystemPrivilege() { diff --git a/plugin/src/main/java/org/elasticsearch/xpack/ssl/RestrictedTrustConfig.java b/plugin/src/main/java/org/elasticsearch/xpack/ssl/RestrictedTrustConfig.java index 3ffee9a5219..6d67aa3c1f9 100644 --- a/plugin/src/main/java/org/elasticsearch/xpack/ssl/RestrictedTrustConfig.java +++ b/plugin/src/main/java/org/elasticsearch/xpack/ssl/RestrictedTrustConfig.java @@ -83,10 +83,8 @@ public final class RestrictedTrustConfig extends TrustConfig { } private CertificateTrustRestrictions readTrustGroup(Path path) throws IOException { - try (InputStream in = Files.newInputStream(path)) { - Settings settings = Settings.builder().loadFromStream(path.toString(), in).build(); - final String[] trustNodeNames = settings.getAsArray(RESTRICTIONS_KEY_SUBJECT_NAME); - return new CertificateTrustRestrictions(Arrays.asList(trustNodeNames)); - } + Settings settings = Settings.builder().loadFromPath(path).build(); + final String[] trustNodeNames = settings.getAsArray(RESTRICTIONS_KEY_SUBJECT_NAME); + return new CertificateTrustRestrictions(Arrays.asList(trustNodeNames)); } } diff --git a/plugin/src/test/java/org/elasticsearch/integration/DocumentLevelSecurityTests.java b/plugin/src/test/java/org/elasticsearch/integration/DocumentLevelSecurityTests.java index 5715f5b574b..96cd8e250a1 100644 --- a/plugin/src/test/java/org/elasticsearch/integration/DocumentLevelSecurityTests.java +++ b/plugin/src/test/java/org/elasticsearch/integration/DocumentLevelSecurityTests.java @@ -586,7 +586,7 @@ public class DocumentLevelSecurityTests extends SecurityIntegTestCase { public void testChildrenAggregation() throws Exception { assertAcked(client().admin().indices().prepareCreate("test") - .setSettings("index.version.created", Version.V_5_6_0.id) + .setSettings(Settings.builder().put("index.version.created", Version.V_5_6_0.id)) .addMapping("type1", "field1", "type=text", "field2", "type=text") .addMapping("type2", "_parent", "type=type1", "field3", "type=text,fielddata=true") ); @@ -643,7 +643,7 @@ public class DocumentLevelSecurityTests extends SecurityIntegTestCase { public void testParentChild_parentField() { assertAcked(prepareCreate("test") - .setSettings("index.version.created", Version.V_5_6_0.id) + .setSettings(Settings.builder().put("index.version.created", Version.V_5_6_0.id)) .addMapping("parent") .addMapping("child", "_parent", "type=parent", "field1", "type=text", "field2", "type=text", "field3", "type=text")); ensureGreen(); diff --git a/plugin/src/test/java/org/elasticsearch/integration/FieldLevelSecurityTests.java b/plugin/src/test/java/org/elasticsearch/integration/FieldLevelSecurityTests.java index d0727a1e90d..3ac523a5860 100644 --- a/plugin/src/test/java/org/elasticsearch/integration/FieldLevelSecurityTests.java +++ b/plugin/src/test/java/org/elasticsearch/integration/FieldLevelSecurityTests.java @@ -1221,7 +1221,7 @@ public class FieldLevelSecurityTests extends SecurityIntegTestCase { public void testParentChild_parentField() { assertAcked(prepareCreate("test") - .setSettings("index.version.created", Version.V_5_6_0.id) + .setSettings(Settings.builder().put("index.version.created", Version.V_5_6_0.id)) .addMapping("parent") .addMapping("child", "_parent", "type=parent")); ensureGreen(); diff --git a/plugin/src/test/java/org/elasticsearch/license/AbstractLicenseServiceTestCase.java b/plugin/src/test/java/org/elasticsearch/license/AbstractLicenseServiceTestCase.java index dcead1236f2..0f5d744e2b5 100644 --- a/plugin/src/test/java/org/elasticsearch/license/AbstractLicenseServiceTestCase.java +++ b/plugin/src/test/java/org/elasticsearch/license/AbstractLicenseServiceTestCase.java @@ -49,16 +49,20 @@ public abstract class AbstractLicenseServiceTestCase extends ESTestCase { } protected void setInitialState(License license, XPackLicenseState licenseState, Settings settings) { + setInitialState(license, licenseState, settings, randomBoolean() ? "trial" : "basic"); + } + + protected void setInitialState(License license, XPackLicenseState licenseState, Settings settings, String selfGeneratedType) { Path tempDir = createTempDir(); when(environment.configFile()).thenReturn(tempDir); - licenseType = randomBoolean() ? "trial" : "basic"; + licenseType = selfGeneratedType; settings = Settings.builder().put(settings).put(LicenseService.SELF_GENERATED_LICENSE_TYPE.getKey(), licenseType).build(); licenseService = new LicenseService(settings, clusterService, clock, environment, resourceWatcherService, licenseState); ClusterState state = mock(ClusterState.class); final ClusterBlocks noBlock = ClusterBlocks.builder().build(); when(state.blocks()).thenReturn(noBlock); MetaData metaData = mock(MetaData.class); - when(metaData.custom(LicensesMetaData.TYPE)).thenReturn(new LicensesMetaData(license)); + when(metaData.custom(LicensesMetaData.TYPE)).thenReturn(new LicensesMetaData(license, null)); when(state.metaData()).thenReturn(metaData); final DiscoveryNode mockNode = new DiscoveryNode("b", buildNewFakeTransportAddress(), emptyMap(), emptySet(), Version.CURRENT); when(discoveryNodes.getMasterNode()).thenReturn(mockNode); diff --git a/plugin/src/test/java/org/elasticsearch/license/AbstractLicensesIntegrationTestCase.java b/plugin/src/test/java/org/elasticsearch/license/AbstractLicensesIntegrationTestCase.java index 4adf914121a..3c2d8768bc0 100644 --- a/plugin/src/test/java/org/elasticsearch/license/AbstractLicensesIntegrationTestCase.java +++ b/plugin/src/test/java/org/elasticsearch/license/AbstractLicensesIntegrationTestCase.java @@ -65,7 +65,7 @@ public abstract class AbstractLicensesIntegrationTestCase extends ESIntegTestCas @Override public ClusterState execute(ClusterState currentState) throws Exception { MetaData.Builder mdBuilder = MetaData.builder(currentState.metaData()); - mdBuilder.putCustom(LicensesMetaData.TYPE, new LicensesMetaData(license)); + mdBuilder.putCustom(LicensesMetaData.TYPE, new LicensesMetaData(license, null)); return ClusterState.builder(currentState).metaData(mdBuilder).build(); } diff --git a/plugin/src/test/java/org/elasticsearch/license/LicenseClusterChangeTests.java b/plugin/src/test/java/org/elasticsearch/license/LicenseClusterChangeTests.java index 903cfa4f5fe..e109b891f28 100644 --- a/plugin/src/test/java/org/elasticsearch/license/LicenseClusterChangeTests.java +++ b/plugin/src/test/java/org/elasticsearch/license/LicenseClusterChangeTests.java @@ -47,7 +47,7 @@ public class LicenseClusterChangeTests extends AbstractLicenseServiceTestCase { public void testNotificationOnNewLicense() throws Exception { ClusterState oldState = ClusterState.builder(new ClusterName("a")).build(); final License license = TestUtils.generateSignedLicense(TimeValue.timeValueHours(24)); - MetaData metaData = MetaData.builder().putCustom(LicensesMetaData.TYPE, new LicensesMetaData(license)).build(); + MetaData metaData = MetaData.builder().putCustom(LicensesMetaData.TYPE, new LicensesMetaData(license, null)).build(); ClusterState newState = ClusterState.builder(new ClusterName("a")).metaData(metaData).build(); licenseService.clusterChanged(new ClusterChangedEvent("simulated", newState, oldState)); assertThat(licenseState.activeUpdates.size(), equalTo(1)); @@ -56,7 +56,7 @@ public class LicenseClusterChangeTests extends AbstractLicenseServiceTestCase { public void testNoNotificationOnExistingLicense() throws Exception { final License license = TestUtils.generateSignedLicense(TimeValue.timeValueHours(24)); - MetaData metaData = MetaData.builder().putCustom(LicensesMetaData.TYPE, new LicensesMetaData(license)).build(); + MetaData metaData = MetaData.builder().putCustom(LicensesMetaData.TYPE, new LicensesMetaData(license, null)).build(); ClusterState newState = ClusterState.builder(new ClusterName("a")).metaData(metaData).build(); ClusterState oldState = ClusterState.builder(newState).build(); licenseService.clusterChanged(new ClusterChangedEvent("simulated", newState, oldState)); diff --git a/plugin/src/test/java/org/elasticsearch/license/LicenseRegistrationTests.java b/plugin/src/test/java/org/elasticsearch/license/LicenseRegistrationTests.java index 8c76a009e40..8476d1b563e 100644 --- a/plugin/src/test/java/org/elasticsearch/license/LicenseRegistrationTests.java +++ b/plugin/src/test/java/org/elasticsearch/license/LicenseRegistrationTests.java @@ -18,9 +18,9 @@ import static org.mockito.Mockito.when; public class LicenseRegistrationTests extends AbstractLicenseServiceTestCase { - public void testTrialLicenseRequestOnEmptyLicenseState() throws Exception { + public void testSelfGeneratedTrialLicense() throws Exception { XPackLicenseState licenseState = new XPackLicenseState(); - setInitialState(null, licenseState, Settings.EMPTY); + setInitialState(null, licenseState, Settings.EMPTY, "trial"); when(discoveryNodes.isLocalNodeElectedMaster()).thenReturn(true); licenseService.start(); @@ -31,6 +31,26 @@ public class LicenseRegistrationTests extends AbstractLicenseServiceTestCase { LicensesMetaData licenseMetaData = stateWithLicense.metaData().custom(LicensesMetaData.TYPE); assertNotNull(licenseMetaData); assertNotNull(licenseMetaData.getLicense()); + assertFalse(licenseMetaData.isEligibleForTrial()); + assertEquals("trial", licenseMetaData.getLicense().type()); + assertEquals(clock.millis() + LicenseService.SELF_GENERATED_LICENSE_DURATION.millis(), licenseMetaData.getLicense().expiryDate()); + } + + public void testSelfGeneratedBasicLicense() throws Exception { + XPackLicenseState licenseState = new XPackLicenseState(); + setInitialState(null, licenseState, Settings.EMPTY, "basic"); + when(discoveryNodes.isLocalNodeElectedMaster()).thenReturn(true); + licenseService.start(); + + ClusterState state = ClusterState.builder(new ClusterName("a")).build(); + ArgumentCaptor stateUpdater = ArgumentCaptor.forClass(ClusterStateUpdateTask.class); + verify(clusterService, Mockito.times(1)).submitStateUpdateTask(any(), stateUpdater.capture()); + ClusterState stateWithLicense = stateUpdater.getValue().execute(state); + LicensesMetaData licenseMetaData = stateWithLicense.metaData().custom(LicensesMetaData.TYPE); + assertNotNull(licenseMetaData); + assertNotNull(licenseMetaData.getLicense()); + assertTrue(licenseMetaData.isEligibleForTrial()); + assertEquals("basic", licenseMetaData.getLicense().type()); assertEquals(clock.millis() + LicenseService.SELF_GENERATED_LICENSE_DURATION.millis(), licenseMetaData.getLicense().expiryDate()); } } \ No newline at end of file diff --git a/plugin/src/test/java/org/elasticsearch/license/LicensesMetaDataSerializationTests.java b/plugin/src/test/java/org/elasticsearch/license/LicensesMetaDataSerializationTests.java index f123cd1390d..b85a3480fa7 100644 --- a/plugin/src/test/java/org/elasticsearch/license/LicensesMetaDataSerializationTests.java +++ b/plugin/src/test/java/org/elasticsearch/license/LicensesMetaDataSerializationTests.java @@ -5,6 +5,7 @@ */ package org.elasticsearch.license; +import org.elasticsearch.Version; import org.elasticsearch.cluster.ClusterModule; import org.elasticsearch.cluster.metadata.MetaData; import org.elasticsearch.cluster.metadata.RepositoriesMetaData; @@ -29,9 +30,10 @@ import static org.hamcrest.Matchers.notNullValue; import static org.hamcrest.Matchers.nullValue; public class LicensesMetaDataSerializationTests extends ESTestCase { + public void testXContentSerializationOneSignedLicense() throws Exception { License license = TestUtils.generateSignedLicense(TimeValue.timeValueHours(2)); - LicensesMetaData licensesMetaData = new LicensesMetaData(license); + LicensesMetaData licensesMetaData = new LicensesMetaData(license, null); XContentBuilder builder = XContentFactory.jsonBuilder(); builder.startObject(); builder.startObject("licenses"); @@ -40,12 +42,27 @@ public class LicensesMetaDataSerializationTests extends ESTestCase { builder.endObject(); LicensesMetaData licensesMetaDataFromXContent = getLicensesMetaDataFromXContent(createParser(builder)); assertThat(licensesMetaDataFromXContent.getLicense(), equalTo(license)); + assertNull(licensesMetaDataFromXContent.getMostRecentTrialVersion()); + } + + public void testXContentSerializationOneSignedLicenseWithUsedTrial() throws Exception { + License license = TestUtils.generateSignedLicense(TimeValue.timeValueHours(2)); + LicensesMetaData licensesMetaData = new LicensesMetaData(license, Version.CURRENT); + XContentBuilder builder = XContentFactory.jsonBuilder(); + builder.startObject(); + builder.startObject("licenses"); + licensesMetaData.toXContent(builder, ToXContent.EMPTY_PARAMS); + builder.endObject(); + builder.endObject(); + LicensesMetaData licensesMetaDataFromXContent = getLicensesMetaDataFromXContent(createParser(builder)); + assertThat(licensesMetaDataFromXContent.getLicense(), equalTo(license)); + assertEquals(licensesMetaDataFromXContent.getMostRecentTrialVersion(), Version.CURRENT); } public void testLicenseMetadataParsingDoesNotSwallowOtherMetaData() throws Exception { new Licensing(Settings.EMPTY); // makes sure LicensePlugin is registered in Custom MetaData License license = TestUtils.generateSignedLicense(TimeValue.timeValueHours(2)); - LicensesMetaData licensesMetaData = new LicensesMetaData(license); + LicensesMetaData licensesMetaData = new LicensesMetaData(license, Version.CURRENT); RepositoryMetaData repositoryMetaData = new RepositoryMetaData("repo", "fs", Settings.EMPTY); RepositoriesMetaData repositoriesMetaData = new RepositoriesMetaData(repositoryMetaData); final MetaData.Builder metaDataBuilder = MetaData.builder(); @@ -79,7 +96,7 @@ public class LicensesMetaDataSerializationTests extends ESTestCase { .type(randomBoolean() ? "trial" : "basic") .expiryDate(issueDate + TimeValue.timeValueHours(2).getMillis()); final License trialLicense = SelfGeneratedLicense.create(specBuilder); - LicensesMetaData licensesMetaData = new LicensesMetaData(trialLicense); + LicensesMetaData licensesMetaData = new LicensesMetaData(trialLicense, Version.CURRENT); XContentBuilder builder = XContentFactory.jsonBuilder(); builder.startObject(); builder.startObject("licenses"); @@ -88,6 +105,7 @@ public class LicensesMetaDataSerializationTests extends ESTestCase { builder.endObject(); LicensesMetaData licensesMetaDataFromXContent = getLicensesMetaDataFromXContent(createParser(builder)); assertThat(licensesMetaDataFromXContent.getLicense(), equalTo(trialLicense)); + assertEquals(licensesMetaDataFromXContent.getMostRecentTrialVersion(), Version.CURRENT); } public void testLicenseTombstoneFromXContext() throws Exception { @@ -101,6 +119,19 @@ public class LicensesMetaDataSerializationTests extends ESTestCase { assertThat(metaDataFromXContent.getLicense(), equalTo(LicensesMetaData.LICENSE_TOMBSTONE)); } + public void testLicenseTombstoneWithUsedTrialFromXContext() throws Exception { + final XContentBuilder builder = XContentFactory.jsonBuilder(); + builder.startObject(); + builder.startObject("licenses"); + builder.nullField("license"); + builder.field("trial_license", Version.CURRENT.toString()); + builder.endObject(); + builder.endObject(); + LicensesMetaData metaDataFromXContent = getLicensesMetaDataFromXContent(createParser(builder)); + assertThat(metaDataFromXContent.getLicense(), equalTo(LicensesMetaData.LICENSE_TOMBSTONE)); + assertEquals(metaDataFromXContent.getMostRecentTrialVersion(), Version.CURRENT); + } + private static LicensesMetaData getLicensesMetaDataFromXContent(XContentParser parser) throws Exception { parser.nextToken(); // consume null parser.nextToken(); // consume "licenses" diff --git a/plugin/src/test/java/org/elasticsearch/license/TestUtils.java b/plugin/src/test/java/org/elasticsearch/license/TestUtils.java index 554246ae1e1..0e1b520b6ff 100644 --- a/plugin/src/test/java/org/elasticsearch/license/TestUtils.java +++ b/plugin/src/test/java/org/elasticsearch/license/TestUtils.java @@ -348,6 +348,6 @@ public class TestUtils { } public static void putLicense(MetaData.Builder builder, License license) { - builder.putCustom(LicensesMetaData.TYPE, new LicensesMetaData(license)); + builder.putCustom(LicensesMetaData.TYPE, new LicensesMetaData(license, null)); } } diff --git a/plugin/src/test/java/org/elasticsearch/license/UpgradeToTrialTests.java b/plugin/src/test/java/org/elasticsearch/license/UpgradeToTrialTests.java new file mode 100644 index 00000000000..7326e4d7e25 --- /dev/null +++ b/plugin/src/test/java/org/elasticsearch/license/UpgradeToTrialTests.java @@ -0,0 +1,78 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License; + * you may not use this file except in compliance with the Elastic License. + */ +package org.elasticsearch.license; + +import org.elasticsearch.client.Response; +import org.elasticsearch.client.ResponseException; +import org.elasticsearch.client.RestClient; +import org.elasticsearch.common.io.Streams; +import org.elasticsearch.common.network.NetworkModule; +import org.elasticsearch.common.settings.Settings; +import org.elasticsearch.plugins.Plugin; +import org.elasticsearch.test.ESIntegTestCase; +import org.elasticsearch.transport.Netty4Plugin; +import org.elasticsearch.xpack.XPackPlugin; + +import java.io.InputStreamReader; +import java.nio.charset.StandardCharsets; +import java.util.Arrays; +import java.util.Collection; + +import static org.elasticsearch.test.ESIntegTestCase.Scope.SUITE; + +@ESIntegTestCase.ClusterScope(scope = SUITE) +public class UpgradeToTrialTests extends AbstractLicensesIntegrationTestCase { + + @Override + protected Settings nodeSettings(int nodeOrdinal) { + return Settings.builder() + .put(super.nodeSettings(nodeOrdinal)) + .put("node.data", true) + .put(LicenseService.SELF_GENERATED_LICENSE_TYPE.getKey(), "basic") + .put(NetworkModule.HTTP_ENABLED.getKey(), true).build(); + } + + @Override + protected Collection> nodePlugins() { + return Arrays.asList(XPackPlugin.class, Netty4Plugin.class); + } + + @Override + protected Collection> transportClientPlugins() { + return nodePlugins(); + } + + public void testUpgradeToTrial() throws Exception { + LicensingClient licensingClient = new LicensingClient(client()); + GetLicenseResponse getLicenseResponse = licensingClient.prepareGetLicense().get(); + + assertEquals("basic", getLicenseResponse.license().type()); + + RestClient restClient = getRestClient(); + Response response = restClient.performRequest("GET", "/_xpack/license/trial_status"); + String body = Streams.copyToString(new InputStreamReader(response.getEntity().getContent(), StandardCharsets.UTF_8)); + assertEquals(200, response.getStatusLine().getStatusCode()); + assertEquals("{\"eligible_to_start_trial\":true}", body); + + Response response2 = restClient.performRequest("POST", "/_xpack/license/start_trial"); + String body2 = Streams.copyToString(new InputStreamReader(response2.getEntity().getContent(), StandardCharsets.UTF_8)); + assertEquals(200, response2.getStatusLine().getStatusCode()); + assertEquals("{\"trial_was_started\":true}", body2); + + Response response3 = restClient.performRequest("GET", "/_xpack/license/trial_status"); + String body3 = Streams.copyToString(new InputStreamReader(response3.getEntity().getContent(), StandardCharsets.UTF_8)); + assertEquals(200, response3.getStatusLine().getStatusCode()); + assertEquals("{\"eligible_to_start_trial\":false}", body3); + + ResponseException ex = expectThrows(ResponseException.class, + () -> restClient.performRequest("POST", "/_xpack/license/start_trial")); + Response response4 = ex.getResponse(); + String body4 = Streams.copyToString(new InputStreamReader(response4.getEntity().getContent(), StandardCharsets.UTF_8)); + assertEquals(403, response4.getStatusLine().getStatusCode()); + assertTrue(body4.contains("\"trial_was_started\":false")); + assertTrue(body4.contains("\"error_message\":\"Operation failed: Trial was already activated.\"")); + } +} diff --git a/plugin/src/test/java/org/elasticsearch/test/SecurityIntegTestCase.java b/plugin/src/test/java/org/elasticsearch/test/SecurityIntegTestCase.java index bc7370ece0a..f267f40f228 100644 --- a/plugin/src/test/java/org/elasticsearch/test/SecurityIntegTestCase.java +++ b/plugin/src/test/java/org/elasticsearch/test/SecurityIntegTestCase.java @@ -214,7 +214,7 @@ public abstract class SecurityIntegTestCase extends ESIntegTestCase { // Disable native ML autodetect_process as the c++ controller won't be available .put(MachineLearning.AUTODETECT_PROCESS.getKey(), false); Settings customSettings = customSecuritySettingsSource.nodeSettings(nodeOrdinal); - builder.put(customSettings.getAsMap()); // handle secure settings separately + builder.put(customSettings, false); // handle secure settings separately Settings.Builder customBuilder = Settings.builder().put(customSettings); if (customBuilder.getSecureSettings() != null) { SecuritySettingsSource.addSecureSettings(builder, secureSettings -> diff --git a/plugin/src/test/java/org/elasticsearch/xpack/graph/test/GraphTests.java b/plugin/src/test/java/org/elasticsearch/xpack/graph/test/GraphTests.java index b903a7bc518..9ad13c83e3d 100644 --- a/plugin/src/test/java/org/elasticsearch/xpack/graph/test/GraphTests.java +++ b/plugin/src/test/java/org/elasticsearch/xpack/graph/test/GraphTests.java @@ -79,7 +79,7 @@ public class GraphTests extends XPackSingleNodeTestCase { public void setUp() throws Exception { super.setUp(); assertAcked(client().admin().indices().prepareCreate("test") - .setSettings(SETTING_NUMBER_OF_SHARDS, 2, SETTING_NUMBER_OF_REPLICAS, 0) + .setSettings(Settings.builder().put(SETTING_NUMBER_OF_SHARDS, 2).put(SETTING_NUMBER_OF_REPLICAS, 0)) .addMapping("type", "decade", "type=keyword", "people", "type=keyword", diff --git a/plugin/src/test/java/org/elasticsearch/xpack/monitoring/MonitoringSettingsIntegTests.java b/plugin/src/test/java/org/elasticsearch/xpack/monitoring/MonitoringSettingsIntegTests.java index 8fbe69c7377..7a05a291e06 100644 --- a/plugin/src/test/java/org/elasticsearch/xpack/monitoring/MonitoringSettingsIntegTests.java +++ b/plugin/src/test/java/org/elasticsearch/xpack/monitoring/MonitoringSettingsIntegTests.java @@ -92,16 +92,12 @@ public class MonitoringSettingsIntegTests extends MonitoringIntegTestCase { MonitoringSettings.JOB_STATS_TIMEOUT}; for (Setting setting : monitoringSettings) { if (setting.isDynamic()) { - Object updated = null; if (setting.get(Settings.EMPTY) instanceof TimeValue) { - updated = newRandomTimeValue(); - transientSettings.put(setting.getKey(), updated); + transientSettings.put(setting.getKey(), newRandomTimeValue().toString()); } else if (setting.get(Settings.EMPTY) instanceof Boolean) { - updated = randomBoolean(); - transientSettings.put(setting.getKey(), updated); + transientSettings.put(setting.getKey(), randomBoolean()); } else if (setting.get(Settings.EMPTY) instanceof List) { - updated = randomStringArray(); - transientSettings.putArray(setting.getKey(), (String[]) updated); + transientSettings.putArray(setting.getKey(), randomStringArray()); } else { fail("unknown dynamic setting [" + setting + "]"); } diff --git a/plugin/src/test/java/org/elasticsearch/xpack/monitoring/action/TransportMonitoringBulkActionTests.java b/plugin/src/test/java/org/elasticsearch/xpack/monitoring/action/TransportMonitoringBulkActionTests.java index decc1f01a37..50c3066ff4e 100644 --- a/plugin/src/test/java/org/elasticsearch/xpack/monitoring/action/TransportMonitoringBulkActionTests.java +++ b/plugin/src/test/java/org/elasticsearch/xpack/monitoring/action/TransportMonitoringBulkActionTests.java @@ -131,7 +131,7 @@ public class TransportMonitoringBulkActionTests extends ESTestCase { final MonitoringBulkRequest request = new MonitoringBulkRequest(); - final MonitoredSystem system = randomFrom(MonitoredSystem.KIBANA, MonitoredSystem.BEATS, MonitoredSystem.LOGSTASH); + final MonitoredSystem system = randomFrom(MonitoredSystem.KIBANA, MonitoredSystem.LOGSTASH); final String type = randomAlphaOfLength(5); final String id = randomBoolean() ? randomAlphaOfLength(5) : null; final long timestamp = randomNonNegativeLong(); @@ -198,7 +198,7 @@ public class TransportMonitoringBulkActionTests extends ESTestCase { public void testAsyncActionCreateMonitoringDocs() throws Exception { final List docs = new ArrayList<>(); - final MonitoredSystem system = randomFrom(MonitoredSystem.KIBANA, MonitoredSystem.BEATS, MonitoredSystem.LOGSTASH); + final MonitoredSystem system = randomFrom(MonitoredSystem.KIBANA, MonitoredSystem.LOGSTASH); final String type = randomAlphaOfLength(5); final String id = randomBoolean() ? randomAlphaOfLength(5) : null; final long timestamp = randomBoolean() ? randomNonNegativeLong() : 0L; @@ -235,7 +235,7 @@ public class TransportMonitoringBulkActionTests extends ESTestCase { public void testAsyncActionCreateMonitoringDocWithNoTimestamp() throws Exception { final MonitoringBulkDoc monitoringBulkDoc = - new MonitoringBulkDoc(MonitoredSystem.BEATS, "_type", "_id", 0L, 0L, BytesArray.EMPTY, XContentType.JSON); + new MonitoringBulkDoc(MonitoredSystem.LOGSTASH, "_type", "_id", 0L, 0L, BytesArray.EMPTY, XContentType.JSON); final MonitoringDoc monitoringDoc = new TransportMonitoringBulkAction.AsyncAction(null, null, null, "", 456L, null).createMonitoringDoc(monitoringBulkDoc); diff --git a/plugin/src/test/java/org/elasticsearch/xpack/monitoring/exporter/MonitoringTemplateUtilsTests.java b/plugin/src/test/java/org/elasticsearch/xpack/monitoring/exporter/MonitoringTemplateUtilsTests.java index 38f3d25bbeb..f5ce6b7a34b 100644 --- a/plugin/src/test/java/org/elasticsearch/xpack/monitoring/exporter/MonitoringTemplateUtilsTests.java +++ b/plugin/src/test/java/org/elasticsearch/xpack/monitoring/exporter/MonitoringTemplateUtilsTests.java @@ -96,8 +96,6 @@ public class MonitoringTemplateUtilsTests extends ESTestCase { DateTimeFormatter formatter = DateTimeFormat.forPattern("YYYY.MM.dd").withZoneUTC(); assertThat(indexName(formatter, MonitoredSystem.ES, timestamp), equalTo(".monitoring-es-" + TEMPLATE_VERSION + "-2017.08.03")); - assertThat(indexName(formatter, MonitoredSystem.BEATS, timestamp), - equalTo(".monitoring-beats-" + TEMPLATE_VERSION + "-2017.08.03")); assertThat(indexName(formatter, MonitoredSystem.KIBANA, timestamp), equalTo(".monitoring-kibana-" + TEMPLATE_VERSION + "-2017.08.03")); assertThat(indexName(formatter, MonitoredSystem.LOGSTASH, timestamp), @@ -106,8 +104,6 @@ public class MonitoringTemplateUtilsTests extends ESTestCase { formatter = DateTimeFormat.forPattern("YYYY-dd-MM-HH.mm.ss").withZoneUTC(); assertThat(indexName(formatter, MonitoredSystem.ES, timestamp), equalTo(".monitoring-es-" + TEMPLATE_VERSION + "-2017-03-08-13.47.58")); - assertThat(indexName(formatter, MonitoredSystem.BEATS, timestamp), - equalTo(".monitoring-beats-" + TEMPLATE_VERSION + "-2017-03-08-13.47.58")); assertThat(indexName(formatter, MonitoredSystem.KIBANA, timestamp), equalTo(".monitoring-kibana-" + TEMPLATE_VERSION + "-2017-03-08-13.47.58")); assertThat(indexName(formatter, MonitoredSystem.LOGSTASH, timestamp), diff --git a/plugin/src/test/java/org/elasticsearch/xpack/monitoring/exporter/http/HttpExporterResourceTests.java b/plugin/src/test/java/org/elasticsearch/xpack/monitoring/exporter/http/HttpExporterResourceTests.java index b8ae64fe09e..0e49dca970b 100644 --- a/plugin/src/test/java/org/elasticsearch/xpack/monitoring/exporter/http/HttpExporterResourceTests.java +++ b/plugin/src/test/java/org/elasticsearch/xpack/monitoring/exporter/http/HttpExporterResourceTests.java @@ -61,9 +61,9 @@ public class HttpExporterResourceTests extends AbstractPublishableHttpResourceTe private final boolean createOldTemplates = randomBoolean(); /** - * kibana, logstash, beats + * kibana, logstash (and beats in the future) */ - private final int EXPECTED_TEMPLATES = 5 + (createOldTemplates ? OLD_TEMPLATE_IDS.length : 0); + private final int EXPECTED_TEMPLATES = TEMPLATE_IDS.length + (createOldTemplates ? OLD_TEMPLATE_IDS.length : 0); private final int EXPECTED_PIPELINES = PIPELINE_IDS.length; private final int EXPECTED_WATCHES = ClusterAlertsUtil.WATCH_IDS.length; diff --git a/plugin/src/test/java/org/elasticsearch/xpack/monitoring/exporter/http/HttpExporterTests.java b/plugin/src/test/java/org/elasticsearch/xpack/monitoring/exporter/http/HttpExporterTests.java index 449fb2ec538..e1d66161918 100644 --- a/plugin/src/test/java/org/elasticsearch/xpack/monitoring/exporter/http/HttpExporterTests.java +++ b/plugin/src/test/java/org/elasticsearch/xpack/monitoring/exporter/http/HttpExporterTests.java @@ -37,6 +37,7 @@ import java.util.stream.Collectors; import static org.elasticsearch.xpack.monitoring.exporter.MonitoringTemplateUtils.OLD_TEMPLATE_IDS; import static org.elasticsearch.xpack.monitoring.exporter.MonitoringTemplateUtils.PIPELINE_IDS; +import static org.elasticsearch.xpack.monitoring.exporter.MonitoringTemplateUtils.TEMPLATE_IDS; import static org.hamcrest.Matchers.equalTo; import static org.hamcrest.Matchers.hasSize; import static org.hamcrest.Matchers.not; @@ -347,7 +348,7 @@ public class HttpExporterTests extends ESTestCase { assertThat(multiResource.getResources().size(), equalTo(version + templates.size() + pipelines.size() + watcherCheck.size())); assertThat(version, equalTo(1)); - assertThat(templates, hasSize(createOldTemplates ? 5 + OLD_TEMPLATE_IDS.length : 5)); + assertThat(templates, hasSize(createOldTemplates ? TEMPLATE_IDS.length + OLD_TEMPLATE_IDS.length : TEMPLATE_IDS.length)); assertThat(pipelines, hasSize(useIngest ? PIPELINE_IDS.length : 0)); assertThat(watcherCheck, hasSize(clusterAlertManagement ? 1 : 0)); assertThat(watches, hasSize(clusterAlertManagement ? ClusterAlertsUtil.WATCH_IDS.length : 0)); diff --git a/plugin/src/test/java/org/elasticsearch/xpack/monitoring/exporter/local/LocalExporterIntegTests.java b/plugin/src/test/java/org/elasticsearch/xpack/monitoring/exporter/local/LocalExporterIntegTests.java index efef37c16f6..637f083c794 100644 --- a/plugin/src/test/java/org/elasticsearch/xpack/monitoring/exporter/local/LocalExporterIntegTests.java +++ b/plugin/src/test/java/org/elasticsearch/xpack/monitoring/exporter/local/LocalExporterIntegTests.java @@ -55,7 +55,6 @@ import java.util.stream.Collectors; import static org.elasticsearch.search.aggregations.AggregationBuilders.max; import static org.elasticsearch.search.aggregations.AggregationBuilders.terms; import static org.elasticsearch.test.hamcrest.ElasticsearchAssertions.assertAcked; -import static org.elasticsearch.xpack.monitoring.MonitoredSystem.BEATS; import static org.elasticsearch.xpack.monitoring.MonitoredSystem.KIBANA; import static org.elasticsearch.xpack.monitoring.MonitoredSystem.LOGSTASH; import static org.elasticsearch.xpack.monitoring.exporter.MonitoringTemplateUtils.PIPELINE_IDS; @@ -231,12 +230,9 @@ public class LocalExporterIntegTests extends LocalExporterIntegTestCase { templates.add(".monitoring-es"); templates.add(".monitoring-kibana"); templates.add(".monitoring-logstash"); - templates.add(".monitoring-beats"); - GetIndexTemplatesResponse response = - client().admin().indices().prepareGetTemplates(".monitoring-*").get(); - Set actualTemplates = response.getIndexTemplates().stream() - .map(IndexTemplateMetaData::getName).collect(Collectors.toSet()); + GetIndexTemplatesResponse response = client().admin().indices().prepareGetTemplates(".monitoring-*").get(); + Set actualTemplates = response.getIndexTemplates().stream().map(IndexTemplateMetaData::getName).collect(Collectors.toSet()); assertEquals(templates, actualTemplates); } @@ -330,7 +326,7 @@ public class LocalExporterIntegTests extends LocalExporterIntegTestCase { } private static MonitoringBulkDoc createMonitoringBulkDoc() throws IOException { - final MonitoredSystem system = randomFrom(BEATS, KIBANA, LOGSTASH); + final MonitoredSystem system = randomFrom(KIBANA, LOGSTASH); final XContentType xContentType = randomFrom(XContentType.values()); final BytesReference source; diff --git a/plugin/src/test/java/org/elasticsearch/xpack/monitoring/integration/MonitoringIT.java b/plugin/src/test/java/org/elasticsearch/xpack/monitoring/integration/MonitoringIT.java index 6045695d0a9..c803594bb05 100644 --- a/plugin/src/test/java/org/elasticsearch/xpack/monitoring/integration/MonitoringIT.java +++ b/plugin/src/test/java/org/elasticsearch/xpack/monitoring/integration/MonitoringIT.java @@ -11,6 +11,7 @@ import org.apache.http.entity.ContentType; import org.apache.http.entity.StringEntity; import org.apache.http.nio.entity.NStringEntity; import org.apache.lucene.util.Constants; +import org.apache.lucene.util.LuceneTestCase; import org.elasticsearch.ElasticsearchException; import org.elasticsearch.Version; import org.elasticsearch.client.Response; @@ -63,6 +64,7 @@ import static org.hamcrest.Matchers.not; import static org.hamcrest.Matchers.notNullValue; import static org.hamcrest.Matchers.nullValue; +@LuceneTestCase.AwaitsFix(bugUrl = "https://github.com/elastic/x-pack-elasticsearch/issues/2609") public class MonitoringIT extends ESRestTestCase { private static final String BASIC_AUTH_VALUE = basicAuthHeaderValue("x_pack_rest_user", TEST_PASSWORD_SECURE_STRING); @@ -410,7 +412,7 @@ public class MonitoringIT extends ESRestTestCase { assertEquals(5, source.size()); final Map nodeStats = (Map) source.get(NodeStatsMonitoringDoc.TYPE); - assertEquals(9, nodeStats.size()); + assertEquals(Constants.WINDOWS ? 8 : 9, nodeStats.size()); NodeStatsMonitoringDoc.XCONTENT_FILTERS.forEach(filter -> { if (Constants.WINDOWS && filter.startsWith("node_stats.os.cpu.load_average")) { @@ -650,6 +652,6 @@ public class MonitoringIT extends ESRestTestCase { * Returns a {@link MonitoredSystem} supported by the Monitoring Bulk API */ private static MonitoredSystem randomSystem() { - return randomFrom(MonitoredSystem.LOGSTASH, MonitoredSystem.KIBANA, MonitoredSystem.BEATS); + return randomFrom(MonitoredSystem.LOGSTASH, MonitoredSystem.KIBANA); } } diff --git a/plugin/src/test/java/org/elasticsearch/xpack/notification/jira/JiraAccountTests.java b/plugin/src/test/java/org/elasticsearch/xpack/notification/jira/JiraAccountTests.java index c5448d4b76e..3c98660037f 100644 --- a/plugin/src/test/java/org/elasticsearch/xpack/notification/jira/JiraAccountTests.java +++ b/plugin/src/test/java/org/elasticsearch/xpack/notification/jira/JiraAccountTests.java @@ -216,7 +216,7 @@ public class JiraAccountTests extends ESTestCase { for (Map.Entry setting : defaults.entrySet()) { String key = "xpack.notification.jira.account." + name + "." + JiraAccount.ISSUE_DEFAULTS_SETTING + "." + setting.getKey(); if (setting.getValue() instanceof String) { - builder.put(key, setting.getValue()); + builder.put(key, setting.getValue().toString()); } else if (setting.getValue() instanceof Map) { builder.putProperties((Map) setting.getValue(), s -> key + "." + s); } diff --git a/plugin/src/test/java/org/elasticsearch/xpack/security/SecurityTribeIT.java b/plugin/src/test/java/org/elasticsearch/xpack/security/SecurityTribeIT.java index 36ca4f95b57..d8099b5d3d1 100644 --- a/plugin/src/test/java/org/elasticsearch/xpack/security/SecurityTribeIT.java +++ b/plugin/src/test/java/org/elasticsearch/xpack/security/SecurityTribeIT.java @@ -226,17 +226,19 @@ public class SecurityTribeIT extends NativeRealmIntegTestCase { } }; final Settings settingsTemplate = cluster2SettingsSource.nodeSettings(0); - - Map asMap = new HashMap<>(settingsTemplate.getAsMap()); - asMap.remove(NodeEnvironment.MAX_LOCAL_STORAGE_NODES_SETTING.getKey()); Settings.Builder tribe1Defaults = Settings.builder(); Settings.Builder tribe2Defaults = Settings.builder(); - for (Map.Entry entry : asMap.entrySet()) { - if (entry.getKey().startsWith("path.")) { - continue; - } else if (entry.getKey().equals("transport.tcp.port")) { - continue; + Settings tribeSettings = settingsTemplate.filter(k -> { + if (k.equals(NodeEnvironment.MAX_LOCAL_STORAGE_NODES_SETTING.getKey())) { + return false; + } if (k.startsWith("path.")) { + return false; + } else if (k.equals("transport.tcp.port")) { + return false; } + return true; + }); + for (Map.Entry entry : tribeSettings.getAsMap().entrySet()) { tribe1Defaults.put("tribe.t1." + entry.getKey(), entry.getValue()); tribe2Defaults.put("tribe.t2." + entry.getKey(), entry.getValue()); } @@ -255,7 +257,7 @@ public class SecurityTribeIT extends NativeRealmIntegTestCase { Settings merged = Settings.builder() .put(internalCluster().getDefaultSettings()) - .put(asMap) + .put(tribeSettings, false) .put("tribe.t1.cluster.name", internalCluster().getClusterName()) .put("tribe.t2.cluster.name", cluster2.getClusterName()) .put("tribe.blocks.write", false) diff --git a/plugin/src/test/java/org/elasticsearch/xpack/security/authz/privilege/PrivilegeTests.java b/plugin/src/test/java/org/elasticsearch/xpack/security/authz/privilege/PrivilegeTests.java index 0c365c695a0..f6a5571a418 100644 --- a/plugin/src/test/java/org/elasticsearch/xpack/security/authz/privilege/PrivilegeTests.java +++ b/plugin/src/test/java/org/elasticsearch/xpack/security/authz/privilege/PrivilegeTests.java @@ -123,5 +123,8 @@ public class PrivilegeTests extends ESTestCase { assertThat(predicate.test("indices:admin/mapping/put"), is(true)); assertThat(predicate.test("indices:admin/mapping/whatever"), is(false)); assertThat(predicate.test("internal:transport/proxy/indices:data/read/query"), is(false)); + assertThat(predicate.test("indices:admin/seq_no/global_checkpoint_sync"), is(true)); + assertThat(predicate.test("indices:admin/seq_no/global_checkpoint_sync[p]"), is(true)); + assertThat(predicate.test("indices:admin/seq_no/global_checkpoint_sync[r]"), is(true)); } } diff --git a/plugin/src/test/java/org/elasticsearch/xpack/security/transport/ServerTransportFilterIntegrationTests.java b/plugin/src/test/java/org/elasticsearch/xpack/security/transport/ServerTransportFilterIntegrationTests.java index cf885973adf..f8dde4c7806 100644 --- a/plugin/src/test/java/org/elasticsearch/xpack/security/transport/ServerTransportFilterIntegrationTests.java +++ b/plugin/src/test/java/org/elasticsearch/xpack/security/transport/ServerTransportFilterIntegrationTests.java @@ -102,7 +102,6 @@ public class ServerTransportFilterIntegrationTests extends SecurityIntegTestCase // test that starting up a node works Settings.Builder nodeSettings = Settings.builder() - .put() .put("node.name", "my-test-node") .put("network.host", "localhost") .put("cluster.name", internalCluster().getClusterName()) diff --git a/plugin/src/test/java/org/elasticsearch/xpack/security/transport/netty4/DNSOnlyHostnameVerificationTests.java b/plugin/src/test/java/org/elasticsearch/xpack/security/transport/netty4/DNSOnlyHostnameVerificationTests.java index 288d23d43fc..d97e6df367c 100644 --- a/plugin/src/test/java/org/elasticsearch/xpack/security/transport/netty4/DNSOnlyHostnameVerificationTests.java +++ b/plugin/src/test/java/org/elasticsearch/xpack/security/transport/netty4/DNSOnlyHostnameVerificationTests.java @@ -95,7 +95,7 @@ public class DNSOnlyHostnameVerificationTests extends SecurityIntegTestCase { public Settings nodeSettings(int nodeOrdinal) { Settings defaultSettings = super.nodeSettings(nodeOrdinal); Settings.Builder builder = Settings.builder() - .put(defaultSettings.filter((s) -> s.startsWith("xpack.ssl.") == false).getAsMap()) + .put(defaultSettings.filter((s) -> s.startsWith("xpack.ssl.") == false), false) .put("transport.host", hostName); Path keystorePath = nodeConfigPath(nodeOrdinal).resolve("keystore.jks"); try (OutputStream os = Files.newOutputStream(keystorePath)) { diff --git a/plugin/src/test/java/org/elasticsearch/xpack/security/transport/netty4/IPHostnameVerificationTests.java b/plugin/src/test/java/org/elasticsearch/xpack/security/transport/netty4/IPHostnameVerificationTests.java index 3b3139d121c..8b859d12a91 100644 --- a/plugin/src/test/java/org/elasticsearch/xpack/security/transport/netty4/IPHostnameVerificationTests.java +++ b/plugin/src/test/java/org/elasticsearch/xpack/security/transport/netty4/IPHostnameVerificationTests.java @@ -31,7 +31,7 @@ public class IPHostnameVerificationTests extends SecurityIntegTestCase { protected Settings nodeSettings(int nodeOrdinal) { Settings settings = super.nodeSettings(nodeOrdinal); Settings.Builder builder = Settings.builder() - .put(settings.filter((s) -> s.startsWith("xpack.ssl.") == false).getAsMap()); + .put(settings.filter((s) -> s.startsWith("xpack.ssl.") == false), false); settings = builder.build(); // The default Unicast test behavior is to use 'localhost' with the port number. For this test we need to use IP diff --git a/plugin/src/test/resources/org/elasticsearch/transport/actions b/plugin/src/test/resources/org/elasticsearch/transport/actions index a44e33d0fb7..bc5d92c5999 100644 --- a/plugin/src/test/resources/org/elasticsearch/transport/actions +++ b/plugin/src/test/resources/org/elasticsearch/transport/actions @@ -162,3 +162,5 @@ indices:data/write/delete/byquery indices:data/write/reindex cluster:admin/xpack/deprecation/info cluster:admin/xpack/ml/job/forecast +cluster:admin/xpack/license/start_trial +cluster:admin/xpack/license/trial_status diff --git a/plugin/src/test/resources/rest-api-spec/api/xpack.license.get_trial_status.json b/plugin/src/test/resources/rest-api-spec/api/xpack.license.get_trial_status.json new file mode 100644 index 00000000000..9824b022405 --- /dev/null +++ b/plugin/src/test/resources/rest-api-spec/api/xpack.license.get_trial_status.json @@ -0,0 +1,15 @@ +{ + "xpack.license.get_trial_status": { + "documentation": "https://www.elastic.co/guide/en/x-pack/current/license-management.html", + "methods": ["GET"], + "url": { + "path": "/_xpack/license/trial_status", + "paths": ["/_xpack/license/trial_status"], + "parts" : { + }, + "params": { + } + }, + "body": null + } +} \ No newline at end of file diff --git a/plugin/src/test/resources/rest-api-spec/api/xpack.license.post_start_trial.json b/plugin/src/test/resources/rest-api-spec/api/xpack.license.post_start_trial.json new file mode 100644 index 00000000000..ea0d294b1d3 --- /dev/null +++ b/plugin/src/test/resources/rest-api-spec/api/xpack.license.post_start_trial.json @@ -0,0 +1,15 @@ +{ + "xpack.license.post_start_trial": { + "documentation": "https://www.elastic.co/guide/en/x-pack/current/license-management.html", + "methods": ["POST"], + "url": { + "path": "/_xpack/license/start_trial", + "paths": ["/_xpack/license/start_trial"], + "parts" : { + }, + "params": { + } + }, + "body": null + } +} diff --git a/plugin/src/test/resources/rest-api-spec/test/monitoring/bulk/10_basic.yml b/plugin/src/test/resources/rest-api-spec/test/monitoring/bulk/10_basic.yml index 74d3680e0d2..96b78478c67 100644 --- a/plugin/src/test/resources/rest-api-spec/test/monitoring/bulk/10_basic.yml +++ b/plugin/src/test/resources/rest-api-spec/test/monitoring/bulk/10_basic.yml @@ -161,7 +161,7 @@ - do: xpack.monitoring.bulk: - system_id: "beats" + system_id: "logstash" system_api_version: "6" interval: "5s" body: @@ -183,17 +183,17 @@ - do: search: - index: .monitoring-beats-* + index: .monitoring-logstash-* - match: { hits.total: 2 } - do: indices.close: - index: .monitoring-beats-* + index: .monitoring-logstash-* - do: catch: /export_exception/ xpack.monitoring.bulk: - system_id: "beats" + system_id: "logstash" system_api_version: "6" interval: "5s" body: diff --git a/qa/security-example-extension/src/test/java/org/elasticsearch/example/realm/CustomRealmIT.java b/qa/security-example-extension/src/test/java/org/elasticsearch/example/realm/CustomRealmIT.java index 04360ec535d..3924ee332ff 100644 --- a/qa/security-example-extension/src/test/java/org/elasticsearch/example/realm/CustomRealmIT.java +++ b/qa/security-example-extension/src/test/java/org/elasticsearch/example/realm/CustomRealmIT.java @@ -38,7 +38,7 @@ public class CustomRealmIT extends ESIntegTestCase { protected Settings externalClusterClientSettings() { return Settings.builder() .put(ThreadContext.PREFIX + "." + CustomRealm.USER_HEADER, CustomRealm.KNOWN_USER) - .put(ThreadContext.PREFIX + "." + CustomRealm.PW_HEADER, CustomRealm.KNOWN_PW) + .put(ThreadContext.PREFIX + "." + CustomRealm.PW_HEADER, CustomRealm.KNOWN_PW.toString()) .put(NetworkModule.TRANSPORT_TYPE_KEY, "security4") .build(); } @@ -78,7 +78,7 @@ public class CustomRealmIT extends ESIntegTestCase { .put("cluster.name", clusterName) .put(Environment.PATH_HOME_SETTING.getKey(), createTempDir().toAbsolutePath().toString()) .put(ThreadContext.PREFIX + "." + CustomRealm.USER_HEADER, CustomRealm.KNOWN_USER) - .put(ThreadContext.PREFIX + "." + CustomRealm.PW_HEADER, CustomRealm.KNOWN_PW) + .put(ThreadContext.PREFIX + "." + CustomRealm.PW_HEADER, CustomRealm.KNOWN_PW.toString()) .build(); try (TransportClient client = new PreBuiltXPackTransportClient(settings)) { client.addTransportAddress(publishAddress); @@ -98,7 +98,7 @@ public class CustomRealmIT extends ESIntegTestCase { .put("cluster.name", clusterName) .put(Environment.PATH_HOME_SETTING.getKey(), createTempDir().toAbsolutePath().toString()) .put(ThreadContext.PREFIX + "." + CustomRealm.USER_HEADER, CustomRealm.KNOWN_USER + randomAlphaOfLength(1)) - .put(ThreadContext.PREFIX + "." + CustomRealm.PW_HEADER, CustomRealm.KNOWN_PW) + .put(ThreadContext.PREFIX + "." + CustomRealm.PW_HEADER, CustomRealm.KNOWN_PW.toString()) .build(); try (TransportClient client = new PreBuiltXPackTransportClient(settings)) { client.addTransportAddress(publishAddress); diff --git a/qa/security-example-extension/src/test/java/org/elasticsearch/example/role/CustomRolesProviderIT.java b/qa/security-example-extension/src/test/java/org/elasticsearch/example/role/CustomRolesProviderIT.java index eef08e140a3..4fcda0527d6 100644 --- a/qa/security-example-extension/src/test/java/org/elasticsearch/example/role/CustomRolesProviderIT.java +++ b/qa/security-example-extension/src/test/java/org/elasticsearch/example/role/CustomRolesProviderIT.java @@ -40,7 +40,7 @@ public class CustomRolesProviderIT extends ESIntegTestCase { protected Settings externalClusterClientSettings() { return Settings.builder() .put(ThreadContext.PREFIX + "." + CustomRealm.USER_HEADER, CustomRealm.KNOWN_USER) - .put(ThreadContext.PREFIX + "." + CustomRealm.PW_HEADER, CustomRealm.KNOWN_PW) + .put(ThreadContext.PREFIX + "." + CustomRealm.PW_HEADER, CustomRealm.KNOWN_PW.toString()) .put(NetworkModule.TRANSPORT_TYPE_KEY, "security4") .build(); } diff --git a/qa/tribe-tests-with-license/src/test/java/org/elasticsearch/test/LicensingTribeIT.java b/qa/tribe-tests-with-license/src/test/java/org/elasticsearch/test/LicensingTribeIT.java index 7306e33ea04..c2f46379ebb 100644 --- a/qa/tribe-tests-with-license/src/test/java/org/elasticsearch/test/LicensingTribeIT.java +++ b/qa/tribe-tests-with-license/src/test/java/org/elasticsearch/test/LicensingTribeIT.java @@ -5,6 +5,7 @@ */ package org.elasticsearch.test; +import org.elasticsearch.Build; import org.elasticsearch.common.bytes.BytesArray; import org.elasticsearch.common.settings.Settings; import org.elasticsearch.common.transport.TransportAddress; @@ -101,6 +102,7 @@ public class LicensingTribeIT extends ESIntegTestCase { } public void testLicensePropagateToTribeNode() throws Exception { + assumeTrue("License is only valid when tested against snapshot/test keys", Build.CURRENT.isSnapshot()); // test that auto-generated trial license propagates to tribe assertBusy(() -> { GetLicenseResponse getLicenseResponse = new LicensingClient(tribeNode.client()).prepareGetLicense().get();