initial merge of license
Original commit: elastic/x-pack-elasticsearch@d520d6be57
This commit is contained in:
parent
082406f364
commit
dbb9c5d918
36
README.md
36
README.md
|
@ -1,36 +0,0 @@
|
|||
elasticsearch-license
|
||||
=====================
|
||||
|
||||
Elasticsearch Licensing core, tools and plugin
|
||||
|
||||
## Core
|
||||
|
||||
Contains core data structures, utilities used by **Licensor** and **Plugin**.
|
||||
|
||||
See `core/` and `core-shaded/`
|
||||
|
||||
## Licensor
|
||||
|
||||
Contains a collection of tools to generate key-pairs, licenses and validate licenses.
|
||||
|
||||
See `licensor/`
|
||||
|
||||
see [wiki] (https://github.com/elasticsearch/elasticsearch-license/wiki) for documentation on
|
||||
[Licensing Tools Usage & Reference] (https://github.com/elasticsearch/elasticsearch-license/wiki/License-Tools-Usage-&-Reference)
|
||||
|
||||
## Plugin
|
||||
|
||||
**NOTE**: The license plugin has to be packaged with the right public key when being deployed to public repositories in maven
|
||||
or uploaded to s3. Use `-Dkeys.path=<PATH_TO_KEY_DIR>` with maven command to package the plugin with a specified key.
|
||||
|
||||
See `plugin/`
|
||||
|
||||
see [Getting Started] (https://github.com/elasticsearch/elasticsearch-license/blob/master/docs/getting-started.asciidoc) to install license plugin.
|
||||
|
||||
see [Licensing REST APIs] (https://github.com/elasticsearch/elasticsearch-license/blob/master/docs/license.asciidoc)
|
||||
to use the license plugin from an elasticsearch deployment.
|
||||
|
||||
see [wiki] (https://github.com/elasticsearch/elasticsearch-license/wiki) for documentation on
|
||||
- [License Plugin Consumer Interface] (https://github.com/elasticsearch/elasticsearch-license/wiki/License---Consumer-Interface)
|
||||
- [License Plugin Release Process] (https://github.com/elasticsearch/elasticsearch-license/wiki/Plugin-Release-Process)
|
||||
- [License Plugin Design] (https://github.com/elasticsearch/elasticsearch-license/wiki/License-Plugin--Design)
|
17
core/pom.xml
17
core/pom.xml
|
@ -1,17 +0,0 @@
|
|||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<project xmlns="http://maven.apache.org/POM/4.0.0"
|
||||
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
|
||||
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
|
||||
<parent>
|
||||
<artifactId>elasticsearch-license</artifactId>
|
||||
<groupId>org.elasticsearch</groupId>
|
||||
<version>2.0.0.beta1-SNAPSHOT</version>
|
||||
</parent>
|
||||
<modelVersion>4.0.0</modelVersion>
|
||||
<properties>
|
||||
<license.basedir combine.self="override">${project.parent.basedir}</license.basedir>
|
||||
<!-- we aren't really a plugin... -->
|
||||
<skip.integ.tests>true</skip.integ.tests>
|
||||
</properties>
|
||||
<artifactId>elasticsearch-license-core</artifactId>
|
||||
</project>
|
|
@ -1,245 +0,0 @@
|
|||
/*
|
||||
* 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.core;
|
||||
|
||||
|
||||
import org.elasticsearch.common.Base64;
|
||||
|
||||
import javax.crypto.*;
|
||||
import javax.crypto.spec.PBEKeySpec;
|
||||
import javax.crypto.spec.SecretKeySpec;
|
||||
import java.nio.charset.StandardCharsets;
|
||||
import java.security.*;
|
||||
import java.security.spec.InvalidKeySpecException;
|
||||
import java.security.spec.PKCS8EncodedKeySpec;
|
||||
import java.security.spec.X509EncodedKeySpec;
|
||||
|
||||
public class CryptUtils {
|
||||
private static final int minimumPadding = 20;
|
||||
private static final byte[] salt = {
|
||||
(byte) 0xA9, (byte) 0xA2, (byte) 0xB5, (byte) 0xDE,
|
||||
(byte) 0x2A, (byte) 0x8A, (byte) 0x9A, (byte) 0xE6
|
||||
};
|
||||
private static final int iterationCount = 1024;
|
||||
private static final int aesKeyLength = 128;
|
||||
private static final String keyAlgorithm = "RSA";
|
||||
private static final String passHashAlgorithm = "SHA-512";
|
||||
private static final String DEFAULT_PASS_PHRASE = "elasticsearch-license";
|
||||
|
||||
private static final SecureRandom random = new SecureRandom();
|
||||
|
||||
/**
|
||||
* Read encrypted private key file content with default pass phrase
|
||||
*/
|
||||
public static PrivateKey readEncryptedPrivateKey(byte[] fileContents) {
|
||||
try {
|
||||
return readEncryptedPrivateKey(fileContents, hashPassPhrase(DEFAULT_PASS_PHRASE));
|
||||
} catch (NoSuchAlgorithmException e) {
|
||||
throw new IllegalStateException(e);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Read encrypted public key file content with default pass phrase
|
||||
*/
|
||||
public static PublicKey readEncryptedPublicKey(byte[] fileContents) {
|
||||
try {
|
||||
return readEncryptedPublicKey(fileContents, hashPassPhrase(DEFAULT_PASS_PHRASE));
|
||||
} catch (NoSuchAlgorithmException e) {
|
||||
throw new IllegalStateException(e);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns encrypted public key file content with default pass phrase
|
||||
*/
|
||||
public static byte[] writeEncryptedPublicKey(PublicKey publicKey) {
|
||||
try {
|
||||
return writeEncryptedPublicKey(publicKey, hashPassPhrase(DEFAULT_PASS_PHRASE));
|
||||
} catch (NoSuchAlgorithmException e) {
|
||||
throw new IllegalStateException(e);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns encrypted private key file content with default pass phrase
|
||||
*/
|
||||
public static byte[] writeEncryptedPrivateKey(PrivateKey privateKey) {
|
||||
try {
|
||||
return writeEncryptedPrivateKey(privateKey, hashPassPhrase(DEFAULT_PASS_PHRASE));
|
||||
} catch (NoSuchAlgorithmException e) {
|
||||
throw new IllegalStateException(e);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Read encrypted private key file content with provided <code>passPhrase</code>
|
||||
*/
|
||||
public static PrivateKey readEncryptedPrivateKey(byte[] fileContents, char[] passPhrase) {
|
||||
PKCS8EncodedKeySpec privateKeySpec = new PKCS8EncodedKeySpec(decrypt(fileContents, passPhrase));
|
||||
try {
|
||||
return KeyFactory.getInstance(keyAlgorithm).generatePrivate(privateKeySpec);
|
||||
} catch (NoSuchAlgorithmException | InvalidKeySpecException e) {
|
||||
throw new IllegalStateException(e);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Read encrypted public key file content with provided <code>passPhrase</code>
|
||||
*/
|
||||
public static PublicKey readEncryptedPublicKey(byte[] fileContents, char[] passPhrase) {
|
||||
X509EncodedKeySpec publicKeySpec = new X509EncodedKeySpec(decrypt(fileContents, passPhrase));
|
||||
try {
|
||||
return KeyFactory.getInstance(CryptUtils.keyAlgorithm).generatePublic(publicKeySpec);
|
||||
} catch (NoSuchAlgorithmException | InvalidKeySpecException e) {
|
||||
throw new IllegalStateException(e);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns encrypted public key file content with provided <code>passPhrase</code>
|
||||
*/
|
||||
public static byte[] writeEncryptedPublicKey(PublicKey publicKey, char[] passPhrase) {
|
||||
X509EncodedKeySpec encodedKeySpec = new X509EncodedKeySpec(publicKey.getEncoded());
|
||||
return encrypt(encodedKeySpec.getEncoded(), passPhrase);
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns encrypted private key file content with provided <code>passPhrase</code>
|
||||
*/
|
||||
public static byte[] writeEncryptedPrivateKey(PrivateKey privateKey, char[] passPhrase) {
|
||||
PKCS8EncodedKeySpec encodedKeySpec = new PKCS8EncodedKeySpec(privateKey.getEncoded());
|
||||
return encrypt(encodedKeySpec.getEncoded(), passPhrase);
|
||||
}
|
||||
|
||||
/**
|
||||
* Encrypts provided <code>data</code> with <code>DEFAULT_PASS_PHRASE</code>
|
||||
*/
|
||||
public static byte[] encrypt(byte[] data) {
|
||||
try {
|
||||
return encrypt(data, hashPassPhrase(DEFAULT_PASS_PHRASE));
|
||||
} catch (NoSuchAlgorithmException e) {
|
||||
throw new IllegalStateException(e);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Decrypts provided <code>encryptedData</code> with <code>DEFAULT_PASS_PHRASE</code>
|
||||
*/
|
||||
public static byte[] decrypt(byte[] encryptedData) {
|
||||
try {
|
||||
return decrypt(encryptedData, hashPassPhrase(DEFAULT_PASS_PHRASE));
|
||||
} catch (NoSuchAlgorithmException e) {
|
||||
throw new IllegalStateException(e);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Encrypts provided <code>data</code> with <code>passPhrase</code>
|
||||
*/
|
||||
public static byte[] encrypt(byte[] data, char[] passPhrase) {
|
||||
try {
|
||||
final Cipher encryptionCipher = getEncryptionCipher(getSecretKey(passPhrase));
|
||||
return encryptionCipher.doFinal(pad(data, minimumPadding));
|
||||
} catch (InvalidKeySpecException | IllegalBlockSizeException | BadPaddingException e) {
|
||||
throw new IllegalStateException(e);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Decrypts provided <code>encryptedData</code> with <code>passPhrase</code>
|
||||
*/
|
||||
private static byte[] decrypt(byte[] encryptedData, char[] passPhrase) {
|
||||
try {
|
||||
final Cipher cipher = getDecryptionCipher(getSecretKey(passPhrase));
|
||||
return unPad(cipher.doFinal(encryptedData));
|
||||
} catch (IllegalBlockSizeException | BadPaddingException | InvalidKeySpecException e) {
|
||||
throw new IllegalStateException(e);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
private static SecretKey getSecretKey(char[] passPhrase) throws InvalidKeySpecException {
|
||||
try {
|
||||
PBEKeySpec keySpec = new PBEKeySpec(passPhrase, salt, iterationCount, aesKeyLength);
|
||||
|
||||
byte[] shortKey = SecretKeyFactory.getInstance("PBEWithSHA1AndDESede").
|
||||
generateSecret(keySpec).getEncoded();
|
||||
|
||||
byte[] intermediaryKey = new byte[aesKeyLength / 8];
|
||||
for (int i = 0, j = 0; i < aesKeyLength / 8; i++) {
|
||||
intermediaryKey[i] = shortKey[j];
|
||||
if (++j == shortKey.length)
|
||||
j = 0;
|
||||
}
|
||||
|
||||
return new SecretKeySpec(intermediaryKey, "AES");
|
||||
} catch (NoSuchAlgorithmException | InvalidKeySpecException e) {
|
||||
throw new IllegalStateException(e);
|
||||
}
|
||||
}
|
||||
|
||||
private static Cipher getEncryptionCipher(SecretKey secretKey) {
|
||||
return getCipher(Cipher.ENCRYPT_MODE, secretKey);
|
||||
}
|
||||
|
||||
private static Cipher getDecryptionCipher(SecretKey secretKey) {
|
||||
return getCipher(Cipher.DECRYPT_MODE, secretKey);
|
||||
}
|
||||
|
||||
private static Cipher getCipher(int mode, SecretKey secretKey) {
|
||||
try {
|
||||
Cipher cipher = Cipher.getInstance(secretKey.getAlgorithm());
|
||||
cipher.init(mode, secretKey, random);
|
||||
return cipher;
|
||||
} catch (NoSuchAlgorithmException | InvalidKeyException | NoSuchPaddingException e) {
|
||||
throw new IllegalStateException(e);
|
||||
}
|
||||
}
|
||||
|
||||
private static byte[] pad(byte[] bytes, int length) {
|
||||
if (bytes.length >= length) {
|
||||
byte[] out = new byte[bytes.length + 1];
|
||||
System.arraycopy(bytes, 0, out, 0, bytes.length);
|
||||
out[bytes.length] = (byte) 1;
|
||||
return out;
|
||||
}
|
||||
|
||||
byte[] out = new byte[length + 1];
|
||||
|
||||
int i = 0;
|
||||
for (; i < bytes.length; i++)
|
||||
out[i] = bytes[i];
|
||||
|
||||
int padded = length - i;
|
||||
|
||||
// fill the rest with random bytes
|
||||
byte[] fill = new byte[padded - 1];
|
||||
random.nextBytes(fill);
|
||||
System.arraycopy(fill, 0, out, i, padded - 1);
|
||||
|
||||
out[length] = (byte) (padded + 1);
|
||||
|
||||
return out;
|
||||
}
|
||||
|
||||
private static byte[] unPad(byte[] bytes) {
|
||||
int padded = (int) bytes[bytes.length - 1];
|
||||
int targetLength = bytes.length - padded;
|
||||
|
||||
byte[] out = new byte[targetLength];
|
||||
|
||||
System.arraycopy(bytes, 0, out, 0, targetLength);
|
||||
|
||||
return out;
|
||||
}
|
||||
|
||||
private static char[] hashPassPhrase(String passPhrase) throws NoSuchAlgorithmException {
|
||||
final byte[] passBytes = passPhrase.getBytes(StandardCharsets.UTF_8);
|
||||
final byte[] digest = MessageDigest.getInstance(passHashAlgorithm).digest(passBytes);
|
||||
return new String(Base64.encodeBytesToBytes(digest), StandardCharsets.UTF_8).toCharArray();
|
||||
}
|
||||
}
|
|
@ -1,44 +0,0 @@
|
|||
/*
|
||||
* 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.core;
|
||||
|
||||
import org.elasticsearch.common.joda.FormatDateTimeFormatter;
|
||||
import org.elasticsearch.common.joda.Joda;
|
||||
import org.joda.time.MutableDateTime;
|
||||
import org.joda.time.format.DateTimeFormatter;
|
||||
import org.joda.time.format.ISODateTimeFormat;
|
||||
|
||||
public class DateUtils {
|
||||
|
||||
private final static FormatDateTimeFormatter formatDateOnlyFormatter = Joda.forPattern("yyyy-MM-dd");
|
||||
|
||||
private final static DateTimeFormatter dateOnlyFormatter = formatDateOnlyFormatter.parser().withZoneUTC();
|
||||
|
||||
private final static DateTimeFormatter dateTimeFormatter = ISODateTimeFormat.dateTime().withZoneUTC();
|
||||
|
||||
public static long endOfTheDay(String date) {
|
||||
try {
|
||||
// Try parsing using complete date/time format
|
||||
return dateTimeFormatter.parseDateTime(date).getMillis();
|
||||
} catch (IllegalArgumentException ex) {
|
||||
// Fall back to the date only format
|
||||
MutableDateTime dateTime = dateOnlyFormatter.parseMutableDateTime(date);
|
||||
dateTime.millisOfDay().set(dateTime.millisOfDay().getMaximumValue());
|
||||
return dateTime.getMillis();
|
||||
}
|
||||
}
|
||||
|
||||
public static long beginningOfTheDay(String date) {
|
||||
try {
|
||||
// Try parsing using complete date/time format
|
||||
return dateTimeFormatter.parseDateTime(date).getMillis();
|
||||
} catch (IllegalArgumentException ex) {
|
||||
// Fall back to the date only format
|
||||
return dateOnlyFormatter.parseDateTime(date).getMillis();
|
||||
}
|
||||
|
||||
}
|
||||
}
|
|
@ -1,433 +0,0 @@
|
|||
/*
|
||||
* 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.core;
|
||||
|
||||
import org.elasticsearch.ElasticsearchException;
|
||||
import org.elasticsearch.ElasticsearchParseException;
|
||||
import org.elasticsearch.common.io.stream.StreamInput;
|
||||
import org.elasticsearch.common.io.stream.StreamOutput;
|
||||
import org.elasticsearch.common.xcontent.ToXContent;
|
||||
import org.elasticsearch.common.xcontent.XContentBuilder;
|
||||
import org.elasticsearch.common.xcontent.XContentBuilderString;
|
||||
import org.elasticsearch.common.xcontent.XContentParser;
|
||||
|
||||
import java.io.IOException;
|
||||
|
||||
/**
|
||||
* Data structure for license. Use {@link Builder} to build a license.
|
||||
* Provides serialization/deserialization & validation methods for license object
|
||||
*/
|
||||
public class License implements ToXContent {
|
||||
public final static int VERSION_START = 1;
|
||||
public final static int VERSION_CURRENT = VERSION_START;
|
||||
|
||||
private final String uid;
|
||||
private final String issuer;
|
||||
private final String issuedTo;
|
||||
private final long issueDate;
|
||||
private final String type;
|
||||
private final String subscriptionType;
|
||||
private final String feature;
|
||||
private final String signature;
|
||||
private final long expiryDate;
|
||||
private final int maxNodes;
|
||||
|
||||
private License(String uid, String issuer, String issuedTo, long issueDate, String type,
|
||||
String subscriptionType, String feature, String signature, long expiryDate, int maxNodes) {
|
||||
this.uid = uid;
|
||||
this.issuer = issuer;
|
||||
this.issuedTo = issuedTo;
|
||||
this.issueDate = issueDate;
|
||||
this.type = type;
|
||||
this.subscriptionType = subscriptionType;
|
||||
this.feature = feature;
|
||||
this.signature = signature;
|
||||
this.expiryDate = expiryDate;
|
||||
this.maxNodes = maxNodes;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* @return a unique identifier for a license
|
||||
*/
|
||||
public String uid() {
|
||||
return uid;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return type of the license [trial, subscription, internal]
|
||||
*/
|
||||
public String type() {
|
||||
return type;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return subscription type of the license [none, silver, gold, platinum]
|
||||
*/
|
||||
public String subscriptionType() {
|
||||
return subscriptionType;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return the issueDate in milliseconds
|
||||
*/
|
||||
public long issueDate() {
|
||||
return issueDate;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return the featureType for the license [shield, marvel]
|
||||
*/
|
||||
public String feature() {
|
||||
return feature;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return the expiry date in milliseconds
|
||||
*/
|
||||
public long expiryDate() {
|
||||
return expiryDate;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return the maximum number of nodes this license has been issued for
|
||||
*/
|
||||
public int maxNodes() {
|
||||
return maxNodes;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return a string representing the entity this licenses has been issued to
|
||||
*/
|
||||
public String issuedTo() {
|
||||
return issuedTo;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return a string representing the entity responsible for issuing this license (internal)
|
||||
*/
|
||||
public String issuer() {
|
||||
return issuer;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return a string representing the signature of the license used for license verification
|
||||
*/
|
||||
public String signature() {
|
||||
return signature;
|
||||
}
|
||||
|
||||
public void validate() {
|
||||
if (issuer == null) {
|
||||
throw new IllegalStateException("issuer can not be null");
|
||||
} else if (issuedTo == null) {
|
||||
throw new IllegalStateException("issuedTo can not be null");
|
||||
} else if (issueDate == -1) {
|
||||
throw new IllegalStateException("issueDate has to be set");
|
||||
} else if (type == null) {
|
||||
throw new IllegalStateException("type can not be null");
|
||||
} else if (subscriptionType == null) {
|
||||
throw new IllegalStateException("subscriptionType can not be null");
|
||||
} else if (uid == null) {
|
||||
throw new IllegalStateException("uid can not be null");
|
||||
} else if (feature == null) {
|
||||
throw new IllegalStateException("at least one feature has to be enabled");
|
||||
} else if (signature == null) {
|
||||
throw new IllegalStateException("signature can not be null");
|
||||
} else if (maxNodes == -1) {
|
||||
throw new IllegalStateException("maxNodes has to be set");
|
||||
} else if (expiryDate == -1) {
|
||||
throw new IllegalStateException("expiryDate has to be set");
|
||||
}
|
||||
}
|
||||
|
||||
static License readLicense(StreamInput in) throws IOException {
|
||||
int version = in.readVInt(); // Version for future extensibility
|
||||
if (version > VERSION_CURRENT) {
|
||||
throw new ElasticsearchException("Unknown license version found, please upgrade all nodes to the latest elasticsearch-license plugin");
|
||||
}
|
||||
Builder builder = builder();
|
||||
builder.uid(in.readString());
|
||||
builder.type(in.readString());
|
||||
builder.subscriptionType(in.readString());
|
||||
builder.issueDate(in.readLong());
|
||||
builder.feature(in.readString());
|
||||
builder.expiryDate(in.readLong());
|
||||
builder.maxNodes(in.readInt());
|
||||
builder.issuedTo(in.readString());
|
||||
builder.issuer(in.readString());
|
||||
builder.signature(in.readOptionalString());
|
||||
return builder.build();
|
||||
}
|
||||
|
||||
public void writeTo(StreamOutput out) throws IOException {
|
||||
out.writeVInt(VERSION_CURRENT);
|
||||
out.writeString(uid);
|
||||
out.writeString(type);
|
||||
out.writeString(subscriptionType);
|
||||
out.writeLong(issueDate);
|
||||
out.writeString(feature);
|
||||
out.writeLong(expiryDate);
|
||||
out.writeInt(maxNodes);
|
||||
out.writeString(issuedTo);
|
||||
out.writeString(issuer);
|
||||
out.writeOptionalString(signature);
|
||||
}
|
||||
|
||||
@Override
|
||||
public XContentBuilder toXContent(XContentBuilder builder, Params params) throws IOException {
|
||||
boolean licenseSpecMode = params.paramAsBoolean(Licenses.LICENSE_SPEC_VIEW_MODE, false);
|
||||
boolean restViewMode = params.paramAsBoolean(Licenses.REST_VIEW_MODE, false);
|
||||
boolean previouslyHumanReadable = builder.humanReadable();
|
||||
if (licenseSpecMode && restViewMode) {
|
||||
throw new IllegalArgumentException("can have either " + Licenses.REST_VIEW_MODE + " or " + Licenses.LICENSE_SPEC_VIEW_MODE);
|
||||
} else if (restViewMode) {
|
||||
if (!previouslyHumanReadable) {
|
||||
builder.humanReadable(true);
|
||||
}
|
||||
}
|
||||
builder.startObject();
|
||||
if (restViewMode) {
|
||||
String status = "active";
|
||||
long now = System.currentTimeMillis();
|
||||
if (issueDate > now) {
|
||||
status = "invalid";
|
||||
} else if (expiryDate < now) {
|
||||
status = "expired";
|
||||
}
|
||||
builder.field(XFields.STATUS, status);
|
||||
}
|
||||
builder.field(XFields.UID, uid);
|
||||
builder.field(XFields.TYPE, type);
|
||||
builder.field(XFields.SUBSCRIPTION_TYPE, subscriptionType);
|
||||
builder.dateValueField(XFields.ISSUE_DATE_IN_MILLIS, XFields.ISSUE_DATE, issueDate);
|
||||
builder.field(XFields.FEATURE, feature);
|
||||
builder.dateValueField(XFields.EXPIRY_DATE_IN_MILLIS, XFields.EXPIRY_DATE, expiryDate);
|
||||
builder.field(XFields.MAX_NODES, maxNodes);
|
||||
builder.field(XFields.ISSUED_TO, issuedTo);
|
||||
builder.field(XFields.ISSUER, issuer);
|
||||
if (!licenseSpecMode && !restViewMode && signature != null) {
|
||||
builder.field(XFields.SIGNATURE, signature);
|
||||
}
|
||||
builder.endObject();
|
||||
if (restViewMode) {
|
||||
builder.humanReadable(previouslyHumanReadable);
|
||||
}
|
||||
return builder;
|
||||
}
|
||||
|
||||
|
||||
final static class Fields {
|
||||
static final String STATUS = "status";
|
||||
static final String UID = "uid";
|
||||
static final String TYPE = "type";
|
||||
static final String SUBSCRIPTION_TYPE = "subscription_type";
|
||||
static final String ISSUE_DATE_IN_MILLIS = "issue_date_in_millis";
|
||||
static final String ISSUE_DATE = "issue_date";
|
||||
static final String FEATURE = "feature";
|
||||
static final String EXPIRY_DATE_IN_MILLIS = "expiry_date_in_millis";
|
||||
static final String EXPIRY_DATE = "expiry_date";
|
||||
static final String MAX_NODES = "max_nodes";
|
||||
static final String ISSUED_TO = "issued_to";
|
||||
static final String ISSUER = "issuer";
|
||||
static final String SIGNATURE = "signature";
|
||||
}
|
||||
|
||||
private final static class XFields {
|
||||
static final XContentBuilderString STATUS = new XContentBuilderString(Fields.STATUS);
|
||||
static final XContentBuilderString UID = new XContentBuilderString(Fields.UID);
|
||||
static final XContentBuilderString TYPE = new XContentBuilderString(Fields.TYPE);
|
||||
static final XContentBuilderString SUBSCRIPTION_TYPE = new XContentBuilderString(Fields.SUBSCRIPTION_TYPE);
|
||||
static final XContentBuilderString ISSUE_DATE_IN_MILLIS = new XContentBuilderString(Fields.ISSUE_DATE_IN_MILLIS);
|
||||
static final XContentBuilderString ISSUE_DATE = new XContentBuilderString(Fields.ISSUE_DATE);
|
||||
static final XContentBuilderString FEATURE = new XContentBuilderString(Fields.FEATURE);
|
||||
static final XContentBuilderString EXPIRY_DATE_IN_MILLIS = new XContentBuilderString(Fields.EXPIRY_DATE_IN_MILLIS);
|
||||
static final XContentBuilderString EXPIRY_DATE = new XContentBuilderString(Fields.EXPIRY_DATE);
|
||||
static final XContentBuilderString MAX_NODES = new XContentBuilderString(Fields.MAX_NODES);
|
||||
static final XContentBuilderString ISSUED_TO = new XContentBuilderString(Fields.ISSUED_TO);
|
||||
static final XContentBuilderString ISSUER = new XContentBuilderString(Fields.ISSUER);
|
||||
static final XContentBuilderString SIGNATURE = new XContentBuilderString(Fields.SIGNATURE);
|
||||
}
|
||||
|
||||
private static long parseDate(XContentParser parser, String description, boolean endOfTheDay) throws IOException {
|
||||
if (parser.currentToken() == XContentParser.Token.VALUE_NUMBER) {
|
||||
return parser.longValue();
|
||||
} else {
|
||||
try {
|
||||
if (endOfTheDay) {
|
||||
return DateUtils.endOfTheDay(parser.text());
|
||||
} else {
|
||||
return DateUtils.beginningOfTheDay(parser.text());
|
||||
}
|
||||
} catch (IllegalArgumentException ex) {
|
||||
throw new ElasticsearchParseException("invalid " + description + " date format " + parser.text());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public static Builder builder() {
|
||||
return new Builder();
|
||||
}
|
||||
|
||||
public static class Builder {
|
||||
private String uid;
|
||||
private String issuer;
|
||||
private String issuedTo;
|
||||
private long issueDate = -1;
|
||||
private String type;
|
||||
private String subscriptionType = "none";
|
||||
private String feature;
|
||||
private String signature;
|
||||
private long expiryDate = -1;
|
||||
private int maxNodes = -1;
|
||||
|
||||
|
||||
public Builder uid(String uid) {
|
||||
this.uid = uid;
|
||||
return this;
|
||||
}
|
||||
|
||||
public Builder issuer(String issuer) {
|
||||
this.issuer = issuer;
|
||||
return this;
|
||||
}
|
||||
|
||||
public Builder issuedTo(String issuedTo) {
|
||||
this.issuedTo = issuedTo;
|
||||
return this;
|
||||
}
|
||||
|
||||
public Builder issueDate(long issueDate) {
|
||||
this.issueDate = issueDate;
|
||||
return this;
|
||||
}
|
||||
|
||||
public Builder type(String type) {
|
||||
this.type = type;
|
||||
return this;
|
||||
}
|
||||
|
||||
public Builder subscriptionType(String subscriptionType) {
|
||||
this.subscriptionType = subscriptionType;
|
||||
return this;
|
||||
}
|
||||
|
||||
public Builder feature(String feature) {
|
||||
this.feature = feature;
|
||||
return this;
|
||||
}
|
||||
|
||||
public Builder expiryDate(long expiryDate) {
|
||||
this.expiryDate = expiryDate;
|
||||
return this;
|
||||
}
|
||||
|
||||
public Builder maxNodes(int maxNodes) {
|
||||
this.maxNodes = maxNodes;
|
||||
return this;
|
||||
}
|
||||
|
||||
public Builder signature(String signature) {
|
||||
if (signature != null) {
|
||||
this.signature = signature;
|
||||
}
|
||||
return this;
|
||||
}
|
||||
|
||||
public Builder fromLicenseSpec(License license, String signature) {
|
||||
return uid(license.uid())
|
||||
.issuedTo(license.issuedTo())
|
||||
.issueDate(license.issueDate())
|
||||
.type(license.type())
|
||||
.subscriptionType(license.subscriptionType())
|
||||
.feature(license.feature())
|
||||
.maxNodes(license.maxNodes())
|
||||
.expiryDate(license.expiryDate())
|
||||
.issuer(license.issuer())
|
||||
.signature(signature);
|
||||
}
|
||||
|
||||
public Builder fromXContent(XContentParser parser) throws IOException {
|
||||
XContentParser.Token token = parser.currentToken();
|
||||
if (token == XContentParser.Token.START_OBJECT) {
|
||||
while ((token = parser.nextToken()) != XContentParser.Token.END_OBJECT) {
|
||||
if (token == XContentParser.Token.FIELD_NAME) {
|
||||
String currentFieldName = parser.currentName();
|
||||
token = parser.nextToken();
|
||||
if (token.isValue()) {
|
||||
if (Fields.UID.equals(currentFieldName)) {
|
||||
uid(parser.text());
|
||||
} else if (Fields.TYPE.equals(currentFieldName)) {
|
||||
type(parser.text());
|
||||
} else if (Fields.SUBSCRIPTION_TYPE.equals(currentFieldName)) {
|
||||
subscriptionType(parser.text());
|
||||
} else if (Fields.ISSUE_DATE.equals(currentFieldName)) {
|
||||
issueDate(parseDate(parser, "issue", false));
|
||||
} else if (Fields.ISSUE_DATE_IN_MILLIS.equals(currentFieldName)) {
|
||||
issueDate(parser.longValue());
|
||||
} else if (Fields.FEATURE.equals(currentFieldName)) {
|
||||
feature(parser.text());
|
||||
} else if (Fields.EXPIRY_DATE.equals(currentFieldName)) {
|
||||
expiryDate(parseDate(parser, "expiration", true));
|
||||
} else if (Fields.EXPIRY_DATE_IN_MILLIS.equals(currentFieldName)) {
|
||||
expiryDate(parser.longValue());
|
||||
} else if (Fields.MAX_NODES.equals(currentFieldName)) {
|
||||
maxNodes(parser.intValue());
|
||||
} else if (Fields.ISSUED_TO.equals(currentFieldName)) {
|
||||
issuedTo(parser.text());
|
||||
} else if (Fields.ISSUER.equals(currentFieldName)) {
|
||||
issuer(parser.text());
|
||||
} else if (Fields.SIGNATURE.equals(currentFieldName)) {
|
||||
signature(parser.text());
|
||||
}
|
||||
// Ignore unknown elements - might be new version of license
|
||||
} else if (token == XContentParser.Token.START_ARRAY) {
|
||||
// It was probably created by newer version - ignoring
|
||||
parser.skipChildren();
|
||||
} else if (token == XContentParser.Token.START_OBJECT) {
|
||||
// It was probably created by newer version - ignoring
|
||||
parser.skipChildren();
|
||||
}
|
||||
}
|
||||
}
|
||||
} else {
|
||||
throw new ElasticsearchParseException("failed to parse licenses expected a license object");
|
||||
}
|
||||
return this;
|
||||
}
|
||||
|
||||
public License build() {
|
||||
return new License(uid, issuer, issuedTo, issueDate, type,
|
||||
subscriptionType, feature, signature, expiryDate, maxNodes);
|
||||
}
|
||||
|
||||
public Builder validate() {
|
||||
if (issuer == null) {
|
||||
throw new IllegalStateException("issuer can not be null");
|
||||
} else if (issuedTo == null) {
|
||||
throw new IllegalStateException("issuedTo can not be null");
|
||||
} else if (issueDate == -1) {
|
||||
throw new IllegalStateException("issueDate has to be set");
|
||||
} else if (type == null) {
|
||||
throw new IllegalStateException("type can not be null");
|
||||
} else if (subscriptionType == null) {
|
||||
throw new IllegalStateException("subscriptionType can not be null");
|
||||
} else if (uid == null) {
|
||||
throw new IllegalStateException("uid can not be null");
|
||||
} else if (feature == null) {
|
||||
throw new IllegalStateException("at least one feature has to be enabled");
|
||||
} else if (signature == null) {
|
||||
throw new IllegalStateException("signature can not be null");
|
||||
} else if (maxNodes == -1) {
|
||||
throw new IllegalStateException("maxNodes has to be set");
|
||||
} else if (expiryDate == -1) {
|
||||
throw new IllegalStateException("expiryDate has to be set");
|
||||
}
|
||||
return this;
|
||||
}
|
||||
}
|
||||
|
||||
}
|
|
@ -1,159 +0,0 @@
|
|||
/*
|
||||
* 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.core;
|
||||
|
||||
import org.elasticsearch.common.Base64;
|
||||
import org.elasticsearch.common.io.Streams;
|
||||
import org.elasticsearch.common.xcontent.ToXContent;
|
||||
import org.elasticsearch.common.xcontent.XContentBuilder;
|
||||
import org.elasticsearch.common.xcontent.XContentFactory;
|
||||
import org.elasticsearch.common.xcontent.XContentType;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.nio.ByteBuffer;
|
||||
import java.nio.file.Files;
|
||||
import java.nio.file.Path;
|
||||
import java.security.InvalidKeyException;
|
||||
import java.security.NoSuchAlgorithmException;
|
||||
import java.security.Signature;
|
||||
import java.security.SignatureException;
|
||||
import java.util.Arrays;
|
||||
import java.util.Collection;
|
||||
import java.util.Collections;
|
||||
|
||||
/**
|
||||
* Responsible for verifying signed licenses
|
||||
* The signed licenses are expected to have signatures with the appropriate spec
|
||||
* (see {@link org.elasticsearch.license.core.LicenseVerifier})
|
||||
* along with the appropriate encrypted public key
|
||||
*/
|
||||
public class LicenseVerifier {
|
||||
|
||||
/**
|
||||
* Verifies Licenses using {@link #verifyLicense(License, byte[])} using the public key from the
|
||||
* resources
|
||||
*/
|
||||
public static boolean verifyLicenses(final Collection<License> licenses) {
|
||||
final byte[] encryptedPublicKeyData = getPublicKeyContentFromResource("/public.key");
|
||||
try {
|
||||
for (License license : licenses) {
|
||||
if (!verifyLicense(license, encryptedPublicKeyData)) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
return true;
|
||||
} finally {
|
||||
Arrays.fill(encryptedPublicKeyData, (byte) 0);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Verifies Licenses using {@link #verifyLicense(License, byte[])} using the provided public key
|
||||
*/
|
||||
public static boolean verifyLicenses(final Collection<License> licenses, final Path publicKeyPath) throws IOException {
|
||||
final byte[] encryptedPublicKeyData = Files.readAllBytes(publicKeyPath);
|
||||
try {
|
||||
for (License license : licenses) {
|
||||
if (!verifyLicense(license, encryptedPublicKeyData)) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
return true;
|
||||
} finally {
|
||||
Arrays.fill(encryptedPublicKeyData, (byte) 0);
|
||||
}
|
||||
}
|
||||
|
||||
public static boolean verifyLicense(final License license) {
|
||||
final byte[] encryptedPublicKeyData = getPublicKeyContentFromResource("/public.key");
|
||||
try {
|
||||
return verifyLicense(license, encryptedPublicKeyData);
|
||||
} finally {
|
||||
Arrays.fill(encryptedPublicKeyData, (byte) 0);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* verifies the license content with the signature and ensures that an expected public key is used
|
||||
* @param license to verify
|
||||
* @return true if valid, false otherwise
|
||||
*/
|
||||
public static boolean verifyLicense(final License license, final byte[] encryptedPublicKeyData) {
|
||||
LicenseSignature licenseSignature = null;
|
||||
try {
|
||||
XContentBuilder contentBuilder = XContentFactory.contentBuilder(XContentType.JSON);
|
||||
license.toXContent(contentBuilder, new ToXContent.MapParams(Collections.singletonMap(Licenses.LICENSE_SPEC_VIEW_MODE, "true")));
|
||||
licenseSignature = parseSignature(license.signature());
|
||||
if(!verifyContent(encryptedPublicKeyData, contentBuilder.bytes().toBytes(), licenseSignature.contentSignature)) {
|
||||
return false;
|
||||
}
|
||||
final byte[] hash = Base64.encodeBytesToBytes(encryptedPublicKeyData);
|
||||
return Arrays.equals(hash, licenseSignature.hash);
|
||||
} catch (IOException e) {
|
||||
throw new IllegalStateException(e);
|
||||
} finally {
|
||||
if (licenseSignature != null) {
|
||||
licenseSignature.clear();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private static boolean verifyContent(byte[] encryptedPublicKeyData, byte[] data, byte[] contentSignature) {
|
||||
try {
|
||||
Signature rsa = Signature.getInstance("SHA512withRSA");
|
||||
rsa.initVerify(CryptUtils.readEncryptedPublicKey(encryptedPublicKeyData));
|
||||
rsa.update(data);
|
||||
return rsa.verify(contentSignature);
|
||||
} catch (NoSuchAlgorithmException | InvalidKeyException | SignatureException e) {
|
||||
throw new IllegalStateException(e);
|
||||
}
|
||||
}
|
||||
|
||||
private static byte[] getPublicKeyContentFromResource(String resource) {
|
||||
try {
|
||||
return Streams.copyToBytesFromClasspath(resource);
|
||||
} catch (IOException ex) {
|
||||
throw new IllegalStateException(ex);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Signature structure:
|
||||
* | VERSION | MAGIC | PUB_KEY_DIGEST | SIGNED_LICENSE_CONTENT |
|
||||
*/
|
||||
private static LicenseSignature parseSignature(String signature) throws IOException {
|
||||
byte[] signatureBytes = Base64.decode(signature);
|
||||
ByteBuffer byteBuffer = ByteBuffer.wrap(signatureBytes);
|
||||
int version = byteBuffer.getInt();
|
||||
int magicLen = byteBuffer.getInt();
|
||||
byte[] magic = new byte[magicLen];
|
||||
byteBuffer.get(magic);
|
||||
int hashLen = byteBuffer.getInt();
|
||||
byte[] hash = new byte[hashLen];
|
||||
byteBuffer.get(hash);
|
||||
int signedContentLen = byteBuffer.getInt();
|
||||
byte[] signedContent = new byte[signedContentLen];
|
||||
byteBuffer.get(signedContent);
|
||||
return new LicenseSignature(version, hash, signedContent);
|
||||
}
|
||||
|
||||
private static class LicenseSignature {
|
||||
private final int version;
|
||||
private final byte[] hash;
|
||||
private final byte[] contentSignature;
|
||||
|
||||
private LicenseSignature(int version, byte[] hash, byte[] contentSignature) {
|
||||
this.version = version;
|
||||
this.hash = hash;
|
||||
this.contentSignature = contentSignature;
|
||||
}
|
||||
|
||||
private void clear() {
|
||||
Arrays.fill(hash, (byte)0);
|
||||
Arrays.fill(contentSignature, (byte)0);
|
||||
}
|
||||
}
|
||||
}
|
|
@ -1,149 +0,0 @@
|
|||
/*
|
||||
* 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.core;
|
||||
|
||||
import com.google.common.collect.ImmutableMap;
|
||||
import org.elasticsearch.ElasticsearchParseException;
|
||||
import org.elasticsearch.common.io.stream.StreamInput;
|
||||
import org.elasticsearch.common.io.stream.StreamOutput;
|
||||
import org.elasticsearch.common.xcontent.*;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.nio.charset.StandardCharsets;
|
||||
import java.util.*;
|
||||
|
||||
/**
|
||||
* Utility class for operating on a collection of {@link License}s.
|
||||
* Provides serialization/deserialization methods and reduce
|
||||
* operations
|
||||
*/
|
||||
public final class Licenses {
|
||||
|
||||
/**
|
||||
* XContent param name to deserialize license(s) with
|
||||
* an additional <code>status</code> field, indicating whether a
|
||||
* particular license is 'active' or 'expired' and no signature
|
||||
* and in a human readable format
|
||||
*/
|
||||
public static final String REST_VIEW_MODE = "rest_view";
|
||||
|
||||
/**
|
||||
* XContent param name to deserialize license(s) with
|
||||
* no signature
|
||||
*/
|
||||
public static final String LICENSE_SPEC_VIEW_MODE = "license_spec_view";
|
||||
|
||||
private final static class Fields {
|
||||
static final String LICENSES = "licenses";
|
||||
}
|
||||
|
||||
private final static class XFields {
|
||||
static final XContentBuilderString LICENSES = new XContentBuilderString(Fields.LICENSES);
|
||||
}
|
||||
|
||||
private Licenses() {}
|
||||
|
||||
public static void toXContent(Collection<License> licenses, XContentBuilder builder, ToXContent.Params params) throws IOException {
|
||||
builder.startObject();
|
||||
builder.startArray(XFields.LICENSES);
|
||||
for (License license : licenses) {
|
||||
license.toXContent(builder, params);
|
||||
}
|
||||
builder.endArray();
|
||||
builder.endObject();
|
||||
}
|
||||
|
||||
public static List<License> fromSource(String content) throws IOException {
|
||||
return fromSource(content.getBytes(StandardCharsets.UTF_8), true);
|
||||
}
|
||||
|
||||
public static List<License> fromSource(byte[] bytes) throws IOException {
|
||||
return fromXContent(XContentFactory.xContent(bytes).createParser(bytes), true);
|
||||
}
|
||||
|
||||
public static List<License> fromSource(byte[] bytes, boolean verify) throws IOException {
|
||||
return fromXContent(XContentFactory.xContent(bytes).createParser(bytes), verify);
|
||||
}
|
||||
|
||||
public static List<License> fromXContent(XContentParser parser, boolean verify) throws IOException {
|
||||
List<License> licenses = new ArrayList<>();
|
||||
if (parser.nextToken() == XContentParser.Token.START_OBJECT) {
|
||||
if (parser.nextToken() == XContentParser.Token.FIELD_NAME) {
|
||||
String currentFieldName = parser.currentName();
|
||||
if (Fields.LICENSES.equals(currentFieldName)) {
|
||||
if (parser.nextToken() == XContentParser.Token.START_ARRAY) {
|
||||
while (parser.nextToken() != XContentParser.Token.END_ARRAY) {
|
||||
License.Builder builder = License.builder().fromXContent(parser);
|
||||
if (verify) {
|
||||
builder.validate();
|
||||
}
|
||||
licenses.add(builder.build());
|
||||
}
|
||||
} else {
|
||||
throw new ElasticsearchParseException("failed to parse licenses expected an array of licenses");
|
||||
}
|
||||
}
|
||||
// Ignore all other fields - might be created with new version
|
||||
} else {
|
||||
throw new ElasticsearchParseException("failed to parse licenses expected field");
|
||||
}
|
||||
} else {
|
||||
throw new ElasticsearchParseException("failed to parse licenses expected start object");
|
||||
}
|
||||
return licenses;
|
||||
}
|
||||
|
||||
public static List<License> readFrom(StreamInput in) throws IOException {
|
||||
int size = in.readVInt();
|
||||
List<License> licenses = new ArrayList<>(size);
|
||||
for (int i = 0; i < size; i++) {
|
||||
licenses.add(License.readLicense(in));
|
||||
}
|
||||
return licenses;
|
||||
}
|
||||
|
||||
public static void writeTo(List<License> licenses, StreamOutput out) throws IOException {
|
||||
out.writeVInt(licenses.size());
|
||||
for (License license : licenses) {
|
||||
license.writeTo(out);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Given a set of {@link License}s, reduces the set to one license per feature.
|
||||
* Uses {@link #putIfAppropriate(java.util.Map, License)} to reduce.
|
||||
*
|
||||
* @param licensesSet a set of licenses to be reduced
|
||||
* @return a map of (feature, license)
|
||||
*/
|
||||
public static ImmutableMap<String, License> reduceAndMap(Set<License> licensesSet) {
|
||||
Map<String, License> map = new HashMap<>(licensesSet.size());
|
||||
for (License license : licensesSet) {
|
||||
putIfAppropriate(map, license);
|
||||
}
|
||||
return ImmutableMap.copyOf(map);
|
||||
}
|
||||
|
||||
/**
|
||||
* Adds or updates the <code>license</code> to <code>licenseMap</code> if the <code>license</code>
|
||||
* has not expired already and if the <code>license</code> has a later expiry date from any
|
||||
* existing licenses in <code>licenseMap</code> for the same feature
|
||||
*
|
||||
* @param licenseMap a map of (feature, license)
|
||||
* @param license a new license to be added to <code>licenseMap</code>
|
||||
*/
|
||||
private static void putIfAppropriate(Map<String, License> licenseMap, License license) {
|
||||
final String featureType = license.feature();
|
||||
if (licenseMap.containsKey(featureType)) {
|
||||
final License previousLicense = licenseMap.get(featureType);
|
||||
if (license.expiryDate() > previousLicense.expiryDate()) {
|
||||
licenseMap.put(featureType, license);
|
||||
}
|
||||
} else {
|
||||
licenseMap.put(featureType, license);
|
||||
}
|
||||
}
|
||||
}
|
|
@ -1,115 +0,0 @@
|
|||
/*
|
||||
* 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.core;
|
||||
|
||||
import com.google.common.collect.ImmutableMap;
|
||||
import org.elasticsearch.common.xcontent.*;
|
||||
import org.elasticsearch.test.ElasticsearchTestCase;
|
||||
import org.junit.Test;
|
||||
|
||||
import java.nio.charset.StandardCharsets;
|
||||
import java.util.*;
|
||||
|
||||
import static org.hamcrest.core.IsEqual.equalTo;
|
||||
import static org.hamcrest.core.IsNull.notNullValue;
|
||||
import static org.hamcrest.core.IsNull.nullValue;
|
||||
|
||||
public class LicenseSerializationTests extends ElasticsearchTestCase {
|
||||
|
||||
@Test
|
||||
public void testSimpleIssueExpiryDate() throws Exception {
|
||||
long now = System.currentTimeMillis();
|
||||
String issueDate = TestUtils.dateMathString("now", now);
|
||||
String expiryDate = TestUtils.dateMathString("now+10d/d", now);
|
||||
String licenseSpecs = TestUtils.generateLicenseSpecString(Arrays.asList(new TestUtils.LicenseSpec("shield", issueDate, expiryDate)));
|
||||
Set<License> licensesOutput = new HashSet<>(Licenses.fromSource(licenseSpecs.getBytes(StandardCharsets.UTF_8), false));
|
||||
License generatedLicense = licensesOutput.iterator().next();
|
||||
|
||||
assertThat(licensesOutput.size(), equalTo(1));
|
||||
assertThat(generatedLicense.issueDate(), equalTo(DateUtils.beginningOfTheDay(issueDate)));
|
||||
assertThat(generatedLicense.expiryDate(), equalTo(DateUtils.endOfTheDay(expiryDate)));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testMultipleIssueExpiryDate() throws Exception {
|
||||
long now = System.currentTimeMillis();
|
||||
String shieldIssueDate = TestUtils.dateMathString("now", now);
|
||||
String shieldExpiryDate = TestUtils.dateMathString("now+30d/d", now);
|
||||
String marvelIssueDate = TestUtils.dateMathString("now", now);
|
||||
String marvelExpiryDate = TestUtils.dateMathString("now+60d/d", now);
|
||||
String licenseSpecs = TestUtils.generateLicenseSpecString(Arrays.asList(new TestUtils.LicenseSpec("shield", shieldIssueDate, shieldExpiryDate)));
|
||||
String licenseSpecs1 = TestUtils.generateLicenseSpecString(Arrays.asList(new TestUtils.LicenseSpec("marvel", marvelIssueDate, marvelExpiryDate)));
|
||||
Set<License> licensesOutput = new HashSet<>();
|
||||
licensesOutput.addAll(Licenses.fromSource(licenseSpecs.getBytes(StandardCharsets.UTF_8), false));
|
||||
licensesOutput.addAll(Licenses.fromSource(licenseSpecs1.getBytes(StandardCharsets.UTF_8), false));
|
||||
assertThat(licensesOutput.size(), equalTo(2));
|
||||
for (License license : licensesOutput) {
|
||||
assertThat(license.issueDate(), equalTo(DateUtils.beginningOfTheDay((license.feature().equals("shield")) ? shieldIssueDate : marvelIssueDate)));
|
||||
assertThat(license.expiryDate(), equalTo(DateUtils.endOfTheDay((license.feature().equals("shield")) ? shieldExpiryDate : marvelExpiryDate)));
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testLicensesFields() throws Exception {
|
||||
Map<String, TestUtils.LicenseSpec> licenseSpecs = new HashMap<>();
|
||||
for (int i = 0; i < randomIntBetween(1, 5); i++) {
|
||||
TestUtils.LicenseSpec randomLicenseSpec = TestUtils.generateRandomLicenseSpec();
|
||||
licenseSpecs.put(randomLicenseSpec.feature, randomLicenseSpec);
|
||||
}
|
||||
|
||||
ArrayList<TestUtils.LicenseSpec> specs = new ArrayList<>(licenseSpecs.values());
|
||||
String licenseSpecsSource = TestUtils.generateLicenseSpecString(specs);
|
||||
Set<License> licensesOutput = new HashSet<>(Licenses.fromSource(licenseSpecsSource.getBytes(StandardCharsets.UTF_8), false));
|
||||
assertThat(licensesOutput.size(), equalTo(licenseSpecs.size()));
|
||||
|
||||
for (License license : licensesOutput) {
|
||||
TestUtils.LicenseSpec spec = licenseSpecs.get(license.feature());
|
||||
assertThat(spec, notNullValue());
|
||||
TestUtils.assertLicenseSpec(spec, license);
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testLicenseRestView() throws Exception {
|
||||
long now = System.currentTimeMillis();
|
||||
String expiredLicenseExpiryDate = TestUtils.dateMathString("now-1d/d", now);
|
||||
String validLicenseIssueDate = TestUtils.dateMathString("now-10d/d", now);
|
||||
String invalidLicenseIssueDate = TestUtils.dateMathString("now+1d/d", now);
|
||||
String validLicenseExpiryDate = TestUtils.dateMathString("now+2d/d", now);
|
||||
|
||||
Set<License> licenses = TestUtils.generateLicenses(Arrays.asList(new TestUtils.LicenseSpec("expired_feature", validLicenseIssueDate, expiredLicenseExpiryDate)
|
||||
, new TestUtils.LicenseSpec("valid_feature", validLicenseIssueDate, validLicenseExpiryDate),
|
||||
new TestUtils.LicenseSpec("invalid_feature", invalidLicenseIssueDate, validLicenseExpiryDate)));
|
||||
|
||||
assertThat(licenses.size(), equalTo(3));
|
||||
for (License license : licenses) {
|
||||
XContentBuilder builder = XContentFactory.contentBuilder(XContentType.JSON);
|
||||
license.toXContent(builder, new ToXContent.MapParams(ImmutableMap.of(Licenses.REST_VIEW_MODE, "true")));
|
||||
builder.flush();
|
||||
Map<String, Object> map = XContentHelper.convertToMap(builder.bytesStream().bytes(), false).v2();
|
||||
|
||||
// should have an extra status field, human readable issue_data and expiry_date
|
||||
assertThat(map.get("status"), notNullValue());
|
||||
assertThat(map.get("issue_date"), notNullValue());
|
||||
assertThat(map.get("expiry_date"), notNullValue());
|
||||
if (license.feature().equals("valid_feature")) {
|
||||
assertThat((String) map.get("status"), equalTo("active"));
|
||||
} else if (license.feature().equals("expired_feature")) {
|
||||
assertThat((String) map.get("status"), equalTo("expired"));
|
||||
} else if (license.feature().equals("invalid_feature")) {
|
||||
assertThat((String) map.get("status"), equalTo("invalid"));
|
||||
}
|
||||
}
|
||||
|
||||
for (License license : licenses) {
|
||||
XContentBuilder builder = XContentFactory.contentBuilder(XContentType.JSON);
|
||||
license.toXContent(builder, ToXContent.EMPTY_PARAMS);
|
||||
builder.flush();
|
||||
Map<String, Object> map = XContentHelper.convertToMap(builder.bytesStream().bytes(), false).v2();
|
||||
assertThat(map.get("status"), nullValue());
|
||||
}
|
||||
}
|
||||
}
|
|
@ -1,224 +0,0 @@
|
|||
/*
|
||||
* 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.core;
|
||||
|
||||
import org.elasticsearch.common.joda.DateMathParser;
|
||||
import org.elasticsearch.common.joda.FormatDateTimeFormatter;
|
||||
import org.elasticsearch.common.joda.Joda;
|
||||
import org.elasticsearch.common.xcontent.XContentBuilder;
|
||||
import org.hamcrest.MatcherAssert;
|
||||
import org.joda.time.format.DateTimeFormatter;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.util.*;
|
||||
import java.util.concurrent.Callable;
|
||||
|
||||
import static com.carrotsearch.randomizedtesting.RandomizedTest.*;
|
||||
import static org.elasticsearch.common.xcontent.XContentFactory.jsonBuilder;
|
||||
import static org.elasticsearch.test.ElasticsearchTestCase.randomFrom;
|
||||
import static org.hamcrest.core.IsEqual.equalTo;
|
||||
import static org.junit.Assert.assertThat;
|
||||
|
||||
public class TestUtils {
|
||||
|
||||
private final static FormatDateTimeFormatter formatDateTimeFormatter = Joda.forPattern("yyyy-MM-dd");
|
||||
private final static DateMathParser dateMathParser = new DateMathParser(formatDateTimeFormatter);
|
||||
private final static DateTimeFormatter dateTimeFormatter = formatDateTimeFormatter.printer();
|
||||
|
||||
public static void isSame(Set<License> firstLicenses, Set<License> secondLicenses) {
|
||||
|
||||
// we do the verifyAndBuild to make sure we weed out any expired licenses
|
||||
final Map<String, License> licenses1 = Licenses.reduceAndMap(firstLicenses);
|
||||
final Map<String, License> licenses2 = Licenses.reduceAndMap(secondLicenses);
|
||||
|
||||
// check if the effective licenses have the same feature set
|
||||
assertThat(licenses1.size(), equalTo(licenses2.size()));
|
||||
|
||||
// for every feature license, check if all the attributes are the same
|
||||
for (String featureType : licenses1.keySet()) {
|
||||
License license1 = licenses1.get(featureType);
|
||||
License license2 = licenses2.get(featureType);
|
||||
isSame(license1, license2);
|
||||
}
|
||||
}
|
||||
|
||||
public static void isSame(License license1, License license2) {
|
||||
assertThat(license1.uid(), equalTo(license2.uid()));
|
||||
assertThat(license1.feature(), equalTo(license2.feature()));
|
||||
assertThat(license1.subscriptionType(), equalTo(license2.subscriptionType()));
|
||||
assertThat(license1.type(), equalTo(license2.type()));
|
||||
assertThat(license1.issuedTo(), equalTo(license2.issuedTo()));
|
||||
assertThat(license1.signature(), equalTo(license2.signature()));
|
||||
assertThat(license1.expiryDate(), equalTo(license2.expiryDate()));
|
||||
assertThat(license1.issueDate(), equalTo(license2.issueDate()));
|
||||
assertThat(license1.maxNodes(), equalTo(license2.maxNodes()));
|
||||
}
|
||||
|
||||
public static String dateMathString(String time, final long now) {
|
||||
return dateTimeFormatter.print(dateMathParser.parse(time, new Callable<Long>() {
|
||||
@Override
|
||||
public Long call() throws Exception {
|
||||
return now;
|
||||
}
|
||||
}));
|
||||
}
|
||||
|
||||
public static long dateMath(String time, final long now) {
|
||||
return dateMathParser.parse(time, new Callable<Long>() {
|
||||
@Override
|
||||
public Long call() throws Exception {
|
||||
return now;
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
public static LicenseSpec generateRandomLicenseSpec() {
|
||||
boolean datesInMillis = randomBoolean();
|
||||
long now = System.currentTimeMillis();
|
||||
String uid = UUID.randomUUID().toString();
|
||||
String feature = "feature__" + randomInt();
|
||||
String issuer = "issuer__" + randomInt();
|
||||
String issuedTo = "issuedTo__" + randomInt();
|
||||
String type = randomFrom("subscription", "internal", "development");
|
||||
String subscriptionType = randomFrom("none", "gold", "silver", "platinum");
|
||||
int maxNodes = randomIntBetween(5, 100);
|
||||
if (datesInMillis) {
|
||||
long issueDateInMillis = dateMath("now", now);
|
||||
long expiryDateInMillis = dateMath("now+10d/d", now);
|
||||
return new LicenseSpec(uid, feature, issueDateInMillis, expiryDateInMillis, type, subscriptionType, issuedTo, issuer, maxNodes);
|
||||
} else {
|
||||
String issueDate = dateMathString("now", now);
|
||||
String expiryDate = dateMathString("now+10d/d", now);
|
||||
return new LicenseSpec(uid, feature, issueDate, expiryDate, type, subscriptionType, issuedTo, issuer, maxNodes);
|
||||
}
|
||||
}
|
||||
|
||||
public static String generateLicenseSpecString(List<LicenseSpec> licenseSpecs) throws IOException {
|
||||
XContentBuilder licenses = jsonBuilder();
|
||||
licenses.startObject();
|
||||
licenses.startArray("licenses");
|
||||
for (LicenseSpec licenseSpec : licenseSpecs) {
|
||||
licenses.startObject()
|
||||
.field("uid", licenseSpec.uid)
|
||||
.field("type", licenseSpec.type)
|
||||
.field("subscription_type", licenseSpec.subscriptionType)
|
||||
.field("issued_to", licenseSpec.issuedTo)
|
||||
.field("issuer", licenseSpec.issuer)
|
||||
.field("feature", licenseSpec.feature)
|
||||
.field("max_nodes", licenseSpec.maxNodes);
|
||||
|
||||
if (licenseSpec.issueDate != null) {
|
||||
licenses.field("issue_date", licenseSpec.issueDate);
|
||||
} else {
|
||||
licenses.field("issue_date_in_millis", licenseSpec.issueDateInMillis);
|
||||
}
|
||||
if (licenseSpec.expiryDate != null) {
|
||||
licenses.field("expiry_date", licenseSpec.expiryDate);
|
||||
} else {
|
||||
licenses.field("expiry_date_in_millis", licenseSpec.expiryDateInMillis);
|
||||
}
|
||||
licenses.endObject();
|
||||
}
|
||||
licenses.endArray();
|
||||
licenses.endObject();
|
||||
return licenses.string();
|
||||
}
|
||||
|
||||
public static Set<License> generateLicenses(List<LicenseSpec> licenseSpecs) {
|
||||
Set<License> unSignedLicenses = new HashSet<>();
|
||||
for (TestUtils.LicenseSpec spec : licenseSpecs) {
|
||||
License.Builder builder = License.builder()
|
||||
.uid(spec.uid)
|
||||
.feature(spec.feature)
|
||||
.type(spec.type)
|
||||
.subscriptionType(spec.subscriptionType)
|
||||
.issuedTo(spec.issuedTo)
|
||||
.issuer(spec.issuer)
|
||||
.maxNodes(spec.maxNodes);
|
||||
|
||||
if (spec.expiryDate != null) {
|
||||
builder.expiryDate(DateUtils.endOfTheDay(spec.expiryDate));
|
||||
} else {
|
||||
builder.expiryDate(spec.expiryDateInMillis);
|
||||
}
|
||||
if (spec.issueDate != null) {
|
||||
builder.issueDate(DateUtils.beginningOfTheDay(spec.issueDate));
|
||||
} else {
|
||||
builder.issueDate(spec.issueDateInMillis);
|
||||
}
|
||||
unSignedLicenses.add(builder.build());
|
||||
}
|
||||
return unSignedLicenses;
|
||||
}
|
||||
|
||||
public static void assertLicenseSpec(LicenseSpec spec, License license) {
|
||||
MatcherAssert.assertThat(license.uid(), equalTo(spec.uid));
|
||||
MatcherAssert.assertThat(license.feature(), equalTo(spec.feature));
|
||||
MatcherAssert.assertThat(license.issuedTo(), equalTo(spec.issuedTo));
|
||||
MatcherAssert.assertThat(license.issuer(), equalTo(spec.issuer));
|
||||
MatcherAssert.assertThat(license.type(), equalTo(spec.type));
|
||||
MatcherAssert.assertThat(license.subscriptionType(), equalTo(spec.subscriptionType));
|
||||
MatcherAssert.assertThat(license.maxNodes(), equalTo(spec.maxNodes));
|
||||
if (spec.issueDate != null) {
|
||||
MatcherAssert.assertThat(license.issueDate(), equalTo(DateUtils.beginningOfTheDay(spec.issueDate)));
|
||||
} else {
|
||||
MatcherAssert.assertThat(license.issueDate(), equalTo(spec.issueDateInMillis));
|
||||
}
|
||||
if (spec.expiryDate != null) {
|
||||
MatcherAssert.assertThat(license.expiryDate(), equalTo(DateUtils.endOfTheDay(spec.expiryDate)));
|
||||
} else {
|
||||
MatcherAssert.assertThat(license.expiryDate(), equalTo(spec.expiryDateInMillis));
|
||||
}
|
||||
}
|
||||
|
||||
public static class LicenseSpec {
|
||||
public final String feature;
|
||||
public final String issueDate;
|
||||
public final long issueDateInMillis;
|
||||
public final String expiryDate;
|
||||
public final long expiryDateInMillis;
|
||||
public final String uid;
|
||||
public final String type;
|
||||
public final String subscriptionType;
|
||||
public final String issuedTo;
|
||||
public final String issuer;
|
||||
public final int maxNodes;
|
||||
|
||||
public LicenseSpec(String feature, String issueDate, String expiryDate) {
|
||||
this(UUID.randomUUID().toString(), feature, issueDate, expiryDate, "trial", "none", "customer", "elasticsearch", 5);
|
||||
}
|
||||
|
||||
public LicenseSpec(String uid, String feature, long issueDateInMillis, long expiryDateInMillis, String type,
|
||||
String subscriptionType, String issuedTo, String issuer, int maxNodes) {
|
||||
this.feature = feature;
|
||||
this.issueDateInMillis = issueDateInMillis;
|
||||
this.issueDate = null;
|
||||
this.expiryDateInMillis = expiryDateInMillis;
|
||||
this.expiryDate = null;
|
||||
this.uid = uid;
|
||||
this.type = type;
|
||||
this.subscriptionType = subscriptionType;
|
||||
this.issuedTo = issuedTo;
|
||||
this.issuer = issuer;
|
||||
this.maxNodes = maxNodes;
|
||||
}
|
||||
|
||||
public LicenseSpec(String uid, String feature, String issueDate, String expiryDate, String type,
|
||||
String subscriptionType, String issuedTo, String issuer, int maxNodes) {
|
||||
this.feature = feature;
|
||||
this.issueDate = issueDate;
|
||||
this.issueDateInMillis = -1;
|
||||
this.expiryDate = expiryDate;
|
||||
this.expiryDateInMillis = -1;
|
||||
this.uid = uid;
|
||||
this.type = type;
|
||||
this.subscriptionType = subscriptionType;
|
||||
this.issuedTo = issuedTo;
|
||||
this.issuer = issuer;
|
||||
this.maxNodes = maxNodes;
|
||||
}
|
||||
}
|
||||
}
|
|
@ -1,11 +0,0 @@
|
|||
es.logger.level=INFO
|
||||
log4j.rootLogger=${es.logger.level}, out
|
||||
|
||||
log4j.logger.org.apache.http=INFO, out
|
||||
log4j.additivity.org.apache.http=false
|
||||
|
||||
log4j.logger.org.elasticsearch.license=TRACE
|
||||
|
||||
log4j.appender.out=org.apache.log4j.ConsoleAppender
|
||||
log4j.appender.out.layout=org.apache.log4j.PatternLayout
|
||||
log4j.appender.out.layout.conversionPattern=[%d{ISO8601}][%-5p][%-25c] %m%n
|
Binary file not shown.
|
@ -1,3 +0,0 @@
|
|||
ýŽÇqÝnęÄĚgŠśwM}Ťą‡UiKŠ•0âbÖ2Řşqö]â쇴ŻÖĎĂcĚ+IŇđÔ &IJ†fÉ~ßlj <09>ş]d™}o§OčľId®Č
|
||||
5A(ěµ´^ŘöW©DŤŞJµë}ů-Oîë?u N5ľŰvpŰ{’Ľ˛Áôśát–ť¤7ůřĂę#˛Vqöó»ktwm’Ś]ĎLőŁz"| Q‹lźňQđsâ>ů<}Ź[Á2ÖÓŕZÖ|5‹ŻŤĘĘ7%ŘęD
|
||||
Yĺ‘xn:ĽlúLČćHň˘«Ë2<C38B>źHvEEWÇ\¦H:“6Žh9 [!š…Űć©Š¤+;Ö.w7Cě©_|ŢÓŞĎÁ*ń§D`<60>Ú?‚ůxU/3>xUÓ“+ č
|
|
@ -1,15 +0,0 @@
|
|||
ELASTICSEARCH CONFIDENTIAL
|
||||
__________________
|
||||
|
||||
[2014] Elasticsearch Incorporated
|
||||
All Rights Reserved.
|
||||
|
||||
NOTICE: All information contained herein is, and remains
|
||||
the property of Elasticsearch Incorporated and its suppliers,
|
||||
if any. The intellectual and technical concepts contained
|
||||
herein are proprietary to Elasticsearch Incorporated
|
||||
and its suppliers and may be covered by U.S. and Foreign Patents,
|
||||
patents in process, and are protected by trade secret or copyright law.
|
||||
Dissemination of this information or reproduction of this material
|
||||
is strictly forbidden unless prior written permission is obtained
|
||||
from Elasticsearch Incorporated.
|
|
@ -1,13 +0,0 @@
|
|||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<additionalHeaders>
|
||||
<javadoc_style>
|
||||
<firstLine>/*</firstLine>
|
||||
<beforeEachLine> * </beforeEachLine>
|
||||
<endLine> */</endLine>
|
||||
<!--skipLine></skipLine-->
|
||||
<firstLineDetectionPattern>(\s|\t)*/\*.*$</firstLineDetectionPattern>
|
||||
<lastLineDetectionPattern>.*\*/(\s|\t)*$</lastLineDetectionPattern>
|
||||
<allowBlankLines>false</allowBlankLines>
|
||||
<isMultiline>true</isMultiline>
|
||||
</javadoc_style>
|
||||
</additionalHeaders>
|
|
@ -1,55 +0,0 @@
|
|||
#!/bin/sh
|
||||
|
||||
# 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.
|
||||
|
||||
CDPATH=""
|
||||
SCRIPT="$0"
|
||||
|
||||
# SCRIPT may be an arbitrarily deep series of symlinks. Loop until we have the concrete path.
|
||||
while [ -h "$SCRIPT" ] ; do
|
||||
ls=`ls -ld "$SCRIPT"`
|
||||
# Drop everything prior to ->
|
||||
link=`expr "$ls" : '.*-> \(.*\)$'`
|
||||
if expr "$link" : '/.*' > /dev/null; then
|
||||
SCRIPT="$link"
|
||||
else
|
||||
SCRIPT=`dirname "$SCRIPT"`/"$link"
|
||||
fi
|
||||
done
|
||||
|
||||
# determine license home
|
||||
LICENSE_HOME=`dirname "$SCRIPT"`/..
|
||||
|
||||
# make LICENSE_HOME absolute
|
||||
LICENSE_HOME=`cd "$LICENSE_HOME"; pwd`
|
||||
|
||||
# setup classpath
|
||||
LICENSE_CLASSPATH=$LICENSE_CLASSPATH:$LICENSE_HOME/lib/${project.artifactId}-${project.version}-exec.jar:$LICENSE_HOME/lib/*
|
||||
|
||||
if [ -x "$JAVA_HOME/bin/java" ]; then
|
||||
JAVA=$JAVA_HOME/bin/java
|
||||
else
|
||||
JAVA=`which java`
|
||||
fi
|
||||
|
||||
# real getopt cannot be used because we need to hand options over to the KeyPairGeneratorTool
|
||||
while [ $# -gt 0 ]; do
|
||||
case $1 in
|
||||
-D*=*)
|
||||
properties="$properties $1"
|
||||
;;
|
||||
-D*)
|
||||
var=$1
|
||||
shift
|
||||
properties="$properties $var=$1"
|
||||
;;
|
||||
*)
|
||||
args="$args $1"
|
||||
esac
|
||||
shift
|
||||
done
|
||||
|
||||
exec "$JAVA" $JAVA_OPTS -Xmx64m -Xms16m $properties -cp "$LICENSE_CLASSPATH" org.elasticsearch.license.licensor.tools.KeyPairGeneratorTool $args
|
||||
|
|
@ -1,55 +0,0 @@
|
|||
#!/bin/sh
|
||||
|
||||
# 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.
|
||||
|
||||
CDPATH=""
|
||||
SCRIPT="$0"
|
||||
|
||||
# SCRIPT may be an arbitrarily deep series of symlinks. Loop until we have the concrete path.
|
||||
while [ -h "$SCRIPT" ] ; do
|
||||
ls=`ls -ld "$SCRIPT"`
|
||||
# Drop everything prior to ->
|
||||
link=`expr "$ls" : '.*-> \(.*\)$'`
|
||||
if expr "$link" : '/.*' > /dev/null; then
|
||||
SCRIPT="$link"
|
||||
else
|
||||
SCRIPT=`dirname "$SCRIPT"`/"$link"
|
||||
fi
|
||||
done
|
||||
|
||||
# determine license home
|
||||
LICENSE_HOME=`dirname "$SCRIPT"`/..
|
||||
|
||||
# make LICENSE_HOME absolute
|
||||
LICENSE_HOME=`cd "$LICENSE_HOME"; pwd`
|
||||
|
||||
# setup classpath
|
||||
LICENSE_CLASSPATH=$LICENSE_CLASSPATH:$LICENSE_HOME/lib/${project.artifactId}-${project.version}-exec.jar:$LICENSE_HOME/lib/*
|
||||
|
||||
if [ -x "$JAVA_HOME/bin/java" ]; then
|
||||
JAVA=$JAVA_HOME/bin/java
|
||||
else
|
||||
JAVA=`which java`
|
||||
fi
|
||||
|
||||
# real getopt cannot be used because we need to hand options over to the LicenseGeneratorTool
|
||||
while [ $# -gt 0 ]; do
|
||||
case $1 in
|
||||
-D*=*)
|
||||
properties="$properties $1"
|
||||
;;
|
||||
-D*)
|
||||
var=$1
|
||||
shift
|
||||
properties="$properties $var=$1"
|
||||
;;
|
||||
*)
|
||||
args="$args $1"
|
||||
esac
|
||||
shift
|
||||
done
|
||||
|
||||
exec "$JAVA" $JAVA_OPTS -Xmx64m -Xms16m $properties -cp "$LICENSE_CLASSPATH" org.elasticsearch.license.licensor.tools.LicenseGeneratorTool $args
|
||||
|
|
@ -1,55 +0,0 @@
|
|||
#!/bin/sh
|
||||
|
||||
# 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.
|
||||
|
||||
CDPATH=""
|
||||
SCRIPT="$0"
|
||||
|
||||
# SCRIPT may be an arbitrarily deep series of symlinks. Loop until we have the concrete path.
|
||||
while [ -h "$SCRIPT" ] ; do
|
||||
ls=`ls -ld "$SCRIPT"`
|
||||
# Drop everything prior to ->
|
||||
link=`expr "$ls" : '.*-> \(.*\)$'`
|
||||
if expr "$link" : '/.*' > /dev/null; then
|
||||
SCRIPT="$link"
|
||||
else
|
||||
SCRIPT=`dirname "$SCRIPT"`/"$link"
|
||||
fi
|
||||
done
|
||||
|
||||
# determine license home
|
||||
LICENSE_HOME=`dirname "$SCRIPT"`/..
|
||||
|
||||
# make LICENSE_HOME absolute
|
||||
LICENSE_HOME=`cd "$LICENSE_HOME"; pwd`
|
||||
|
||||
# setup classpath
|
||||
LICENSE_CLASSPATH=$LICENSE_CLASSPATH:$LICENSE_HOME/lib/${project.artifactId}-${project.version}-exec.jar:$LICENSE_HOME/lib/*
|
||||
|
||||
if [ -x "$JAVA_HOME/bin/java" ]; then
|
||||
JAVA=$JAVA_HOME/bin/java
|
||||
else
|
||||
JAVA=`which java`
|
||||
fi
|
||||
|
||||
# real getopt cannot be used because we need to hand options over to the LicenseVerificationTool
|
||||
while [ $# -gt 0 ]; do
|
||||
case $1 in
|
||||
-D*=*)
|
||||
properties="$properties $1"
|
||||
;;
|
||||
-D*)
|
||||
var=$1
|
||||
shift
|
||||
properties="$properties $var=$1"
|
||||
;;
|
||||
*)
|
||||
args="$args $1"
|
||||
esac
|
||||
shift
|
||||
done
|
||||
|
||||
exec "$JAVA" $JAVA_OPTS -Xmx64m -Xms16m $properties -cp "$LICENSE_CLASSPATH" org.elasticsearch.license.licensor.tools.LicenseVerificationTool $args
|
||||
|
|
@ -1,58 +0,0 @@
|
|||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<project xmlns="http://maven.apache.org/POM/4.0.0"
|
||||
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
|
||||
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
|
||||
<parent>
|
||||
<artifactId>elasticsearch-license</artifactId>
|
||||
<groupId>org.elasticsearch</groupId>
|
||||
<version>2.0.0.beta1-SNAPSHOT</version>
|
||||
</parent>
|
||||
<modelVersion>4.0.0</modelVersion>
|
||||
|
||||
<artifactId>elasticsearch-license-licensor</artifactId>
|
||||
|
||||
<properties>
|
||||
<license.basedir combine.self="override">${project.parent.basedir}</license.basedir>
|
||||
<elasticsearch.assembly.descriptor>${basedir}/src/main/assemblies/exec.xml</elasticsearch.assembly.descriptor>
|
||||
<elasticsearch.assembly.appendId>true</elasticsearch.assembly.appendId>
|
||||
<!-- we aren't really a plugin... -->
|
||||
<skip.integ.tests>true</skip.integ.tests>
|
||||
</properties>
|
||||
|
||||
<dependencies>
|
||||
<dependency>
|
||||
<groupId>org.elasticsearch</groupId>
|
||||
<artifactId>elasticsearch-license-core</artifactId>
|
||||
<version>2.0.0.beta1-SNAPSHOT</version>
|
||||
</dependency>
|
||||
</dependencies>
|
||||
|
||||
<build>
|
||||
<plugins>
|
||||
<plugin>
|
||||
<groupId>org.apache.maven.plugins</groupId>
|
||||
<artifactId>maven-assembly-plugin</artifactId>
|
||||
</plugin>
|
||||
<plugin>
|
||||
<groupId>org.apache.maven.plugins</groupId>
|
||||
<artifactId>maven-jar-plugin</artifactId>
|
||||
<executions>
|
||||
<execution>
|
||||
<id>for-exec</id>
|
||||
<phase>prepare-package</phase>
|
||||
<goals>
|
||||
<goal>jar</goal>
|
||||
</goals>
|
||||
<configuration>
|
||||
<classifier>exec</classifier>
|
||||
<excludes>
|
||||
<!-- exclude public key -->
|
||||
<exclude>public.key</exclude>
|
||||
</excludes>
|
||||
</configuration>
|
||||
</execution>
|
||||
</executions>
|
||||
</plugin>
|
||||
</plugins>
|
||||
</build>
|
||||
</project>
|
|
@ -1 +0,0 @@
|
|||
{"licenses":[{"uid": "893361dc-9749-4997-93cb-802e3d7fa4a8", "type":"internal","subscription_type":"none","issued_to":"issuedTo","issuer":"issuer","issue_date":"2014-09-29","expiry_date":"2015-08-29","feature":"shield","max_nodes":1}]}
|
|
@ -1,47 +0,0 @@
|
|||
<?xml version="1.0"?>
|
||||
<assembly>
|
||||
<id>exec</id>
|
||||
<formats>
|
||||
<format>zip</format>
|
||||
</formats>
|
||||
<includeBaseDirectory>true</includeBaseDirectory>
|
||||
<fileSets>
|
||||
<fileSet>
|
||||
<filtered>true</filtered>
|
||||
<directory>bin</directory>
|
||||
<outputDirectory>bin</outputDirectory>
|
||||
<fileMode>0755</fileMode>
|
||||
<directoryMode>0755</directoryMode>
|
||||
<lineEnding>unix</lineEnding>
|
||||
<includes>
|
||||
<include>key-pair-generator</include>
|
||||
<include>license-generator</include>
|
||||
<include>verify-license</include>
|
||||
</includes>
|
||||
</fileSet>
|
||||
</fileSets>
|
||||
<dependencySets>
|
||||
<dependencySet>
|
||||
<outputDirectory>/lib</outputDirectory>
|
||||
<useProjectArtifact>false</useProjectArtifact>
|
||||
<useProjectAttachments>true</useProjectAttachments>
|
||||
<useTransitiveFiltering>true</useTransitiveFiltering>
|
||||
<includes>
|
||||
<include>org.elasticsearch:elasticsearch-license-licensor:*:exec</include>
|
||||
<include>org.elasticsearch:elasticsearch</include>
|
||||
</includes>
|
||||
<excludes>
|
||||
<exclude>org.apache.lucene:*</exclude>
|
||||
</excludes>
|
||||
</dependencySet>
|
||||
<dependencySet>
|
||||
<outputDirectory>/lib</outputDirectory>
|
||||
<useProjectArtifact>false</useProjectArtifact>
|
||||
<useProjectAttachments>true</useProjectAttachments>
|
||||
<useTransitiveFiltering>true</useTransitiveFiltering>
|
||||
<includes>
|
||||
<include>org.apache.lucene:lucene-core</include>
|
||||
</includes>
|
||||
</dependencySet>
|
||||
</dependencySets>
|
||||
</assembly>
|
|
@ -1,104 +0,0 @@
|
|||
/*
|
||||
* 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.licensor;
|
||||
|
||||
import com.google.common.collect.ImmutableSet;
|
||||
import org.elasticsearch.common.Base64;
|
||||
import org.elasticsearch.common.SuppressForbidden;
|
||||
import org.elasticsearch.common.io.PathUtils;
|
||||
import org.elasticsearch.common.xcontent.ToXContent;
|
||||
import org.elasticsearch.common.xcontent.XContentBuilder;
|
||||
import org.elasticsearch.common.xcontent.XContentFactory;
|
||||
import org.elasticsearch.common.xcontent.XContentType;
|
||||
import org.elasticsearch.license.core.License;
|
||||
import org.elasticsearch.license.core.Licenses;
|
||||
import org.elasticsearch.license.core.CryptUtils;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.nio.ByteBuffer;
|
||||
import java.nio.file.Files;
|
||||
import java.nio.file.Path;
|
||||
import java.security.*;
|
||||
import java.util.Collections;
|
||||
import java.util.Set;
|
||||
|
||||
/**
|
||||
* Responsible for generating a license signature according to
|
||||
* the signature spec and sign it with the provided encrypted private key
|
||||
*/
|
||||
@SuppressForbidden(reason = "can we avoid bare string paths and resolve from Environment or similar?")
|
||||
public class LicenseSigner {
|
||||
|
||||
private final static int MAGIC_LENGTH = 13;
|
||||
|
||||
private final Path publicKeyPath;
|
||||
|
||||
private final Path privateKeyPath;
|
||||
|
||||
public LicenseSigner(final String privateKeyPath, final String publicKeyPath) {
|
||||
this(PathUtils.get(privateKeyPath), PathUtils.get(publicKeyPath));
|
||||
}
|
||||
|
||||
public LicenseSigner(final Path privateKeyPath, final Path publicKeyPath) {
|
||||
this.publicKeyPath = publicKeyPath;
|
||||
this.privateKeyPath = privateKeyPath;
|
||||
}
|
||||
|
||||
|
||||
public ImmutableSet<License> sign(Set<License> licenseSpecs) throws IOException {
|
||||
final ImmutableSet.Builder<License> builder = ImmutableSet.builder();
|
||||
for (License licenseSpec : licenseSpecs) {
|
||||
builder.add(sign(licenseSpec));
|
||||
}
|
||||
return builder.build();
|
||||
}
|
||||
|
||||
/**
|
||||
* Generates a signature for the <code>licenseSpec</code>.
|
||||
* Signature structure:
|
||||
* | VERSION | MAGIC | PUB_KEY_DIGEST | SIGNED_LICENSE_CONTENT |
|
||||
*
|
||||
* @return a signed License
|
||||
* @throws java.io.IOException
|
||||
*/
|
||||
public License sign(License licenseSpec) throws IOException {
|
||||
XContentBuilder contentBuilder = XContentFactory.contentBuilder(XContentType.JSON);
|
||||
licenseSpec.toXContent(contentBuilder, new ToXContent.MapParams(Collections.singletonMap(Licenses.LICENSE_SPEC_VIEW_MODE, "true")));
|
||||
|
||||
final byte[] signedContent = sign(contentBuilder.bytes().toBytes(), privateKeyPath);
|
||||
final byte[] magic = new byte[MAGIC_LENGTH];
|
||||
SecureRandom random = new SecureRandom();
|
||||
random.nextBytes(magic);
|
||||
final byte[] hash = Base64.encodeBytesToBytes(Files.readAllBytes(publicKeyPath));
|
||||
assert hash != null;
|
||||
byte[] bytes = new byte[4 + 4 + MAGIC_LENGTH + 4 + hash.length + 4 + signedContent.length];
|
||||
|
||||
ByteBuffer byteBuffer = ByteBuffer.wrap(bytes);
|
||||
byteBuffer.putInt(License.VERSION_CURRENT)
|
||||
.putInt(magic.length)
|
||||
.put(magic)
|
||||
.putInt(hash.length)
|
||||
.put(hash)
|
||||
.putInt(signedContent.length)
|
||||
.put(signedContent);
|
||||
|
||||
return License.builder()
|
||||
.fromLicenseSpec(licenseSpec, Base64.encodeBytes(bytes))
|
||||
.validate()
|
||||
.build();
|
||||
}
|
||||
|
||||
private static byte[] sign(byte[] data, Path privateKeyPath) {
|
||||
try {
|
||||
final Signature rsa = Signature.getInstance("SHA512withRSA");
|
||||
rsa.initSign(CryptUtils.readEncryptedPrivateKey(Files.readAllBytes(privateKeyPath)));
|
||||
rsa.update(data);
|
||||
return rsa.sign();
|
||||
} catch (InvalidKeyException | IOException | NoSuchAlgorithmException | SignatureException e) {
|
||||
throw new IllegalStateException(e);
|
||||
}
|
||||
}
|
||||
}
|
|
@ -1,102 +0,0 @@
|
|||
/*
|
||||
* 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.licensor.tools;
|
||||
|
||||
import org.apache.commons.cli.CommandLine;
|
||||
import org.elasticsearch.common.cli.CliTool;
|
||||
import org.elasticsearch.common.cli.CliToolConfig;
|
||||
import org.elasticsearch.common.cli.Terminal;
|
||||
import org.elasticsearch.common.settings.Settings;
|
||||
import org.elasticsearch.env.Environment;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.nio.file.Files;
|
||||
import java.nio.file.Path;
|
||||
import java.security.KeyPair;
|
||||
import java.security.KeyPairGenerator;
|
||||
import java.security.NoSuchAlgorithmException;
|
||||
import java.security.SecureRandom;
|
||||
|
||||
import static org.elasticsearch.common.cli.CliToolConfig.Builder.cmd;
|
||||
import static org.elasticsearch.common.cli.CliToolConfig.Builder.option;
|
||||
import static org.elasticsearch.common.cli.CliToolConfig.config;
|
||||
import static org.elasticsearch.license.core.CryptUtils.writeEncryptedPrivateKey;
|
||||
import static org.elasticsearch.license.core.CryptUtils.writeEncryptedPublicKey;
|
||||
|
||||
public class KeyPairGeneratorTool extends CliTool {
|
||||
|
||||
public static final String NAME = "key-pair-generator";
|
||||
private static final CliToolConfig CONFIG = config("licensor", KeyPairGeneratorTool.class)
|
||||
.cmds(KeyGenerator.CMD)
|
||||
.build();
|
||||
|
||||
public KeyPairGeneratorTool() {
|
||||
super(CONFIG);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected Command parse(String s, CommandLine commandLine) throws Exception {
|
||||
return KeyGenerator.parse(terminal, commandLine, env);
|
||||
}
|
||||
|
||||
public static class KeyGenerator extends Command {
|
||||
|
||||
private static final CliToolConfig.Cmd CMD = cmd(NAME, KeyGenerator.class)
|
||||
.options(
|
||||
option("pub", "publicKeyPath").required(true).hasArg(true),
|
||||
option("pri", "privateKeyPath").required(true).hasArg(true)
|
||||
).build();
|
||||
|
||||
public final Path publicKeyPath;
|
||||
public final Path privateKeyPath;
|
||||
|
||||
protected KeyGenerator(Terminal terminal, Path publicKeyPath, Path privateKeyPath) {
|
||||
super(terminal);
|
||||
this.privateKeyPath = privateKeyPath;
|
||||
this.publicKeyPath = publicKeyPath;
|
||||
}
|
||||
|
||||
public static Command parse(Terminal terminal, CommandLine commandLine, Environment environment) {
|
||||
Path publicKeyPath = environment.homeFile().resolve(commandLine.getOptionValue("publicKeyPath"));
|
||||
Path privateKeyPath = environment.homeFile().resolve(commandLine.getOptionValue("privateKeyPath"));
|
||||
|
||||
if (Files.exists(privateKeyPath)) {
|
||||
return exitCmd(ExitStatus.USAGE, terminal, privateKeyPath + " already exists");
|
||||
} else if (Files.exists(publicKeyPath)) {
|
||||
return exitCmd(ExitStatus.USAGE, terminal, publicKeyPath + " already exists");
|
||||
}
|
||||
return new KeyGenerator(terminal, publicKeyPath, privateKeyPath);
|
||||
}
|
||||
|
||||
@Override
|
||||
public ExitStatus execute(Settings settings, Environment env) throws Exception {
|
||||
KeyPair keyPair = generateKeyPair(privateKeyPath, publicKeyPath);
|
||||
terminal.println(Terminal.Verbosity.VERBOSE, "generating key pair [public key: " + publicKeyPath + ", private key: " + privateKeyPath + "]");
|
||||
return (keyPair != null) ? ExitStatus.OK : ExitStatus.CANT_CREATE;
|
||||
}
|
||||
|
||||
private static KeyPair generateKeyPair(Path privateKeyPath, Path publicKeyPath) throws IOException, NoSuchAlgorithmException {
|
||||
SecureRandom random = new SecureRandom();
|
||||
|
||||
KeyPairGenerator keyGen = KeyPairGenerator.getInstance("RSA");
|
||||
keyGen.initialize(2048, random);
|
||||
KeyPair keyPair = keyGen.generateKeyPair();
|
||||
|
||||
saveKeyPairToFiles(keyPair, privateKeyPath, publicKeyPath);
|
||||
return keyPair;
|
||||
}
|
||||
}
|
||||
|
||||
private static void saveKeyPairToFiles(KeyPair keyPair, Path privateKeyPath, Path publicKeyPath) throws IOException {
|
||||
Files.write(privateKeyPath, writeEncryptedPrivateKey(keyPair.getPrivate()));
|
||||
Files.write(publicKeyPath, writeEncryptedPublicKey(keyPair.getPublic()));
|
||||
}
|
||||
|
||||
public static void main(String[] args) throws Exception {
|
||||
int status = new KeyPairGeneratorTool().execute(args);
|
||||
System.exit(status);
|
||||
}
|
||||
}
|
|
@ -1,126 +0,0 @@
|
|||
/*
|
||||
* 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.licensor.tools;
|
||||
|
||||
import com.google.common.collect.ImmutableSet;
|
||||
import org.apache.commons.cli.CommandLine;
|
||||
import org.elasticsearch.common.cli.CliTool;
|
||||
import org.elasticsearch.common.cli.CliToolConfig;
|
||||
import org.elasticsearch.common.cli.Terminal;
|
||||
import org.elasticsearch.common.settings.Settings;
|
||||
import org.elasticsearch.common.xcontent.ToXContent;
|
||||
import org.elasticsearch.common.xcontent.XContentBuilder;
|
||||
import org.elasticsearch.common.xcontent.XContentFactory;
|
||||
import org.elasticsearch.common.xcontent.XContentType;
|
||||
import org.elasticsearch.env.Environment;
|
||||
import org.elasticsearch.license.core.License;
|
||||
import org.elasticsearch.license.core.Licenses;
|
||||
import org.elasticsearch.license.licensor.LicenseSigner;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.nio.charset.StandardCharsets;
|
||||
import java.nio.file.Files;
|
||||
import java.nio.file.Path;
|
||||
import java.util.HashSet;
|
||||
import java.util.Set;
|
||||
|
||||
import static org.elasticsearch.common.cli.CliToolConfig.Builder.cmd;
|
||||
import static org.elasticsearch.common.cli.CliToolConfig.Builder.option;
|
||||
import static org.elasticsearch.common.cli.CliToolConfig.config;
|
||||
|
||||
public class LicenseGeneratorTool extends CliTool {
|
||||
public static final String NAME = "license-generator";
|
||||
|
||||
private static final CliToolConfig CONFIG = config("licensor", LicenseGeneratorTool.class)
|
||||
.cmds(LicenseGenerator.CMD)
|
||||
.build();
|
||||
|
||||
public LicenseGeneratorTool() {
|
||||
super(CONFIG);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected Command parse(String s, CommandLine commandLine) throws Exception {
|
||||
return LicenseGenerator.parse(terminal, commandLine, env);
|
||||
}
|
||||
|
||||
public static class LicenseGenerator extends Command {
|
||||
|
||||
private static final CliToolConfig.Cmd CMD = cmd(NAME, LicenseGenerator.class)
|
||||
.options(
|
||||
option("pub", "publicKeyPath").required(true).hasArg(true),
|
||||
option("pri", "privateKeyPath").required(true).hasArg(true),
|
||||
option("l", "license").required(false).hasArg(true),
|
||||
option("lf", "licenseFile").required(false).hasArg(true)
|
||||
).build();
|
||||
|
||||
public final Set<License> licenseSpecs;
|
||||
public final Path publicKeyFilePath;
|
||||
public final Path privateKeyFilePath;
|
||||
|
||||
public LicenseGenerator(Terminal terminal, Path publicKeyFilePath, Path privateKeyFilePath, Set<License> licenseSpecs) {
|
||||
super(terminal);
|
||||
this.licenseSpecs = licenseSpecs;
|
||||
this.privateKeyFilePath = privateKeyFilePath;
|
||||
this.publicKeyFilePath = publicKeyFilePath;
|
||||
}
|
||||
|
||||
public static Command parse(Terminal terminal, CommandLine commandLine, Environment environment) throws IOException {
|
||||
Path publicKeyPath = environment.homeFile().resolve(commandLine.getOptionValue("publicKeyPath"));
|
||||
Path privateKeyPath = environment.homeFile().resolve(commandLine.getOptionValue("privateKeyPath"));
|
||||
String[] licenseSpecSources = commandLine.getOptionValues("license");
|
||||
String[] licenseSpecSourceFiles = commandLine.getOptionValues("licenseFile");
|
||||
|
||||
if (!Files.exists(privateKeyPath)) {
|
||||
return exitCmd(ExitStatus.USAGE, terminal, privateKeyPath + " does not exist");
|
||||
} else if (!Files.exists(publicKeyPath)) {
|
||||
return exitCmd(ExitStatus.USAGE, terminal, publicKeyPath + " does not exist");
|
||||
}
|
||||
|
||||
Set<License> licenseSpecs = new HashSet<>();
|
||||
if (licenseSpecSources != null) {
|
||||
for (String licenseSpec : licenseSpecSources) {
|
||||
licenseSpecs.addAll(Licenses.fromSource(licenseSpec.getBytes(StandardCharsets.UTF_8), false));
|
||||
}
|
||||
}
|
||||
|
||||
if (licenseSpecSourceFiles != null) {
|
||||
for (String licenseSpecFilePath : licenseSpecSourceFiles) {
|
||||
Path licenseSpecPath = environment.homeFile().resolve(licenseSpecFilePath);
|
||||
if (!Files.exists(licenseSpecPath)) {
|
||||
return exitCmd(ExitStatus.USAGE, terminal, licenseSpecFilePath + " does not exist");
|
||||
}
|
||||
licenseSpecs.addAll(Licenses.fromSource(Files.readAllBytes(licenseSpecPath), false));
|
||||
}
|
||||
}
|
||||
|
||||
if (licenseSpecs.size() == 0) {
|
||||
return exitCmd(ExitStatus.USAGE, terminal, "no license spec provided");
|
||||
}
|
||||
return new LicenseGenerator(terminal, publicKeyPath, privateKeyPath, licenseSpecs);
|
||||
}
|
||||
|
||||
@Override
|
||||
public ExitStatus execute(Settings settings, Environment env) throws Exception {
|
||||
|
||||
// sign
|
||||
ImmutableSet<License> signedLicences = new LicenseSigner(privateKeyFilePath, publicKeyFilePath).sign(licenseSpecs);
|
||||
|
||||
// dump
|
||||
XContentBuilder builder = XContentFactory.contentBuilder(XContentType.JSON);
|
||||
Licenses.toXContent(signedLicences, builder, ToXContent.EMPTY_PARAMS);
|
||||
builder.flush();
|
||||
terminal.print(builder.string());
|
||||
|
||||
return ExitStatus.OK;
|
||||
}
|
||||
}
|
||||
|
||||
public static void main(String[] args) throws Exception {
|
||||
int status = new LicenseGeneratorTool().execute(args);
|
||||
System.exit(status);
|
||||
}
|
||||
}
|
|
@ -1,126 +0,0 @@
|
|||
/*
|
||||
* 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.licensor.tools;
|
||||
|
||||
import org.apache.commons.cli.CommandLine;
|
||||
import org.elasticsearch.common.cli.CliTool;
|
||||
import org.elasticsearch.common.cli.CliToolConfig;
|
||||
import org.elasticsearch.common.cli.Terminal;
|
||||
import org.elasticsearch.common.settings.Settings;
|
||||
import org.elasticsearch.common.xcontent.ToXContent;
|
||||
import org.elasticsearch.common.xcontent.XContentBuilder;
|
||||
import org.elasticsearch.common.xcontent.XContentFactory;
|
||||
import org.elasticsearch.common.xcontent.XContentType;
|
||||
import org.elasticsearch.env.Environment;
|
||||
import org.elasticsearch.license.core.License;
|
||||
import org.elasticsearch.license.core.Licenses;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.nio.charset.StandardCharsets;
|
||||
import java.nio.file.Files;
|
||||
import java.nio.file.Path;
|
||||
import java.util.HashSet;
|
||||
import java.util.Map;
|
||||
import java.util.Set;
|
||||
|
||||
import static org.elasticsearch.common.cli.CliToolConfig.Builder.cmd;
|
||||
import static org.elasticsearch.common.cli.CliToolConfig.Builder.option;
|
||||
import static org.elasticsearch.common.cli.CliToolConfig.config;
|
||||
|
||||
public class LicenseVerificationTool extends CliTool {
|
||||
public static final String NAME = "verify-license";
|
||||
|
||||
private static final CliToolConfig CONFIG = config("licensor", LicenseVerificationTool.class)
|
||||
.cmds(LicenseVerifier.CMD)
|
||||
.build();
|
||||
|
||||
public LicenseVerificationTool() {
|
||||
super(CONFIG);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected Command parse(String s, CommandLine commandLine) throws Exception {
|
||||
return LicenseVerifier.parse(terminal, commandLine, env);
|
||||
}
|
||||
|
||||
public static class LicenseVerifier extends Command {
|
||||
|
||||
private static final CliToolConfig.Cmd CMD = cmd(NAME, LicenseVerifier.class)
|
||||
.options(
|
||||
option("pub", "publicKeyPath").required(true).hasArg(true),
|
||||
option("l", "license").required(false).hasArg(true),
|
||||
option("lf", "licenseFile").required(false).hasArg(true)
|
||||
).build();
|
||||
|
||||
public final Set<License> licenses;
|
||||
public final Path publicKeyPath;
|
||||
|
||||
public LicenseVerifier(Terminal terminal, Set<License> licenses, Path publicKeyPath) {
|
||||
super(terminal);
|
||||
this.licenses = licenses;
|
||||
this.publicKeyPath = publicKeyPath;
|
||||
}
|
||||
|
||||
public static Command parse(Terminal terminal, CommandLine commandLine, Environment environment) throws IOException {
|
||||
String publicKeyPathString = commandLine.getOptionValue("publicKeyPath");
|
||||
String[] licenseSources = commandLine.getOptionValues("license");
|
||||
String[] licenseSourceFiles = commandLine.getOptionValues("licenseFile");
|
||||
|
||||
Set<License> licenses = new HashSet<>();
|
||||
if (licenseSources != null) {
|
||||
for (String licenseSpec : licenseSources) {
|
||||
licenses.addAll(Licenses.fromSource(licenseSpec.getBytes(StandardCharsets.UTF_8)));
|
||||
}
|
||||
}
|
||||
|
||||
if (licenseSourceFiles != null) {
|
||||
for (String licenseFilePath : licenseSourceFiles) {
|
||||
Path licensePath = environment.homeFile().resolve(licenseFilePath);
|
||||
if (!Files.exists(licensePath)) {
|
||||
return exitCmd(ExitStatus.USAGE, terminal, licenseFilePath + " does not exist");
|
||||
}
|
||||
licenses.addAll(Licenses.fromSource(Files.readAllBytes(licensePath)));
|
||||
}
|
||||
}
|
||||
|
||||
if (licenses.size() == 0) {
|
||||
return exitCmd(ExitStatus.USAGE, terminal, "no license provided");
|
||||
}
|
||||
|
||||
Path publicKeyPath = environment.homeFile().resolve(publicKeyPathString);
|
||||
if (!Files.exists(publicKeyPath)) {
|
||||
return exitCmd(ExitStatus.USAGE, terminal, publicKeyPath + " does not exist");
|
||||
}
|
||||
|
||||
return new LicenseVerifier(terminal, licenses, publicKeyPath);
|
||||
}
|
||||
|
||||
@Override
|
||||
public ExitStatus execute(Settings settings, Environment env) throws Exception {
|
||||
|
||||
// verify
|
||||
Map<String, License> effectiveLicenses = Licenses.reduceAndMap(licenses);
|
||||
|
||||
if (!org.elasticsearch.license.core.LicenseVerifier.verifyLicenses(effectiveLicenses.values(), publicKeyPath)) {
|
||||
terminal.println("Invalid License(s)!");
|
||||
return ExitStatus.DATA_ERROR;
|
||||
}
|
||||
|
||||
// dump effective licences
|
||||
XContentBuilder builder = XContentFactory.contentBuilder(XContentType.JSON);
|
||||
Licenses.toXContent(effectiveLicenses.values(), builder, ToXContent.EMPTY_PARAMS);
|
||||
builder.flush();
|
||||
terminal.print(builder.string());
|
||||
|
||||
return ExitStatus.OK;
|
||||
}
|
||||
}
|
||||
|
||||
public static void main(String[] args) throws Exception {
|
||||
int status = new LicenseVerificationTool().execute(args);
|
||||
System.exit(status);
|
||||
}
|
||||
}
|
|
@ -1,22 +0,0 @@
|
|||
NAME
|
||||
|
||||
key-pair-generator - generates a key pair with RSA 2048-bit security
|
||||
|
||||
SYNOPSIS
|
||||
|
||||
key-pair-generator -pub publicKeyPath -pri privateKeyPath
|
||||
|
||||
DESCRIPTION
|
||||
|
||||
This tool generates and saves a key pair to the provided publicKeyPath
|
||||
and privateKeyPath. The tool checks the existence of the provided key paths
|
||||
and will not override if any existing keys are found.
|
||||
|
||||
OPTIONS
|
||||
|
||||
-h,--help Shows this message
|
||||
|
||||
-pub,--publicKeyPath <path> Save the generated public key to path
|
||||
|
||||
-pri,--privateKeyPath <path> Save the generated private key to path
|
||||
|
|
@ -1,26 +0,0 @@
|
|||
NAME
|
||||
|
||||
license-generator - generates signed elasticsearch license(s) for a given license spec(s)
|
||||
|
||||
SYNOPSIS
|
||||
|
||||
license-generator -l licenseSpec -pub publicKeyPath -pri privateKeyPath
|
||||
|
||||
DESCRIPTION
|
||||
|
||||
This tool generate elasticsearch license(s) for the provided license spec(s). The tool
|
||||
can take arbitrary number of `--license` and/or `--licenseFile` to generate corrosponding
|
||||
signed license(s).
|
||||
|
||||
OPTIONS
|
||||
|
||||
-h,--help Shows this message
|
||||
|
||||
-l,--license <license spec> License spec to generate a signed license from
|
||||
|
||||
-lf,--licenseFile <path> Path to a license spec file
|
||||
|
||||
-pub,--publicKeyPath <path> Path to public key to be used
|
||||
|
||||
-pri,--privateKeyPath <path> Path to private key to be used
|
||||
|
|
@ -1,28 +0,0 @@
|
|||
NAME
|
||||
|
||||
verify-license - verifies the integrity of elasticsearch signed license(s)
|
||||
|
||||
SYNOPSIS
|
||||
|
||||
verify-license -l signedLicense -pub publicKeyPath
|
||||
|
||||
DESCRIPTION
|
||||
|
||||
This tool assumes the configured public key to be the same as that of the production license plugin public key.
|
||||
The tool can take arbitrary number of `--license` and/or `--licenseFile` for verifying signed license(s). If any
|
||||
of the provided license(s) are invalid, the tool will error out, otherwise it will output a effective licenses file.
|
||||
|
||||
Effective Licenses:
|
||||
A set of licenses that only has one effective sub-license for every feature provided through the input license file.
|
||||
Where effective sub-licenses are identified as the sub-licenses with the latest `expiry_date` for a `feature`
|
||||
and the sub-license has not already expired.
|
||||
|
||||
OPTIONS
|
||||
|
||||
-h,--help Shows this message
|
||||
|
||||
-l,--license <signed license> signed license(s) string
|
||||
|
||||
-lf,--licenseFile <path> Path to signed license(s) file
|
||||
|
||||
-pub,--publicKeyPath <path> Path to public key to verify against
|
|
@ -1,97 +0,0 @@
|
|||
/*
|
||||
* 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.licensor;/*
|
||||
import org.elasticsearch.common.unit.TimeValue;
|
||||
import org.elasticsearch.license.core.DateUtils;
|
||||
import org.elasticsearch.license.core.License;
|
||||
import org.elasticsearch.test.ElasticsearchTestCase;
|
||||
import org.junit.After;
|
||||
import org.junit.Before;
|
||||
|
||||
import java.util.HashSet;
|
||||
import java.util.List;
|
||||
import java.util.Set;
|
||||
import java.util.UUID;
|
||||
|
||||
public abstract class AbstractLicensingTestBase extends ElasticsearchTestCase {
|
||||
|
||||
protected String pubKeyPath = null;
|
||||
protected String priKeyPath = null;
|
||||
|
||||
@Before
|
||||
public void setup() throws Exception {
|
||||
pubKeyPath = getResourcePath("/public.key");
|
||||
priKeyPath = getResourcePath("/private.key");
|
||||
}
|
||||
|
||||
@After
|
||||
public void cleanUp() {
|
||||
pubKeyPath = null;
|
||||
priKeyPath = null;
|
||||
}
|
||||
|
||||
public static Set<License> generateSignedLicenses(List<TestUtils.LicenseSpec> licenseSpecs, String pubKeyPath, String priKeyPath) throws Exception {
|
||||
LicenseSigner signer = new LicenseSigner(priKeyPath, pubKeyPath);
|
||||
Set<License> unSignedLicenses = new HashSet<>();
|
||||
for (TestUtils.LicenseSpec spec : licenseSpecs) {
|
||||
License.Builder builder = License.builder()
|
||||
.uid(spec.uid)
|
||||
.feature(spec.feature)
|
||||
.type(spec.type)
|
||||
.subscriptionType(spec.subscriptionType)
|
||||
.issuedTo(spec.issuedTo)
|
||||
.issuer(spec.issuer)
|
||||
.maxNodes(spec.maxNodes);
|
||||
|
||||
if (spec.expiryDate != null) {
|
||||
builder.expiryDate(DateUtils.endOfTheDay(spec.expiryDate));
|
||||
} else {
|
||||
builder.expiryDate(spec.expiryDateInMillis);
|
||||
}
|
||||
if (spec.issueDate != null) {
|
||||
builder.issueDate(DateUtils.beginningOfTheDay(spec.issueDate));
|
||||
} else {
|
||||
builder.issueDate(spec.issueDateInMillis);
|
||||
}
|
||||
unSignedLicenses.add(builder.build());
|
||||
}
|
||||
return signer.sign(unSignedLicenses);
|
||||
}
|
||||
|
||||
public static License generateSignedLicense(String feature, TimeValue expiryDuration, String pubKeyPath, String priKeyPath) throws Exception {
|
||||
return generateSignedLicense(feature, -1, expiryDuration, pubKeyPath, priKeyPath);
|
||||
}
|
||||
|
||||
public static License generateSignedLicense(String feature, long issueDate, TimeValue expiryDuration, String pubKeyPath, String priKeyPath) throws Exception {
|
||||
long issue = (issueDate != -1l) ? issueDate : System.currentTimeMillis();
|
||||
final License licenseSpec = License.builder()
|
||||
.uid(UUID.randomUUID().toString())
|
||||
.feature(feature)
|
||||
.expiryDate(issue + expiryDuration.getMillis())
|
||||
.issueDate(issue)
|
||||
.type("subscription")
|
||||
.subscriptionType("gold")
|
||||
.issuedTo("customer")
|
||||
.issuer("elasticsearch")
|
||||
.maxNodes(5)
|
||||
.build();
|
||||
|
||||
LicenseSigner signer = new LicenseSigner(priKeyPath, pubKeyPath);
|
||||
return signer.sign(licenseSpec);
|
||||
}
|
||||
|
||||
public String getTestPriKeyPath() throws Exception {
|
||||
return getResourcePath("/private.key");
|
||||
}
|
||||
|
||||
public String getTestPubKeyPath() throws Exception {
|
||||
return getResourcePath("/public.key");
|
||||
}
|
||||
|
||||
private String getResourcePath(String resource) throws Exception {
|
||||
return getDataPath(resource).toString();
|
||||
}
|
||||
}
|
|
@ -1,64 +0,0 @@
|
|||
/*
|
||||
* 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.licensor;
|
||||
|
||||
import org.elasticsearch.common.unit.TimeValue;
|
||||
import org.elasticsearch.license.core.License;
|
||||
import org.elasticsearch.license.core.LicenseVerifier;
|
||||
import org.junit.Test;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.Arrays;
|
||||
import java.util.List;
|
||||
import java.util.Set;
|
||||
|
||||
import static org.hamcrest.Matchers.equalTo;
|
||||
|
||||
public class LicenseVerificationTests extends AbstractLicensingTestBase {
|
||||
|
||||
@Test
|
||||
public void testGeneratedLicenses() throws Exception {
|
||||
License shieldLicense = generateSignedLicense("shield", TimeValue.timeValueHours(2 * 24), pubKeyPath, priKeyPath);
|
||||
assertThat(LicenseVerifier.verifyLicense(shieldLicense), equalTo(true));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testMultipleFeatureLicenses() throws Exception {
|
||||
License shieldLicense = generateSignedLicense("shield", TimeValue.timeValueHours(2 * 24), pubKeyPath, priKeyPath);
|
||||
License marvelLicense = generateSignedLicense("marvel", TimeValue.timeValueHours(2 * 24), pubKeyPath, priKeyPath);
|
||||
|
||||
assertThat(LicenseVerifier.verifyLicenses(Arrays.asList(shieldLicense, marvelLicense)), equalTo(true));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testLicenseTampering() throws Exception {
|
||||
License license = generateSignedLicense("shield", TimeValue.timeValueHours(2), pubKeyPath, priKeyPath);
|
||||
|
||||
final License tamperedLicense = License.builder()
|
||||
.fromLicenseSpec(license, license.signature())
|
||||
.expiryDate(license.expiryDate() + 10 * 24 * 60 * 60 * 1000l)
|
||||
.validate()
|
||||
.build();
|
||||
|
||||
assertThat(LicenseVerifier.verifyLicense(tamperedLicense), equalTo(false));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testRandomLicenseVerification() throws Exception {
|
||||
int n = randomIntBetween(5, 15);
|
||||
List<TestUtils.LicenseSpec> licenseSpecs = new ArrayList<>();
|
||||
for (int i = 0; i < n; i++) {
|
||||
licenseSpecs.add(TestUtils.generateRandomLicenseSpec());
|
||||
}
|
||||
|
||||
Set<License> generatedLicenses = generateSignedLicenses(licenseSpecs, pubKeyPath, priKeyPath);
|
||||
assertThat(generatedLicenses.size(), equalTo(n));
|
||||
|
||||
for (License generatedLicense: generatedLicenses) {
|
||||
assertThat(LicenseVerifier.verifyLicense(generatedLicense), equalTo(true));
|
||||
}
|
||||
}
|
||||
}
|
|
@ -1,213 +0,0 @@
|
|||
/*
|
||||
* 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.licensor;
|
||||
|
||||
import org.elasticsearch.common.joda.DateMathParser;
|
||||
import org.elasticsearch.common.joda.FormatDateTimeFormatter;
|
||||
import org.elasticsearch.common.joda.Joda;
|
||||
import org.elasticsearch.common.xcontent.ToXContent;
|
||||
import org.elasticsearch.common.xcontent.XContentBuilder;
|
||||
import org.elasticsearch.common.xcontent.XContentFactory;
|
||||
import org.elasticsearch.common.xcontent.XContentType;
|
||||
import org.elasticsearch.license.core.DateUtils;
|
||||
import org.elasticsearch.license.core.License;
|
||||
import org.elasticsearch.license.core.Licenses;
|
||||
import org.hamcrest.MatcherAssert;
|
||||
import org.joda.time.format.DateTimeFormatter;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.util.*;
|
||||
import java.util.concurrent.Callable;
|
||||
|
||||
import static com.carrotsearch.randomizedtesting.RandomizedTest.*;
|
||||
import static org.elasticsearch.common.xcontent.XContentFactory.jsonBuilder;
|
||||
import static org.elasticsearch.test.ElasticsearchTestCase.randomFrom;
|
||||
import static org.hamcrest.core.IsEqual.equalTo;
|
||||
import static org.junit.Assert.assertThat;
|
||||
|
||||
public class TestUtils {
|
||||
|
||||
public static final String PUBLIC_KEY_RESOURCE = "/public.key";
|
||||
public static final String PRIVATE_KEY_RESOURCE = "/private.key";
|
||||
|
||||
private final static FormatDateTimeFormatter formatDateTimeFormatter = Joda.forPattern("yyyy-MM-dd");
|
||||
private final static DateMathParser dateMathParser = new DateMathParser(formatDateTimeFormatter);
|
||||
private final static DateTimeFormatter dateTimeFormatter = formatDateTimeFormatter.printer();
|
||||
|
||||
public static void isSame(Set<License> firstLicenses, Set<License> secondLicenses) {
|
||||
|
||||
// we do the verifyAndBuild to make sure we weed out any expired licenses
|
||||
final Map<String, License> licenses1 = Licenses.reduceAndMap(firstLicenses);
|
||||
final Map<String, License> licenses2 = Licenses.reduceAndMap(secondLicenses);
|
||||
|
||||
// check if the effective licenses have the same feature set
|
||||
assertThat(licenses1.size(), equalTo(licenses2.size()));
|
||||
|
||||
// for every feature license, check if all the attributes are the same
|
||||
for (String featureType : licenses1.keySet()) {
|
||||
License license1 = licenses1.get(featureType);
|
||||
License license2 = licenses2.get(featureType);
|
||||
isSame(license1, license2);
|
||||
}
|
||||
}
|
||||
|
||||
public static void isSame(License license1, License license2) {
|
||||
assertThat(license1.uid(), equalTo(license2.uid()));
|
||||
assertThat(license1.feature(), equalTo(license2.feature()));
|
||||
assertThat(license1.subscriptionType(), equalTo(license2.subscriptionType()));
|
||||
assertThat(license1.type(), equalTo(license2.type()));
|
||||
assertThat(license1.issuedTo(), equalTo(license2.issuedTo()));
|
||||
assertThat(license1.signature(), equalTo(license2.signature()));
|
||||
assertThat(license1.expiryDate(), equalTo(license2.expiryDate()));
|
||||
assertThat(license1.issueDate(), equalTo(license2.issueDate()));
|
||||
assertThat(license1.maxNodes(), equalTo(license2.maxNodes()));
|
||||
}
|
||||
|
||||
public static String dumpLicense(License license) throws Exception {
|
||||
XContentBuilder builder = XContentFactory.contentBuilder(XContentType.JSON);
|
||||
Licenses.toXContent(Collections.singletonList(license), builder, ToXContent.EMPTY_PARAMS);
|
||||
builder.flush();
|
||||
return builder.string();
|
||||
}
|
||||
|
||||
public static String dateMathString(String time, final long now) {
|
||||
return dateTimeFormatter.print(dateMathParser.parse(time, new Callable<Long>() {
|
||||
@Override
|
||||
public Long call() throws Exception {
|
||||
return now;
|
||||
}
|
||||
}));
|
||||
}
|
||||
|
||||
public static long dateMath(String time, final long now) {
|
||||
return dateMathParser.parse(time, new Callable<Long>() {
|
||||
@Override
|
||||
public Long call() throws Exception {
|
||||
return now;
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
public static LicenseSpec generateRandomLicenseSpec() {
|
||||
boolean datesInMillis = randomBoolean();
|
||||
long now = System.currentTimeMillis();
|
||||
String uid = UUID.randomUUID().toString();
|
||||
String feature = "feature__" + randomInt();
|
||||
String issuer = "issuer__" + randomInt();
|
||||
String issuedTo = "issuedTo__" + randomInt();
|
||||
String type = randomFrom("subscription", "internal", "development");
|
||||
String subscriptionType = randomFrom("none", "gold", "silver", "platinum");
|
||||
int maxNodes = randomIntBetween(5, 100);
|
||||
if (datesInMillis) {
|
||||
long issueDateInMillis = dateMath("now", now);
|
||||
long expiryDateInMillis = dateMath("now+10d/d", now);
|
||||
return new LicenseSpec(uid, feature, issueDateInMillis, expiryDateInMillis, type, subscriptionType, issuedTo, issuer, maxNodes);
|
||||
} else {
|
||||
String issueDate = dateMathString("now", now);
|
||||
String expiryDate = dateMathString("now+10d/d", now);
|
||||
return new LicenseSpec(uid, feature, issueDate, expiryDate, type, subscriptionType, issuedTo, issuer, maxNodes);
|
||||
}
|
||||
}
|
||||
|
||||
public static String generateLicenseSpecString(List<LicenseSpec> licenseSpecs) throws IOException {
|
||||
XContentBuilder licenses = jsonBuilder();
|
||||
licenses.startObject();
|
||||
licenses.startArray("licenses");
|
||||
for (LicenseSpec licenseSpec : licenseSpecs) {
|
||||
licenses.startObject()
|
||||
.field("uid", licenseSpec.uid)
|
||||
.field("type", licenseSpec.type)
|
||||
.field("subscription_type", licenseSpec.subscriptionType)
|
||||
.field("issued_to", licenseSpec.issuedTo)
|
||||
.field("issuer", licenseSpec.issuer)
|
||||
.field("feature", licenseSpec.feature)
|
||||
.field("max_nodes", licenseSpec.maxNodes);
|
||||
|
||||
if (licenseSpec.issueDate != null) {
|
||||
licenses.field("issue_date", licenseSpec.issueDate);
|
||||
} else {
|
||||
licenses.field("issue_date_in_millis", licenseSpec.issueDateInMillis);
|
||||
}
|
||||
if (licenseSpec.expiryDate != null) {
|
||||
licenses.field("expiry_date", licenseSpec.expiryDate);
|
||||
} else {
|
||||
licenses.field("expiry_date_in_millis", licenseSpec.expiryDateInMillis);
|
||||
}
|
||||
licenses.endObject();
|
||||
}
|
||||
licenses.endArray();
|
||||
licenses.endObject();
|
||||
return licenses.string();
|
||||
}
|
||||
|
||||
public static void assertLicenseSpec(LicenseSpec spec, License license) {
|
||||
MatcherAssert.assertThat(license.uid(), equalTo(spec.uid));
|
||||
MatcherAssert.assertThat(license.feature(), equalTo(spec.feature));
|
||||
MatcherAssert.assertThat(license.issuedTo(), equalTo(spec.issuedTo));
|
||||
MatcherAssert.assertThat(license.issuer(), equalTo(spec.issuer));
|
||||
MatcherAssert.assertThat(license.type(), equalTo(spec.type));
|
||||
MatcherAssert.assertThat(license.subscriptionType(), equalTo(spec.subscriptionType));
|
||||
MatcherAssert.assertThat(license.maxNodes(), equalTo(spec.maxNodes));
|
||||
if (spec.issueDate != null) {
|
||||
MatcherAssert.assertThat(license.issueDate(), equalTo(DateUtils.beginningOfTheDay(spec.issueDate)));
|
||||
} else {
|
||||
MatcherAssert.assertThat(license.issueDate(), equalTo(spec.issueDateInMillis));
|
||||
}
|
||||
if (spec.expiryDate != null) {
|
||||
MatcherAssert.assertThat(license.expiryDate(), equalTo(DateUtils.endOfTheDay(spec.expiryDate)));
|
||||
} else {
|
||||
MatcherAssert.assertThat(license.expiryDate(), equalTo(spec.expiryDateInMillis));
|
||||
}
|
||||
}
|
||||
|
||||
public static class LicenseSpec {
|
||||
public final String feature;
|
||||
public final String issueDate;
|
||||
public final long issueDateInMillis;
|
||||
public final String expiryDate;
|
||||
public final long expiryDateInMillis;
|
||||
public final String uid;
|
||||
public final String type;
|
||||
public final String subscriptionType;
|
||||
public final String issuedTo;
|
||||
public final String issuer;
|
||||
public final int maxNodes;
|
||||
|
||||
public LicenseSpec(String feature, String issueDate, String expiryDate) {
|
||||
this(UUID.randomUUID().toString(), feature, issueDate, expiryDate, "trial", "none", "customer", "elasticsearch", 5);
|
||||
}
|
||||
|
||||
public LicenseSpec(String uid, String feature, long issueDateInMillis, long expiryDateInMillis, String type,
|
||||
String subscriptionType, String issuedTo, String issuer, int maxNodes) {
|
||||
this.feature = feature;
|
||||
this.issueDateInMillis = issueDateInMillis;
|
||||
this.issueDate = null;
|
||||
this.expiryDateInMillis = expiryDateInMillis;
|
||||
this.expiryDate = null;
|
||||
this.uid = uid;
|
||||
this.type = type;
|
||||
this.subscriptionType = subscriptionType;
|
||||
this.issuedTo = issuedTo;
|
||||
this.issuer = issuer;
|
||||
this.maxNodes = maxNodes;
|
||||
}
|
||||
|
||||
public LicenseSpec(String uid, String feature, String issueDate, String expiryDate, String type,
|
||||
String subscriptionType, String issuedTo, String issuer, int maxNodes) {
|
||||
this.feature = feature;
|
||||
this.issueDate = issueDate;
|
||||
this.issueDateInMillis = -1;
|
||||
this.expiryDate = expiryDate;
|
||||
this.expiryDateInMillis = -1;
|
||||
this.uid = uid;
|
||||
this.type = type;
|
||||
this.subscriptionType = subscriptionType;
|
||||
this.issuedTo = issuedTo;
|
||||
this.issuer = issuer;
|
||||
this.maxNodes = maxNodes;
|
||||
}
|
||||
}
|
||||
}
|
|
@ -1,100 +0,0 @@
|
|||
/*
|
||||
* 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.licensor.tools;
|
||||
|
||||
import org.apache.commons.cli.MissingOptionException;
|
||||
import org.elasticsearch.common.cli.CliToolTestCase;
|
||||
import org.elasticsearch.common.settings.Settings;
|
||||
import org.elasticsearch.env.Environment;
|
||||
import org.elasticsearch.license.licensor.tools.KeyPairGeneratorTool.KeyGenerator;
|
||||
import org.junit.Test;
|
||||
|
||||
import java.nio.file.Files;
|
||||
import java.nio.file.Path;
|
||||
|
||||
import static org.elasticsearch.common.cli.CliTool.Command;
|
||||
import static org.elasticsearch.common.cli.CliTool.ExitStatus;
|
||||
import static org.hamcrest.CoreMatchers.containsString;
|
||||
import static org.hamcrest.CoreMatchers.instanceOf;
|
||||
import static org.hamcrest.core.IsEqual.equalTo;
|
||||
|
||||
public class KeyPairGenerationToolTests extends CliToolTestCase {
|
||||
|
||||
@Test
|
||||
public void testParsingMissingPath() throws Exception {
|
||||
KeyPairGeneratorTool keyPairGeneratorTool = new KeyPairGeneratorTool();
|
||||
Path tempFile = createTempFile();
|
||||
try {
|
||||
keyPairGeneratorTool.parse(KeyPairGeneratorTool.NAME, args(
|
||||
"--privateKeyPath " + tempFile.toAbsolutePath()));
|
||||
fail("no public key path provided");
|
||||
} catch (MissingOptionException e) {
|
||||
assertThat(e.getMessage(), containsString("pub"));
|
||||
}
|
||||
try {
|
||||
keyPairGeneratorTool.parse(KeyPairGeneratorTool.NAME, args(
|
||||
"--publicKeyPath " + tempFile.toAbsolutePath()));
|
||||
fail("no private key path provided");
|
||||
} catch (MissingOptionException e) {
|
||||
assertThat(e.getMessage(), containsString("pri"));
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testParsingNeverOverrideKey() throws Exception {
|
||||
KeyPairGeneratorTool keyPairGeneratorTool = new KeyPairGeneratorTool();
|
||||
Path tempFile = createTempFile();
|
||||
Path tempFile2 = createTempFile();
|
||||
String nonExistentFilePath = tempFile2.toAbsolutePath().toString();
|
||||
Files.delete(tempFile2);
|
||||
assertThat(Files.exists(tempFile2), equalTo(false));
|
||||
|
||||
Command command = keyPairGeneratorTool.parse(KeyPairGeneratorTool.NAME, new String[] {"--privateKeyPath", tempFile.toAbsolutePath().toString(),
|
||||
"--publicKeyPath", nonExistentFilePath });
|
||||
|
||||
assertThat(command, instanceOf(Command.Exit.class));
|
||||
Command.Exit exitCommand = (Command.Exit) command;
|
||||
assertThat(exitCommand.status(), equalTo(ExitStatus.USAGE));
|
||||
|
||||
command = keyPairGeneratorTool.parse(KeyPairGeneratorTool.NAME, new String[] {"--publicKeyPath", tempFile.toAbsolutePath().toString(),
|
||||
"--privateKeyPath", nonExistentFilePath });
|
||||
|
||||
assertThat(command, instanceOf(Command.Exit.class));
|
||||
exitCommand = (Command.Exit) command;
|
||||
assertThat(exitCommand.status(), equalTo(ExitStatus.USAGE));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testToolSimple() throws Exception {
|
||||
KeyPairGeneratorTool keyPairGeneratorTool = new KeyPairGeneratorTool();
|
||||
Path publicKeyFilePath = createTempFile().toAbsolutePath();
|
||||
Path privateKeyFilePath = createTempFile().toAbsolutePath();
|
||||
Settings settings = Settings.builder().put("path.home", createTempDir("KeyPairGenerationToolTests")).build();
|
||||
|
||||
Files.delete(publicKeyFilePath);
|
||||
Files.delete(privateKeyFilePath);
|
||||
assertThat(Files.exists(publicKeyFilePath), equalTo(false));
|
||||
assertThat(Files.exists(privateKeyFilePath), equalTo(false));
|
||||
|
||||
Command command = keyPairGeneratorTool.parse(KeyPairGeneratorTool.NAME, new String[] { "--privateKeyPath", privateKeyFilePath.toString(),
|
||||
"--publicKeyPath", publicKeyFilePath.toString() });
|
||||
|
||||
assertThat(command, instanceOf(KeyGenerator.class));
|
||||
KeyGenerator keyGenerator = (KeyGenerator) command;
|
||||
assertThat(keyGenerator.privateKeyPath, equalTo(privateKeyFilePath));
|
||||
assertThat(keyGenerator.publicKeyPath, equalTo(publicKeyFilePath));
|
||||
|
||||
assertThat(Files.exists(publicKeyFilePath), equalTo(false));
|
||||
assertThat(Files.exists(privateKeyFilePath), equalTo(false));
|
||||
|
||||
assertThat(keyGenerator.execute(settings, new Environment(settings)), equalTo(ExitStatus.OK));
|
||||
assertThat(Files.exists(publicKeyFilePath), equalTo(true));
|
||||
assertThat(Files.exists(privateKeyFilePath), equalTo(true));
|
||||
|
||||
Files.delete(publicKeyFilePath);
|
||||
Files.delete(privateKeyFilePath);
|
||||
}
|
||||
}
|
|
@ -1,197 +0,0 @@
|
|||
/*
|
||||
* 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.licensor.tools;
|
||||
|
||||
import org.apache.commons.cli.MissingOptionException;
|
||||
import org.elasticsearch.common.cli.CliToolTestCase;
|
||||
import org.elasticsearch.common.settings.Settings;
|
||||
import org.elasticsearch.env.Environment;
|
||||
import org.elasticsearch.license.core.License;
|
||||
import org.elasticsearch.license.core.Licenses;
|
||||
import org.elasticsearch.license.licensor.TestUtils;
|
||||
import org.junit.Before;
|
||||
import org.junit.Test;
|
||||
|
||||
import java.nio.charset.StandardCharsets;
|
||||
import java.nio.file.Files;
|
||||
import java.nio.file.Path;
|
||||
import java.util.*;
|
||||
|
||||
import static org.elasticsearch.common.cli.CliTool.Command;
|
||||
import static org.elasticsearch.common.cli.CliTool.ExitStatus;
|
||||
import static org.elasticsearch.license.licensor.tools.LicenseGeneratorTool.LicenseGenerator;
|
||||
import static org.hamcrest.CoreMatchers.containsString;
|
||||
import static org.hamcrest.CoreMatchers.instanceOf;
|
||||
import static org.hamcrest.Matchers.notNullValue;
|
||||
import static org.hamcrest.core.IsEqual.equalTo;
|
||||
|
||||
public class LicenseGenerationToolTests extends CliToolTestCase {
|
||||
|
||||
protected Path pubKeyPath = null;
|
||||
protected Path priKeyPath = null;
|
||||
|
||||
@Before
|
||||
public void setup() throws Exception {
|
||||
logger.error("project.basedir [{}]", System.getProperty("project.basedir"));
|
||||
pubKeyPath = getDataPath(TestUtils.PUBLIC_KEY_RESOURCE);
|
||||
priKeyPath = getDataPath(TestUtils.PRIVATE_KEY_RESOURCE);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testParsingNonExistentKeyFile() throws Exception {
|
||||
TestUtils.LicenseSpec inputLicenseSpec = TestUtils.generateRandomLicenseSpec();
|
||||
LicenseGeneratorTool licenseGeneratorTool = new LicenseGeneratorTool();
|
||||
Command command = licenseGeneratorTool.parse(LicenseGeneratorTool.NAME,
|
||||
new String[] {"--license", TestUtils.generateLicenseSpecString(Arrays.asList(inputLicenseSpec)),
|
||||
"--publicKeyPath", pubKeyPath.toString().concat("invalid"),
|
||||
"--privateKeyPath", priKeyPath.toString() });
|
||||
|
||||
assertThat(command, instanceOf(Command.Exit.class));
|
||||
Command.Exit exitCommand = (Command.Exit) command;
|
||||
assertThat(exitCommand.status(), equalTo(ExitStatus.USAGE));
|
||||
|
||||
command = licenseGeneratorTool.parse(LicenseGeneratorTool.NAME,
|
||||
new String[] {"--license", TestUtils.generateLicenseSpecString(Arrays.asList(inputLicenseSpec)),
|
||||
"--privateKeyPath", priKeyPath.toString().concat("invalid"),
|
||||
"--publicKeyPath", pubKeyPath.toString() });
|
||||
|
||||
assertThat(command, instanceOf(Command.Exit.class));
|
||||
exitCommand = (Command.Exit) command;
|
||||
assertThat(exitCommand.status(), equalTo(ExitStatus.USAGE));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testParsingMissingLicenseSpec() throws Exception {
|
||||
LicenseGeneratorTool licenseGeneratorTool = new LicenseGeneratorTool();
|
||||
Command command = licenseGeneratorTool.parse(LicenseGeneratorTool.NAME,
|
||||
new String[] { "--publicKeyPath", pubKeyPath.toString(),
|
||||
"--privateKeyPath", priKeyPath.toString() });
|
||||
|
||||
assertThat(command, instanceOf(Command.Exit.class));
|
||||
Command.Exit exitCommand = (Command.Exit) command;
|
||||
assertThat(exitCommand.status(), equalTo(ExitStatus.USAGE));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testParsingMissingArgs() throws Exception {
|
||||
TestUtils.LicenseSpec inputLicenseSpec = TestUtils.generateRandomLicenseSpec();
|
||||
LicenseGeneratorTool licenseGeneratorTool = new LicenseGeneratorTool();
|
||||
boolean pubKeyMissing = randomBoolean();
|
||||
try {
|
||||
licenseGeneratorTool.parse(LicenseGeneratorTool.NAME,
|
||||
new String[] { "--license", TestUtils.generateLicenseSpecString(Arrays.asList(inputLicenseSpec)),
|
||||
((pubKeyMissing) ? "--privateKeyPath" : "--publicKeyPath"),
|
||||
((pubKeyMissing) ? priKeyPath.toString() : pubKeyPath.toString()) });
|
||||
fail("missing argument: " + ((pubKeyMissing) ? "publicKeyPath" : "privateKeyPath") + " should throw an exception");
|
||||
} catch (MissingOptionException e) {
|
||||
assertThat(e.getMessage(), containsString((pubKeyMissing) ? "pub" : "pri"));
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testParsingSimple() throws Exception {
|
||||
TestUtils.LicenseSpec inputLicenseSpec = TestUtils.generateRandomLicenseSpec();
|
||||
LicenseGeneratorTool licenseGeneratorTool = new LicenseGeneratorTool();
|
||||
Command command = licenseGeneratorTool.parse(LicenseGeneratorTool.NAME,
|
||||
new String[]{"--license", TestUtils.generateLicenseSpecString(Arrays.asList(inputLicenseSpec)),
|
||||
"--publicKeyPath", pubKeyPath.toString(),
|
||||
"--privateKeyPath", priKeyPath.toString() });
|
||||
|
||||
assertThat(command, instanceOf(LicenseGenerator.class));
|
||||
LicenseGenerator licenseGenerator = (LicenseGenerator) command;
|
||||
assertThat(licenseGenerator.publicKeyFilePath, equalTo(pubKeyPath));
|
||||
assertThat(licenseGenerator.privateKeyFilePath, equalTo(priKeyPath));
|
||||
assertThat(licenseGenerator.licenseSpecs.size(), equalTo(1));
|
||||
License outputLicenseSpec = licenseGenerator.licenseSpecs.iterator().next();
|
||||
|
||||
TestUtils.assertLicenseSpec(inputLicenseSpec, outputLicenseSpec);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testParsingLicenseFile() throws Exception {
|
||||
TestUtils.LicenseSpec inputLicenseSpec = TestUtils.generateRandomLicenseSpec();
|
||||
Path tempFile = createTempFile();
|
||||
Files.write(tempFile, TestUtils.generateLicenseSpecString(Arrays.asList(inputLicenseSpec)).getBytes(StandardCharsets.UTF_8));
|
||||
|
||||
LicenseGeneratorTool licenseGeneratorTool = new LicenseGeneratorTool();
|
||||
Command command = licenseGeneratorTool.parse(LicenseGeneratorTool.NAME,
|
||||
new String[] { "--licenseFile", tempFile.toAbsolutePath().toString(),
|
||||
"--publicKeyPath", pubKeyPath.toString(),
|
||||
"--privateKeyPath", priKeyPath.toString() });
|
||||
|
||||
assertThat(command, instanceOf(LicenseGenerator.class));
|
||||
LicenseGenerator licenseGenerator = (LicenseGenerator) command;
|
||||
assertThat(licenseGenerator.publicKeyFilePath, equalTo(pubKeyPath));
|
||||
assertThat(licenseGenerator.privateKeyFilePath, equalTo(priKeyPath));
|
||||
assertThat(licenseGenerator.licenseSpecs.size(), equalTo(1));
|
||||
License outputLicenseSpec = licenseGenerator.licenseSpecs.iterator().next();
|
||||
|
||||
TestUtils.assertLicenseSpec(inputLicenseSpec, outputLicenseSpec);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testParsingMultipleLicense() throws Exception {
|
||||
int n = randomIntBetween(2, 5);
|
||||
Map<String, TestUtils.LicenseSpec> inputLicenseSpecs = new HashMap<>();
|
||||
for (int i = 0; i < n; i++) {
|
||||
TestUtils.LicenseSpec licenseSpec = TestUtils.generateRandomLicenseSpec();
|
||||
inputLicenseSpecs.put(licenseSpec.feature, licenseSpec);
|
||||
}
|
||||
LicenseGeneratorTool licenseGeneratorTool = new LicenseGeneratorTool();
|
||||
Command command = licenseGeneratorTool.parse(LicenseGeneratorTool.NAME,
|
||||
new String[] { "--license", TestUtils.generateLicenseSpecString(new ArrayList<>(inputLicenseSpecs.values())),
|
||||
"--publicKeyPath", pubKeyPath.toString(),
|
||||
"--privateKeyPath", priKeyPath.toString() });
|
||||
|
||||
assertThat(command, instanceOf(LicenseGenerator.class));
|
||||
LicenseGenerator licenseGenerator = (LicenseGenerator) command;
|
||||
assertThat(licenseGenerator.publicKeyFilePath, equalTo(pubKeyPath));
|
||||
assertThat(licenseGenerator.privateKeyFilePath, equalTo(priKeyPath));
|
||||
assertThat(licenseGenerator.licenseSpecs.size(), equalTo(inputLicenseSpecs.size()));
|
||||
|
||||
for (License outputLicenseSpec : licenseGenerator.licenseSpecs) {
|
||||
TestUtils.LicenseSpec inputLicenseSpec = inputLicenseSpecs.get(outputLicenseSpec.feature());
|
||||
assertThat(inputLicenseSpec, notNullValue());
|
||||
TestUtils.assertLicenseSpec(inputLicenseSpec, outputLicenseSpec);
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testTool() throws Exception {
|
||||
int n = randomIntBetween(1, 5);
|
||||
Map<String, TestUtils.LicenseSpec> inputLicenseSpecs = new HashMap<>();
|
||||
for (int i = 0; i < n; i++) {
|
||||
TestUtils.LicenseSpec licenseSpec = TestUtils.generateRandomLicenseSpec();
|
||||
inputLicenseSpecs.put(licenseSpec.feature, licenseSpec);
|
||||
}
|
||||
List<License> licenseSpecs = Licenses.fromSource(TestUtils.generateLicenseSpecString(new ArrayList<>(inputLicenseSpecs.values())).getBytes(StandardCharsets.UTF_8), false);
|
||||
|
||||
String output = runLicenseGenerationTool(pubKeyPath, priKeyPath, new HashSet<>(licenseSpecs), ExitStatus.OK);
|
||||
List<License> outputLicenses = Licenses.fromSource(output.getBytes(StandardCharsets.UTF_8), true);
|
||||
assertThat(outputLicenses.size(), equalTo(inputLicenseSpecs.size()));
|
||||
|
||||
for (License outputLicense : outputLicenses) {
|
||||
TestUtils.LicenseSpec inputLicenseSpec = inputLicenseSpecs.get(outputLicense.feature());
|
||||
assertThat(inputLicenseSpec, notNullValue());
|
||||
TestUtils.assertLicenseSpec(inputLicenseSpec, outputLicense);
|
||||
}
|
||||
}
|
||||
|
||||
private String runLicenseGenerationTool(Path pubKeyPath, Path priKeyPath, Set<License> licenseSpecs, ExitStatus expectedExitStatus) throws Exception {
|
||||
CaptureOutputTerminal outputTerminal = new CaptureOutputTerminal();
|
||||
Settings settings = Settings.builder().put("path.home", createTempDir("LicenseGenerationToolTests")).build();
|
||||
LicenseGenerator licenseGenerator = new LicenseGenerator(outputTerminal, pubKeyPath, priKeyPath, licenseSpecs);
|
||||
assertThat(execute(licenseGenerator, settings), equalTo(expectedExitStatus));
|
||||
assertThat(outputTerminal.getTerminalOutput().size(), equalTo(1));
|
||||
return outputTerminal.getTerminalOutput().get(0);
|
||||
}
|
||||
|
||||
|
||||
private ExitStatus execute(Command cmd, Settings settings) throws Exception {
|
||||
Environment env = new Environment(settings);
|
||||
return cmd.execute(settings, env);
|
||||
}
|
||||
}
|
|
@ -1,203 +0,0 @@
|
|||
/*
|
||||
* 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.licensor.tools;
|
||||
|
||||
import org.apache.commons.cli.MissingOptionException;
|
||||
import org.elasticsearch.common.cli.CliToolTestCase;
|
||||
import org.elasticsearch.common.settings.Settings;
|
||||
import org.elasticsearch.common.unit.TimeValue;
|
||||
import org.elasticsearch.env.Environment;
|
||||
import org.elasticsearch.license.core.License;
|
||||
import org.elasticsearch.license.core.Licenses;
|
||||
import org.elasticsearch.license.licensor.AbstractLicensingTestBase;
|
||||
import org.elasticsearch.license.licensor.TestUtils;
|
||||
import org.junit.Test;
|
||||
|
||||
import java.nio.charset.StandardCharsets;
|
||||
import java.nio.file.Files;
|
||||
import java.nio.file.Path;
|
||||
import java.util.*;
|
||||
|
||||
import static org.elasticsearch.common.cli.CliTool.Command;
|
||||
import static org.elasticsearch.common.cli.CliTool.ExitStatus;
|
||||
import static org.elasticsearch.license.licensor.tools.LicenseVerificationTool.LicenseVerifier;
|
||||
import static org.hamcrest.CoreMatchers.instanceOf;
|
||||
import static org.hamcrest.CoreMatchers.notNullValue;
|
||||
import static org.hamcrest.Matchers.containsString;
|
||||
import static org.hamcrest.core.IsEqual.equalTo;
|
||||
|
||||
public class LicenseVerificationToolTests extends CliToolTestCase {
|
||||
|
||||
@Test
|
||||
public void testParsingMissingLicense() throws Exception {
|
||||
LicenseVerificationTool licenseVerificationTool = new LicenseVerificationTool();
|
||||
Path path = getDataPath(TestUtils.PUBLIC_KEY_RESOURCE);
|
||||
Command command = licenseVerificationTool.parse(LicenseVerificationTool.NAME, args(" --publicKeyPath " + path));
|
||||
|
||||
assertThat(command, instanceOf(Command.Exit.class));
|
||||
Command.Exit exitCommand = (Command.Exit) command;
|
||||
assertThat(exitCommand.status(), equalTo(ExitStatus.USAGE));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testParsingMissingPublicKeyPath() throws Exception {
|
||||
Path pubKeyPath = getDataPath(TestUtils.PUBLIC_KEY_RESOURCE);
|
||||
Path priKeyPath = getDataPath(TestUtils.PRIVATE_KEY_RESOURCE);
|
||||
License inputLicense = AbstractLicensingTestBase.generateSignedLicense("feature__1",
|
||||
TimeValue.timeValueHours(1), pubKeyPath.toString(), priKeyPath.toString());
|
||||
LicenseVerificationTool licenseVerificationTool = new LicenseVerificationTool();
|
||||
try {
|
||||
licenseVerificationTool.parse(LicenseVerificationTool.NAME,
|
||||
args("--license " + TestUtils.dumpLicense(inputLicense)));
|
||||
} catch (MissingOptionException e) {
|
||||
assertThat(e.getMessage(), containsString("pub"));
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testParsingNonExistentPublicKeyPath() throws Exception {
|
||||
LicenseVerificationTool licenseVerificationTool = new LicenseVerificationTool();
|
||||
Path path = getDataPath(TestUtils.PUBLIC_KEY_RESOURCE);
|
||||
Command command = licenseVerificationTool.parse(LicenseVerificationTool.NAME, args(" --publicKeyPath "
|
||||
+ path.toString().concat(".invalid")));
|
||||
|
||||
assertThat(command, instanceOf(Command.Exit.class));
|
||||
Command.Exit exitCommand = (Command.Exit) command;
|
||||
assertThat(exitCommand.status(), equalTo(ExitStatus.USAGE));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testParsingSimple() throws Exception {
|
||||
Path pubKeyPath = getDataPath(TestUtils.PUBLIC_KEY_RESOURCE);
|
||||
Path priKeyPath = getDataPath(TestUtils.PRIVATE_KEY_RESOURCE);
|
||||
License inputLicense = AbstractLicensingTestBase.generateSignedLicense("feature__1",
|
||||
TimeValue.timeValueHours(1), pubKeyPath.toString(), priKeyPath.toString());
|
||||
LicenseVerificationTool licenseVerificationTool = new LicenseVerificationTool();
|
||||
Command command = licenseVerificationTool.parse(LicenseVerificationTool.NAME,
|
||||
args("--license " + TestUtils.dumpLicense(inputLicense)
|
||||
+ " --publicKeyPath " + getDataPath(TestUtils.PUBLIC_KEY_RESOURCE)));
|
||||
assertThat(command, instanceOf(LicenseVerifier.class));
|
||||
LicenseVerifier licenseVerifier = (LicenseVerifier) command;
|
||||
assertThat(licenseVerifier.licenses.size(), equalTo(1));
|
||||
License outputLicense = licenseVerifier.licenses.iterator().next();
|
||||
TestUtils.isSame(inputLicense, outputLicense);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testParsingLicenseFile() throws Exception {
|
||||
Path pubKeyPath = getDataPath(TestUtils.PUBLIC_KEY_RESOURCE);
|
||||
Path priKeyPath = getDataPath(TestUtils.PRIVATE_KEY_RESOURCE);
|
||||
License inputLicense = AbstractLicensingTestBase.generateSignedLicense("feature__1",
|
||||
TimeValue.timeValueHours(1), pubKeyPath.toString(), priKeyPath.toString());
|
||||
|
||||
LicenseVerificationTool licenseVerificationTool = new LicenseVerificationTool();
|
||||
Command command = licenseVerificationTool.parse(LicenseVerificationTool.NAME,
|
||||
new String[] { "--licenseFile", dumpLicenseAsFile(inputLicense),
|
||||
"--publicKeyPath", getDataPath(TestUtils.PUBLIC_KEY_RESOURCE).toString() });
|
||||
assertThat(command, instanceOf(LicenseVerifier.class));
|
||||
LicenseVerifier licenseVerifier = (LicenseVerifier) command;
|
||||
assertThat(licenseVerifier.licenses.size(), equalTo(1));
|
||||
License outputLicense = licenseVerifier.licenses.iterator().next();
|
||||
TestUtils.isSame(inputLicense, outputLicense);
|
||||
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testParsingMultipleLicense() throws Exception {
|
||||
Path pubKeyPath = getDataPath(TestUtils.PUBLIC_KEY_RESOURCE);
|
||||
Path priKeyPath = getDataPath(TestUtils.PRIVATE_KEY_RESOURCE);
|
||||
|
||||
int n = randomIntBetween(2, 5);
|
||||
Map<String, License> inputLicenses = new HashMap<>();
|
||||
for (int i = 0; i < n; i++) {
|
||||
License license = AbstractLicensingTestBase.generateSignedLicense("feature__" + i,
|
||||
TimeValue.timeValueHours(1), pubKeyPath.toString(), priKeyPath.toString());
|
||||
inputLicenses.put(license.feature(), license);
|
||||
}
|
||||
|
||||
StringBuilder argsBuilder = new StringBuilder();
|
||||
for (License inputLicense : inputLicenses.values()) {
|
||||
argsBuilder.append(" --license ")
|
||||
.append(TestUtils.dumpLicense(inputLicense));
|
||||
}
|
||||
argsBuilder.append(" --publicKeyPath ").append(getDataPath(TestUtils.PUBLIC_KEY_RESOURCE).toString());
|
||||
LicenseVerificationTool licenseVerificationTool = new LicenseVerificationTool();
|
||||
Command command = licenseVerificationTool.parse(LicenseVerificationTool.NAME, args(argsBuilder.toString()));
|
||||
|
||||
assertThat(command, instanceOf(LicenseVerifier.class));
|
||||
LicenseVerifier licenseVerifier = (LicenseVerifier) command;
|
||||
assertThat(licenseVerifier.licenses.size(), equalTo(inputLicenses.size()));
|
||||
|
||||
for (License outputLicense : licenseVerifier.licenses) {
|
||||
License inputLicense = inputLicenses.get(outputLicense.feature());
|
||||
assertThat(inputLicense, notNullValue());
|
||||
TestUtils.isSame(inputLicense, outputLicense);
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testToolSimple() throws Exception {
|
||||
Path pubKeyPath = getDataPath(TestUtils.PUBLIC_KEY_RESOURCE);
|
||||
Path priKeyPath = getDataPath(TestUtils.PRIVATE_KEY_RESOURCE);
|
||||
|
||||
int n = randomIntBetween(2, 5);
|
||||
Map<String, License> inputLicenses = new HashMap<>();
|
||||
for (int i = 0; i < n; i++) {
|
||||
License license = AbstractLicensingTestBase.generateSignedLicense("feature__" + i,
|
||||
TimeValue.timeValueHours(1), pubKeyPath.toString(), priKeyPath.toString());
|
||||
inputLicenses.put(license.feature(), license);
|
||||
}
|
||||
|
||||
String output = runLicenseVerificationTool(new HashSet<>(inputLicenses.values()), getDataPath(TestUtils.PUBLIC_KEY_RESOURCE), ExitStatus.OK);
|
||||
List<License> outputLicenses = Licenses.fromSource(output.getBytes(StandardCharsets.UTF_8), true);
|
||||
assertThat(outputLicenses.size(), equalTo(inputLicenses.size()));
|
||||
|
||||
for (License outputLicense : outputLicenses) {
|
||||
License inputLicense = inputLicenses.get(outputLicense.feature());
|
||||
assertThat(inputLicense, notNullValue());
|
||||
TestUtils.isSame(inputLicense, outputLicense);
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testToolInvalidLicense() throws Exception {
|
||||
Path pubKeyPath = getDataPath(TestUtils.PUBLIC_KEY_RESOURCE);
|
||||
Path priKeyPath = getDataPath(TestUtils.PRIVATE_KEY_RESOURCE);
|
||||
License signedLicense = AbstractLicensingTestBase.generateSignedLicense("feature__1"
|
||||
, TimeValue.timeValueHours(1), pubKeyPath.toString(), priKeyPath.toString());
|
||||
|
||||
License tamperedLicense = License.builder()
|
||||
.fromLicenseSpec(signedLicense, signedLicense.signature())
|
||||
.expiryDate(signedLicense.expiryDate() + randomIntBetween(1, 1000)).build();
|
||||
|
||||
runLicenseVerificationTool(Collections.singleton(tamperedLicense), getDataPath(TestUtils.PUBLIC_KEY_RESOURCE), ExitStatus.DATA_ERROR);
|
||||
}
|
||||
|
||||
private String dumpLicenseAsFile(License license) throws Exception {
|
||||
Path tempFile = createTempFile();
|
||||
Files.write(tempFile, TestUtils.dumpLicense(license).getBytes(StandardCharsets.UTF_8));
|
||||
return tempFile.toAbsolutePath().toString();
|
||||
}
|
||||
|
||||
private String runLicenseVerificationTool(Set<License> licenses, Path publicKeyPath, ExitStatus expectedExitStatus) throws Exception {
|
||||
CaptureOutputTerminal outputTerminal = new CaptureOutputTerminal();
|
||||
Settings settings = Settings.builder().put("path.home", createTempDir("LicenseVerificationToolTests")).build();
|
||||
LicenseVerifier licenseVerifier = new LicenseVerifier(outputTerminal, licenses, publicKeyPath);
|
||||
assertThat(execute(licenseVerifier, settings), equalTo(expectedExitStatus));
|
||||
if (expectedExitStatus == ExitStatus.OK) {
|
||||
assertThat(outputTerminal.getTerminalOutput().size(), equalTo(1));
|
||||
|
||||
return outputTerminal.getTerminalOutput().get(0);
|
||||
} else {
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
private ExitStatus execute(Command cmd, Settings settings) throws Exception {
|
||||
Environment env = new Environment(settings);
|
||||
return cmd.execute(settings, env);
|
||||
}
|
||||
}
|
|
@ -1,11 +0,0 @@
|
|||
es.logger.level=INFO
|
||||
log4j.rootLogger=${es.logger.level}, out
|
||||
|
||||
log4j.logger.org.apache.http=INFO, out
|
||||
log4j.additivity.org.apache.http=false
|
||||
|
||||
log4j.logger.org.elasticsearch.license=TRACE
|
||||
|
||||
log4j.appender.out=org.apache.log4j.ConsoleAppender
|
||||
log4j.appender.out.layout=org.apache.log4j.PatternLayout
|
||||
log4j.appender.out.layout.conversionPattern=[%d{ISO8601}][%-5p][%-25c] %m%n
|
Binary file not shown.
|
@ -1,3 +0,0 @@
|
|||
ýŽÇqÝnęÄĚgŠśwM}Ťą‡UiKŠ•0âbÖ2Řşqö]â쇴ŻÖĎĂcĚ+IŇđÔ &IJ†fÉ~ßlj <09>ş]d™}o§OčľId®Č
|
||||
5A(ěµ´^ŘöW©DŤŞJµë}ů-Oîë?u N5ľŰvpŰ{’Ľ˛Áôśát–ť¤7ůřĂę#˛Vqöó»ktwm’Ś]ĎLőŁz"| Q‹lźňQđsâ>ů<}Ź[Á2ÖÓŕZÖ|5‹ŻŤĘĘ7%ŘęD
|
||||
Yĺ‘xn:ĽlúLČćHň˘«Ë2<C38B>źHvEEWÇ\¦H:“6Žh9 [!š…Űć©Š¤+;Ö.w7Cě©_|ŢÓŞĎÁ*ń§D`<60>Ú?‚ůxU/3>xUÓ“+ č
|
|
@ -1 +0,0 @@
|
|||
/eclipse-build/
|
|
@ -1,43 +0,0 @@
|
|||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<project xmlns="http://maven.apache.org/POM/4.0.0"
|
||||
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
|
||||
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
|
||||
<parent>
|
||||
<artifactId>elasticsearch-license</artifactId>
|
||||
<groupId>org.elasticsearch</groupId>
|
||||
<version>2.0.0.beta1-SNAPSHOT</version>
|
||||
</parent>
|
||||
<modelVersion>4.0.0</modelVersion>
|
||||
|
||||
<artifactId>elasticsearch-license-plugin-api</artifactId>
|
||||
<properties>
|
||||
<license.basedir combine.self="override">${project.parent.basedir}</license.basedir>
|
||||
<!-- we aren't really a plugin... -->
|
||||
<skip.integ.tests>true</skip.integ.tests>
|
||||
</properties>
|
||||
|
||||
|
||||
<dependencies>
|
||||
<dependency>
|
||||
<groupId>org.elasticsearch</groupId>
|
||||
<artifactId>elasticsearch-license-licensor</artifactId>
|
||||
<version>2.0.0.beta1-SNAPSHOT</version>
|
||||
<scope>test</scope>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.elasticsearch</groupId>
|
||||
<artifactId>elasticsearch-license-core</artifactId>
|
||||
<version>2.0.0.beta1-SNAPSHOT</version>
|
||||
<scope>compile</scope>
|
||||
</dependency>
|
||||
</dependencies>
|
||||
|
||||
<build>
|
||||
<resources>
|
||||
<resource>
|
||||
<directory>src/main/resources</directory>
|
||||
<filtering>true</filtering>
|
||||
</resource>
|
||||
</resources>
|
||||
</build>
|
||||
</project>
|
|
@ -1,191 +0,0 @@
|
|||
/*
|
||||
* 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.plugin;
|
||||
|
||||
import org.elasticsearch.Version;
|
||||
import org.elasticsearch.common.Nullable;
|
||||
import org.elasticsearch.common.Strings;
|
||||
import org.elasticsearch.common.io.stream.StreamInput;
|
||||
import org.elasticsearch.common.io.stream.StreamOutput;
|
||||
import org.elasticsearch.license.core.License;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.io.Serializable;
|
||||
|
||||
@SuppressWarnings("deprecation")
|
||||
public class LicenseVersion implements Serializable {
|
||||
|
||||
// The logic for ID is: XXYYZZAA, where XX is major version, YY is minor version, ZZ is revision, and AA is Beta/RC indicator
|
||||
// AA values below 50 are beta builds, and below 99 are RC builds, with 99 indicating a release
|
||||
// the (internal) format of the id is there so we can easily do after/before checks on the id
|
||||
|
||||
public static final int V_1_0_0_ID = /*00*/1000099;
|
||||
public static final int V_2_0_0_beta1_ID = /*00*/2000001;
|
||||
public static final LicenseVersion V_1_0_0 = new LicenseVersion(V_1_0_0_ID, false, License.VERSION_START, Version.V_1_4_0_Beta1);
|
||||
public static final LicenseVersion V_2_0_0_beta1 = new LicenseVersion(V_2_0_0_beta1_ID, true, License.VERSION_START, Version.V_2_0_0_beta1);
|
||||
|
||||
public static final LicenseVersion CURRENT = V_2_0_0_beta1;
|
||||
|
||||
public static LicenseVersion readVersion(StreamInput in) throws IOException {
|
||||
return fromId(in.readVInt());
|
||||
}
|
||||
|
||||
public static LicenseVersion fromId(int id) {
|
||||
switch (id) {
|
||||
case V_1_0_0_ID:
|
||||
return V_1_0_0;
|
||||
|
||||
default:
|
||||
return new LicenseVersion(id, null, License.VERSION_CURRENT, Version.CURRENT);
|
||||
}
|
||||
}
|
||||
|
||||
public static void writeVersion(LicenseVersion version, StreamOutput out) throws IOException {
|
||||
out.writeVInt(version.id);
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the smallest version between the 2.
|
||||
*/
|
||||
public static LicenseVersion smallest(LicenseVersion version1, LicenseVersion version2) {
|
||||
return version1.id < version2.id ? version1 : version2;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the version given its string representation, current version if the argument is null or empty
|
||||
*/
|
||||
public static LicenseVersion fromString(String version) {
|
||||
if (!Strings.hasLength(version)) {
|
||||
return LicenseVersion.CURRENT;
|
||||
}
|
||||
|
||||
String[] parts = version.split("\\.|\\-");
|
||||
if (parts.length < 3 || parts.length > 4) {
|
||||
throw new IllegalArgumentException("the version needs to contain major, minor and revision, and optionally the build");
|
||||
}
|
||||
|
||||
try {
|
||||
//we reverse the version id calculation based on some assumption as we can't reliably reverse the modulo
|
||||
int major = Integer.parseInt(parts[0]) * 1000000;
|
||||
int minor = Integer.parseInt(parts[1]) * 10000;
|
||||
int revision = Integer.parseInt(parts[2]) * 100;
|
||||
|
||||
int build = 99;
|
||||
if (parts.length == 4) {
|
||||
String buildStr = parts[3];
|
||||
if (buildStr.startsWith("beta")) {
|
||||
build = Integer.parseInt(buildStr.substring(4));
|
||||
}
|
||||
if (buildStr.startsWith("rc")) {
|
||||
build = Integer.parseInt(buildStr.substring(2)) + 50;
|
||||
}
|
||||
}
|
||||
|
||||
return fromId(major + minor + revision + build);
|
||||
|
||||
} catch(NumberFormatException e) {
|
||||
throw new IllegalArgumentException("unable to parse version " + version, e);
|
||||
}
|
||||
}
|
||||
|
||||
public final int id;
|
||||
public final byte major;
|
||||
public final byte minor;
|
||||
public final byte revision;
|
||||
public final byte build;
|
||||
public final Boolean snapshot;
|
||||
public final int minSignatureVersion;
|
||||
public final Version minEsCompatibilityVersion;
|
||||
|
||||
LicenseVersion(int id, @Nullable Boolean snapshot, int minSignatureVersion, Version minEsCompatibilityVersion) {
|
||||
this.id = id;
|
||||
this.major = (byte) ((id / 1000000) % 100);
|
||||
this.minor = (byte) ((id / 10000) % 100);
|
||||
this.revision = (byte) ((id / 100) % 100);
|
||||
this.build = (byte) (id % 100);
|
||||
this.snapshot = snapshot;
|
||||
this.minSignatureVersion = minSignatureVersion;
|
||||
this.minEsCompatibilityVersion = minEsCompatibilityVersion;
|
||||
}
|
||||
|
||||
public boolean snapshot() {
|
||||
return snapshot != null && snapshot;
|
||||
}
|
||||
|
||||
public boolean after(LicenseVersion version) {
|
||||
return version.id < id;
|
||||
}
|
||||
|
||||
public boolean before(LicenseVersion version) {
|
||||
return version.id > id;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the minimum compatible version based on the current
|
||||
* version. Ie a node needs to have at least the return version in order
|
||||
* to communicate with a node running the current version. The returned version
|
||||
* is in most of the cases the smallest major version release unless the current version
|
||||
* is a beta or RC release then the version itself is returned.
|
||||
*/
|
||||
public LicenseVersion minimumCompatibilityVersion() {
|
||||
return LicenseVersion.smallest(this, fromId(major * 1000000 + 99));
|
||||
}
|
||||
|
||||
/**
|
||||
* @return The minimum elasticsearch version this license version is compatible with.
|
||||
*/
|
||||
public Version minimumEsCompatiblityVersion() {
|
||||
return minEsCompatibilityVersion;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return The minimum license signature version this license plugin is compatible with.
|
||||
*/
|
||||
public int minimumSignatureVersion() {
|
||||
return minSignatureVersion;
|
||||
}
|
||||
|
||||
/**
|
||||
* Just the version number (without -SNAPSHOT if snapshot).
|
||||
*/
|
||||
public String number() {
|
||||
StringBuilder sb = new StringBuilder();
|
||||
sb.append(major).append('.').append(minor).append('.').append(revision);
|
||||
if (build < 50) {
|
||||
sb.append("-beta").append(build);
|
||||
} else if (build < 99) {
|
||||
sb.append("-rc").append(build - 50);
|
||||
}
|
||||
return sb.toString();
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
StringBuilder sb = new StringBuilder();
|
||||
sb.append(number());
|
||||
if (snapshot()) {
|
||||
sb.append("-SNAPSHOT");
|
||||
}
|
||||
return sb.toString();
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean equals(Object o) {
|
||||
if (this == o) return true;
|
||||
if (o == null || getClass() != o.getClass()) return false;
|
||||
|
||||
LicenseVersion that = (LicenseVersion) o;
|
||||
|
||||
if (id != that.id) return false;
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
@Override
|
||||
public int hashCode() {
|
||||
return id;
|
||||
}
|
||||
}
|
|
@ -1,27 +0,0 @@
|
|||
/*
|
||||
* 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.plugin.core;
|
||||
|
||||
import org.elasticsearch.ElasticsearchSecurityException;
|
||||
import org.elasticsearch.rest.RestStatus;
|
||||
|
||||
public class LicenseUtils {
|
||||
|
||||
public final static String EXPIRED_FEATURE_HEADER = "es.license.expired.feature";
|
||||
|
||||
/**
|
||||
* Exception to be thrown when a feature action requires a valid license, but license
|
||||
* has expired
|
||||
*
|
||||
* <code>feature</code> accessible through {@link #EXPIRED_FEATURE_HEADER} in the
|
||||
* exception's rest header
|
||||
*/
|
||||
public static ElasticsearchSecurityException newExpirationException(String feature) {
|
||||
ElasticsearchSecurityException e = new ElasticsearchSecurityException("license expired for feature [{}]", RestStatus.UNAUTHORIZED, feature);
|
||||
e.addHeader(EXPIRED_FEATURE_HEADER, feature);
|
||||
return e;
|
||||
}
|
||||
}
|
|
@ -1,163 +0,0 @@
|
|||
/*
|
||||
* 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.plugin.core;
|
||||
|
||||
import org.elasticsearch.common.unit.TimeValue;
|
||||
import org.elasticsearch.license.core.License;
|
||||
|
||||
import java.util.Collection;
|
||||
|
||||
|
||||
public interface LicensesClientService {
|
||||
|
||||
public interface Listener {
|
||||
|
||||
/**
|
||||
* Called to enable a feature
|
||||
*/
|
||||
public void onEnabled(License license);
|
||||
|
||||
/**
|
||||
* Called to disable a feature
|
||||
*/
|
||||
public void onDisabled(License license);
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* Registers a feature for licensing
|
||||
*
|
||||
* @param feature - name of the feature to register (must be in sync with license Generator feature name)
|
||||
* @param trialLicenseOptions - Trial license specification used to generate a one-time trial license for the feature;
|
||||
* use <code>null</code> if no trial license should be generated for the feature
|
||||
* @param expirationCallbacks - A collection of Pre and/or Post expiration callbacks
|
||||
* @param listener - used to notify on feature enable/disable
|
||||
*/
|
||||
void register(String feature, TrialLicenseOptions trialLicenseOptions, Collection<ExpirationCallback> expirationCallbacks, Listener listener);
|
||||
|
||||
public static class TrialLicenseOptions {
|
||||
final TimeValue duration;
|
||||
final int maxNodes;
|
||||
|
||||
public TrialLicenseOptions(TimeValue duration, int maxNodes) {
|
||||
this.duration = duration;
|
||||
this.maxNodes = maxNodes;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
public static interface LicenseCallback {
|
||||
void on(License license, ExpirationStatus status);
|
||||
}
|
||||
|
||||
public static abstract class ExpirationCallback implements LicenseCallback {
|
||||
|
||||
public enum Orientation { PRE, POST }
|
||||
|
||||
public static abstract class Pre extends ExpirationCallback {
|
||||
|
||||
/**
|
||||
* Callback schedule prior to license expiry
|
||||
*
|
||||
* @param min latest relative time to execute before license expiry
|
||||
* @param max earliest relative time to execute before license expiry
|
||||
* @param frequency interval between execution
|
||||
*/
|
||||
public Pre(TimeValue min, TimeValue max, TimeValue frequency) {
|
||||
super(Orientation.PRE, min, max, frequency);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean matches(long expirationDate, long now) {
|
||||
long expiryDuration = expirationDate - now;
|
||||
if (expiryDuration > 0l) {
|
||||
if (expiryDuration <= max().getMillis()) {
|
||||
return expiryDuration >= min().getMillis();
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
public static abstract class Post extends ExpirationCallback {
|
||||
|
||||
/**
|
||||
* Callback schedule after license expiry
|
||||
*
|
||||
* @param min earliest relative time to execute after license expiry
|
||||
* @param max latest relative time to execute after license expiry
|
||||
* @param frequency interval between execution
|
||||
*/
|
||||
public Post(TimeValue min, TimeValue max, TimeValue frequency) {
|
||||
super(Orientation.POST, min, max, frequency);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean matches(long expirationDate, long now) {
|
||||
long postExpiryDuration = now - expirationDate;
|
||||
if (postExpiryDuration > 0l) {
|
||||
if (postExpiryDuration <= max().getMillis()) {
|
||||
return postExpiryDuration >= min().getMillis();
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
private final Orientation orientation;
|
||||
private final TimeValue min;
|
||||
private final TimeValue max;
|
||||
private final TimeValue frequency;
|
||||
|
||||
private ExpirationCallback(Orientation orientation, TimeValue min, TimeValue max, TimeValue frequency) {
|
||||
this.orientation = orientation;
|
||||
this.min = (min == null) ? TimeValue.timeValueMillis(0) : min;
|
||||
this.max = (max == null) ? TimeValue.timeValueMillis(Long.MAX_VALUE) : max;
|
||||
this.frequency = frequency;
|
||||
if (frequency == null) {
|
||||
throw new IllegalArgumentException("frequency can not be null");
|
||||
}
|
||||
}
|
||||
|
||||
public Orientation orientation() {
|
||||
return orientation;
|
||||
}
|
||||
|
||||
public TimeValue min() {
|
||||
return min;
|
||||
}
|
||||
|
||||
public TimeValue max() {
|
||||
return max;
|
||||
}
|
||||
|
||||
public TimeValue frequency() {
|
||||
return frequency;
|
||||
}
|
||||
|
||||
public abstract boolean matches(long expirationDate, long now);
|
||||
}
|
||||
|
||||
public static class ExpirationStatus {
|
||||
private final boolean expired;
|
||||
private final TimeValue time;
|
||||
|
||||
ExpirationStatus(boolean expired, TimeValue time) {
|
||||
this.expired = expired;
|
||||
this.time = time;
|
||||
}
|
||||
|
||||
public boolean expired() {
|
||||
return expired;
|
||||
}
|
||||
|
||||
public TimeValue time() {
|
||||
return time;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
}
|
|
@ -1,54 +0,0 @@
|
|||
/*
|
||||
* 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.plugin;
|
||||
|
||||
import org.elasticsearch.test.ElasticsearchTestCase;
|
||||
import org.junit.Test;
|
||||
|
||||
import static org.hamcrest.Matchers.equalTo;
|
||||
import static org.hamcrest.Matchers.is;
|
||||
|
||||
/**
|
||||
*
|
||||
*/
|
||||
public class LicenseVersionTests extends ElasticsearchTestCase {
|
||||
|
||||
@Test
|
||||
public void testStrings() throws Exception {
|
||||
boolean beta = randomBoolean();
|
||||
int buildNumber = beta ? randomIntBetween(0, 49) : randomIntBetween(0, 48);
|
||||
int major = randomIntBetween(0, 20);
|
||||
int minor = randomIntBetween(0, 20);
|
||||
int revision = randomIntBetween(0, 20);
|
||||
|
||||
String build = buildNumber == 0 ? "" :
|
||||
beta ? "-beta" + buildNumber : "-rc" + buildNumber;
|
||||
|
||||
|
||||
String versionName = new StringBuilder()
|
||||
.append(major)
|
||||
.append(".").append(minor)
|
||||
.append(".").append(revision)
|
||||
.append(build).toString();
|
||||
LicenseVersion version = LicenseVersion.fromString(versionName);
|
||||
|
||||
logger.info("version: {}", versionName);
|
||||
|
||||
assertThat(version.major, is((byte) major));
|
||||
assertThat(version.minor, is((byte) minor));
|
||||
assertThat(version.revision, is((byte) revision));
|
||||
if (buildNumber == 0) {
|
||||
assertThat(version.build, is((byte) 99));
|
||||
} else if (beta) {
|
||||
assertThat(version.build, is((byte) buildNumber));
|
||||
} else {
|
||||
assertThat(version.build, is((byte) (buildNumber + 50)));
|
||||
}
|
||||
|
||||
assertThat(version.number(), equalTo(versionName));
|
||||
}
|
||||
|
||||
}
|
|
@ -1 +0,0 @@
|
|||
/eclipse-build/
|
125
plugin/pom.xml
125
plugin/pom.xml
|
@ -1,125 +0,0 @@
|
|||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<project xmlns="http://maven.apache.org/POM/4.0.0"
|
||||
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
|
||||
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
|
||||
<parent>
|
||||
<artifactId>elasticsearch-license</artifactId>
|
||||
<groupId>org.elasticsearch</groupId>
|
||||
<version>2.0.0.beta1-SNAPSHOT</version>
|
||||
</parent>
|
||||
<modelVersion>4.0.0</modelVersion>
|
||||
|
||||
<artifactId>elasticsearch-license-plugin</artifactId>
|
||||
|
||||
<properties>
|
||||
<!-- TODO: do we want to always enforce non-default keys
|
||||
-->
|
||||
<keys.path>${basedir}/src/test/resources</keys.path>
|
||||
<license.basedir combine.self="override">${project.parent.basedir}</license.basedir>
|
||||
<tests.rest.suite>license</tests.rest.suite>
|
||||
<tests.rest.load_packaged>false</tests.rest.load_packaged>
|
||||
</properties>
|
||||
|
||||
<dependencies>
|
||||
<dependency>
|
||||
<groupId>org.elasticsearch</groupId>
|
||||
<artifactId>elasticsearch-license-licensor</artifactId>
|
||||
<version>2.0.0.beta1-SNAPSHOT</version>
|
||||
<scope>test</scope>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.elasticsearch</groupId>
|
||||
<artifactId>elasticsearch-license-core</artifactId>
|
||||
<version>2.0.0.beta1-SNAPSHOT</version>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.elasticsearch</groupId>
|
||||
<artifactId>elasticsearch-license-plugin-api</artifactId>
|
||||
<version>2.0.0.beta1-SNAPSHOT</version>
|
||||
</dependency>
|
||||
</dependencies>
|
||||
|
||||
<build>
|
||||
<resources>
|
||||
<resource>
|
||||
<directory>${keys.path}</directory>
|
||||
<filtering>false</filtering>
|
||||
<includes>
|
||||
<include>public.key</include>
|
||||
</includes>
|
||||
</resource>
|
||||
<resource>
|
||||
<directory>src/main/resources</directory>
|
||||
<filtering>true</filtering>
|
||||
</resource>
|
||||
</resources>
|
||||
|
||||
<plugins>
|
||||
<plugin>
|
||||
<groupId>org.apache.maven.plugins</groupId>
|
||||
<artifactId>maven-assembly-plugin</artifactId>
|
||||
</plugin>
|
||||
|
||||
<plugin>
|
||||
<groupId>org.apache.maven.plugins</groupId>
|
||||
<artifactId>maven-enforcer-plugin</artifactId>
|
||||
<executions>
|
||||
<!-- TODO: do we want to always enforce non-default keys -->
|
||||
<execution>
|
||||
<id>enforce-property</id>
|
||||
<goals>
|
||||
<goal>enforce</goal>
|
||||
</goals>
|
||||
<configuration>
|
||||
<rules>
|
||||
<requireProperty>
|
||||
<property>keys.path</property>
|
||||
<message>"You must set a keys.path property!"</message>
|
||||
</requireProperty>
|
||||
<requireFilesExist>
|
||||
<files>
|
||||
<file>${keys.path}/public.key</file>
|
||||
</files>
|
||||
<message>"public.key file does not exist in ${keys.path} directory!"</message>
|
||||
</requireFilesExist>
|
||||
</rules>
|
||||
<fail>true</fail>
|
||||
</configuration>
|
||||
</execution>
|
||||
</executions>
|
||||
</plugin>
|
||||
|
||||
<plugin>
|
||||
<groupId>org.apache.maven.plugins</groupId>
|
||||
<artifactId>maven-jar-plugin</artifactId>
|
||||
<executions>
|
||||
<execution>
|
||||
<id>for-plugin</id>
|
||||
<phase>prepare-package</phase>
|
||||
<goals>
|
||||
<goal>jar</goal>
|
||||
</goals>
|
||||
</execution>
|
||||
</executions>
|
||||
</plugin>
|
||||
|
||||
<plugin>
|
||||
<groupId>com.carrotsearch.randomizedtesting</groupId>
|
||||
<artifactId>junit4-maven-plugin</artifactId>
|
||||
<executions>
|
||||
<execution>
|
||||
<id>tests</id>
|
||||
<phase>test</phase>
|
||||
<goals>
|
||||
<goal>junit4</goal>
|
||||
</goals>
|
||||
<configuration>
|
||||
<heartbeat combine.self="override">40</heartbeat>
|
||||
</configuration>
|
||||
</execution>
|
||||
</executions>
|
||||
</plugin>
|
||||
</plugins>
|
||||
|
||||
</build>
|
||||
</project>
|
|
@ -1,19 +0,0 @@
|
|||
{
|
||||
"licenses": {
|
||||
"documentation": "https://www.elastic.co/guide/en/shield/current/license-management.html",
|
||||
"methods": ["GET"],
|
||||
"url": {
|
||||
"path": "/_licenses",
|
||||
"paths": ["/_licenses"],
|
||||
"parts" : {
|
||||
},
|
||||
"params": {
|
||||
"local": {
|
||||
"type" : "boolean",
|
||||
"description" : "Return local information, do not retrieve the state from master node (default: false)"
|
||||
}
|
||||
}
|
||||
},
|
||||
"body": null
|
||||
}
|
||||
}
|
|
@ -1,4 +0,0 @@
|
|||
---
|
||||
"licenses endpoint exists":
|
||||
- do: {licenses: {}}
|
||||
- match: {licenses: []}
|
|
@ -1,22 +0,0 @@
|
|||
<?xml version="1.0"?>
|
||||
<assembly>
|
||||
<id>plugin</id>
|
||||
<formats>
|
||||
<format>zip</format>
|
||||
</formats>
|
||||
<includeBaseDirectory>false</includeBaseDirectory>
|
||||
<dependencySets>
|
||||
<dependencySet>
|
||||
<outputDirectory>/</outputDirectory>
|
||||
<useProjectArtifact>true</useProjectArtifact>
|
||||
<includes>
|
||||
<include>org.elasticsearch:elasticsearch-license-plugin</include>
|
||||
<include>org.elasticsearch:elasticsearch-license-plugin-api</include>
|
||||
<include>org.elasticsearch:elasticsearch-license-core</include>
|
||||
</includes>
|
||||
<excludes>
|
||||
<exclude>org.elasticsearch:elasticsearch</exclude>
|
||||
</excludes>
|
||||
</dependencySet>
|
||||
</dependencySets>
|
||||
</assembly>
|
|
@ -1,21 +0,0 @@
|
|||
/*
|
||||
* 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.plugin;
|
||||
|
||||
import org.elasticsearch.common.inject.AbstractModule;
|
||||
import org.elasticsearch.common.inject.Scopes;
|
||||
import org.elasticsearch.license.core.LicenseVerifier;
|
||||
import org.elasticsearch.license.plugin.core.LicensesClientService;
|
||||
import org.elasticsearch.license.plugin.core.LicensesService;
|
||||
|
||||
public class LicenseModule extends AbstractModule {
|
||||
@Override
|
||||
protected void configure() {
|
||||
bind(LicenseVerifier.class).in(Scopes.SINGLETON);
|
||||
bind(LicensesService.class).in(Scopes.SINGLETON);
|
||||
bind(LicensesClientService.class).to(LicensesService.class).in(Scopes.SINGLETON);
|
||||
}
|
||||
}
|
|
@ -1,93 +0,0 @@
|
|||
/*
|
||||
* 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.plugin;
|
||||
|
||||
import com.google.common.collect.ImmutableSet;
|
||||
import com.google.common.collect.Lists;
|
||||
import org.elasticsearch.action.ActionModule;
|
||||
import org.elasticsearch.client.Client;
|
||||
import org.elasticsearch.cluster.metadata.MetaData;
|
||||
import org.elasticsearch.cluster.node.DiscoveryNode;
|
||||
import org.elasticsearch.common.component.LifecycleComponent;
|
||||
import org.elasticsearch.common.inject.Inject;
|
||||
import org.elasticsearch.common.inject.Module;
|
||||
import org.elasticsearch.common.settings.Settings;
|
||||
import org.elasticsearch.license.plugin.action.delete.DeleteLicenseAction;
|
||||
import org.elasticsearch.license.plugin.action.delete.TransportDeleteLicenseAction;
|
||||
import org.elasticsearch.license.plugin.action.get.GetLicenseAction;
|
||||
import org.elasticsearch.license.plugin.action.get.TransportGetLicenseAction;
|
||||
import org.elasticsearch.license.plugin.action.put.PutLicenseAction;
|
||||
import org.elasticsearch.license.plugin.action.put.TransportPutLicenseAction;
|
||||
import org.elasticsearch.license.plugin.core.LicensesMetaData;
|
||||
import org.elasticsearch.license.plugin.core.LicensesService;
|
||||
import org.elasticsearch.license.plugin.rest.RestDeleteLicenseAction;
|
||||
import org.elasticsearch.license.plugin.rest.RestGetLicenseAction;
|
||||
import org.elasticsearch.license.plugin.rest.RestPutLicenseAction;
|
||||
import org.elasticsearch.plugins.AbstractPlugin;
|
||||
import org.elasticsearch.rest.RestModule;
|
||||
|
||||
import java.util.Collection;
|
||||
|
||||
public class LicensePlugin extends AbstractPlugin {
|
||||
|
||||
public static final String NAME = "license";
|
||||
private final boolean isEnabled;
|
||||
|
||||
static {
|
||||
MetaData.registerPrototype(LicensesMetaData.TYPE, LicensesMetaData.PROTO);
|
||||
}
|
||||
|
||||
@Inject
|
||||
public LicensePlugin(Settings settings) {
|
||||
if (DiscoveryNode.clientNode(settings)) {
|
||||
// Enable plugin only on node clients
|
||||
this.isEnabled = "node".equals(settings.get(Client.CLIENT_TYPE_SETTING));
|
||||
} else {
|
||||
this.isEnabled = true;
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public String name() {
|
||||
return NAME;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String description() {
|
||||
return "Internal Elasticsearch Licensing Plugin";
|
||||
}
|
||||
|
||||
public void onModule(RestModule module) {
|
||||
// Register REST endpoint
|
||||
module.addRestAction(RestPutLicenseAction.class);
|
||||
module.addRestAction(RestGetLicenseAction.class);
|
||||
module.addRestAction(RestDeleteLicenseAction.class);
|
||||
}
|
||||
|
||||
public void onModule(ActionModule module) {
|
||||
module.registerAction(PutLicenseAction.INSTANCE, TransportPutLicenseAction.class);
|
||||
module.registerAction(GetLicenseAction.INSTANCE, TransportGetLicenseAction.class);
|
||||
module.registerAction(DeleteLicenseAction.INSTANCE, TransportDeleteLicenseAction.class);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Collection<Class<? extends LifecycleComponent>> services() {
|
||||
Collection<Class<? extends LifecycleComponent>> services = Lists.newArrayList();
|
||||
if (isEnabled) {
|
||||
services.add(LicensesService.class);
|
||||
}
|
||||
return services;
|
||||
}
|
||||
|
||||
|
||||
@Override
|
||||
public Collection<Class<? extends Module>> modules() {
|
||||
if (isEnabled) {
|
||||
return ImmutableSet.<Class<? extends Module>>of(LicenseModule.class);
|
||||
}
|
||||
return ImmutableSet.of();
|
||||
}
|
||||
}
|
|
@ -1,29 +0,0 @@
|
|||
/*
|
||||
* 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.plugin.action.delete;
|
||||
|
||||
import org.elasticsearch.action.Action;
|
||||
import org.elasticsearch.client.ElasticsearchClient;
|
||||
|
||||
public class DeleteLicenseAction extends Action<DeleteLicenseRequest, DeleteLicenseResponse, DeleteLicenseRequestBuilder> {
|
||||
|
||||
public static final DeleteLicenseAction INSTANCE = new DeleteLicenseAction();
|
||||
public static final String NAME = "cluster:admin/plugin/license/delete";
|
||||
|
||||
private DeleteLicenseAction() {
|
||||
super(NAME);
|
||||
}
|
||||
|
||||
@Override
|
||||
public DeleteLicenseResponse newResponse() {
|
||||
return new DeleteLicenseResponse();
|
||||
}
|
||||
|
||||
@Override
|
||||
public DeleteLicenseRequestBuilder newRequestBuilder(ElasticsearchClient client) {
|
||||
return new DeleteLicenseRequestBuilder(client, this);
|
||||
}
|
||||
}
|
|
@ -1,56 +0,0 @@
|
|||
/*
|
||||
* 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.plugin.action.delete;
|
||||
|
||||
import org.elasticsearch.action.ActionRequestValidationException;
|
||||
import org.elasticsearch.action.support.master.AcknowledgedRequest;
|
||||
import org.elasticsearch.common.io.stream.StreamInput;
|
||||
import org.elasticsearch.common.io.stream.StreamOutput;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.util.Arrays;
|
||||
import java.util.HashSet;
|
||||
import java.util.Set;
|
||||
|
||||
|
||||
public class DeleteLicenseRequest extends AcknowledgedRequest<DeleteLicenseRequest> {
|
||||
|
||||
private String[] features;
|
||||
|
||||
public DeleteLicenseRequest() {
|
||||
}
|
||||
|
||||
public DeleteLicenseRequest(String... features) {
|
||||
this.features = features;
|
||||
}
|
||||
|
||||
public void features(Set<String> features) {
|
||||
this.features = features.toArray(new String[features.size()]);
|
||||
}
|
||||
|
||||
public Set<String> features() {
|
||||
return new HashSet<>(Arrays.asList(features));
|
||||
}
|
||||
|
||||
@Override
|
||||
public ActionRequestValidationException validate() {
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void readFrom(StreamInput in) throws IOException {
|
||||
super.readFrom(in);
|
||||
features = in.readStringArray();
|
||||
readTimeout(in);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void writeTo(StreamOutput out) throws IOException {
|
||||
super.writeTo(out);
|
||||
out.writeStringArray(features);
|
||||
writeTimeout(out);
|
||||
}
|
||||
}
|
|
@ -1,29 +0,0 @@
|
|||
/*
|
||||
* 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.plugin.action.delete;
|
||||
|
||||
import org.elasticsearch.action.support.master.AcknowledgedRequestBuilder;
|
||||
import org.elasticsearch.client.ClusterAdminClient;
|
||||
import org.elasticsearch.client.ElasticsearchClient;
|
||||
|
||||
import java.util.Set;
|
||||
|
||||
public class DeleteLicenseRequestBuilder extends AcknowledgedRequestBuilder<DeleteLicenseRequest, DeleteLicenseResponse, DeleteLicenseRequestBuilder> {
|
||||
|
||||
/**
|
||||
* Creates new get licenses request builder
|
||||
*
|
||||
* @param client elasticsearch client
|
||||
*/
|
||||
public DeleteLicenseRequestBuilder(ElasticsearchClient client, DeleteLicenseAction action) {
|
||||
super(client, action, new DeleteLicenseRequest());
|
||||
}
|
||||
|
||||
public DeleteLicenseRequestBuilder setFeatures(Set<String> features) {
|
||||
request.features(features);
|
||||
return this;
|
||||
}
|
||||
}
|
|
@ -1,35 +0,0 @@
|
|||
/*
|
||||
* 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.plugin.action.delete;
|
||||
|
||||
import org.elasticsearch.action.support.master.AcknowledgedResponse;
|
||||
import org.elasticsearch.common.io.stream.StreamInput;
|
||||
import org.elasticsearch.common.io.stream.StreamOutput;
|
||||
|
||||
import java.io.IOException;
|
||||
|
||||
public class DeleteLicenseResponse extends AcknowledgedResponse {
|
||||
|
||||
DeleteLicenseResponse() {
|
||||
}
|
||||
|
||||
DeleteLicenseResponse(boolean acknowledged) {
|
||||
super(acknowledged);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void readFrom(StreamInput in) throws IOException {
|
||||
super.readFrom(in);
|
||||
readAcknowledged(in);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void writeTo(StreamOutput out) throws IOException {
|
||||
super.writeTo(out);
|
||||
writeAcknowledged(out);
|
||||
}
|
||||
|
||||
}
|
|
@ -1,67 +0,0 @@
|
|||
/*
|
||||
* 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.plugin.action.delete;
|
||||
|
||||
import org.elasticsearch.ElasticsearchException;
|
||||
import org.elasticsearch.action.ActionListener;
|
||||
import org.elasticsearch.action.support.ActionFilters;
|
||||
import org.elasticsearch.action.support.master.TransportMasterNodeAction;
|
||||
import org.elasticsearch.cluster.ClusterService;
|
||||
import org.elasticsearch.cluster.ClusterState;
|
||||
import org.elasticsearch.cluster.ack.ClusterStateUpdateResponse;
|
||||
import org.elasticsearch.cluster.block.ClusterBlockException;
|
||||
import org.elasticsearch.cluster.block.ClusterBlockLevel;
|
||||
import org.elasticsearch.cluster.metadata.IndexNameExpressionResolver;
|
||||
import org.elasticsearch.common.inject.Inject;
|
||||
import org.elasticsearch.common.settings.Settings;
|
||||
import org.elasticsearch.license.plugin.core.LicensesManagerService;
|
||||
import org.elasticsearch.threadpool.ThreadPool;
|
||||
import org.elasticsearch.transport.TransportService;
|
||||
|
||||
import static org.elasticsearch.license.plugin.core.LicensesService.DeleteLicenseRequestHolder;
|
||||
|
||||
public class TransportDeleteLicenseAction extends TransportMasterNodeAction<DeleteLicenseRequest, DeleteLicenseResponse> {
|
||||
|
||||
private final LicensesManagerService licensesManagerService;
|
||||
|
||||
@Inject
|
||||
public TransportDeleteLicenseAction(Settings settings, TransportService transportService, ClusterService clusterService, LicensesManagerService licensesManagerService,
|
||||
ThreadPool threadPool, ActionFilters actionFilters, IndexNameExpressionResolver indexNameExpressionResolver) {
|
||||
super(settings, DeleteLicenseAction.NAME, transportService, clusterService, threadPool, actionFilters, indexNameExpressionResolver, DeleteLicenseRequest.class);
|
||||
this.licensesManagerService = licensesManagerService;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected String executor() {
|
||||
return ThreadPool.Names.MANAGEMENT;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected DeleteLicenseResponse newResponse() {
|
||||
return new DeleteLicenseResponse();
|
||||
}
|
||||
|
||||
@Override
|
||||
protected ClusterBlockException checkBlock(DeleteLicenseRequest request, ClusterState state) {
|
||||
return state.blocks().globalBlockedException(ClusterBlockLevel.METADATA_WRITE);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void masterOperation(final DeleteLicenseRequest request, ClusterState state, final ActionListener<DeleteLicenseResponse> listener) throws ElasticsearchException {
|
||||
final DeleteLicenseRequestHolder requestHolder = new DeleteLicenseRequestHolder(request, "delete licenses []");
|
||||
licensesManagerService.removeLicenses(requestHolder, new ActionListener<ClusterStateUpdateResponse>() {
|
||||
@Override
|
||||
public void onResponse(ClusterStateUpdateResponse clusterStateUpdateResponse) {
|
||||
listener.onResponse(new DeleteLicenseResponse(clusterStateUpdateResponse.isAcknowledged()));
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onFailure(Throwable e) {
|
||||
listener.onFailure(e);
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
|
@ -1,29 +0,0 @@
|
|||
/*
|
||||
* 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.plugin.action.get;
|
||||
|
||||
import org.elasticsearch.action.Action;
|
||||
import org.elasticsearch.client.ElasticsearchClient;
|
||||
|
||||
public class GetLicenseAction extends Action<GetLicenseRequest, GetLicenseResponse, GetLicenseRequestBuilder> {
|
||||
|
||||
public static final GetLicenseAction INSTANCE = new GetLicenseAction();
|
||||
public static final String NAME = "cluster:admin/plugin/license/get";
|
||||
|
||||
private GetLicenseAction() {
|
||||
super(NAME);
|
||||
}
|
||||
|
||||
@Override
|
||||
public GetLicenseResponse newResponse() {
|
||||
return new GetLicenseResponse();
|
||||
}
|
||||
|
||||
@Override
|
||||
public GetLicenseRequestBuilder newRequestBuilder(ElasticsearchClient client) {
|
||||
return new GetLicenseRequestBuilder(client, this);
|
||||
}
|
||||
}
|
|
@ -1,21 +0,0 @@
|
|||
/*
|
||||
* 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.plugin.action.get;
|
||||
|
||||
import org.elasticsearch.action.ActionRequestValidationException;
|
||||
import org.elasticsearch.action.support.master.MasterNodeReadRequest;
|
||||
|
||||
|
||||
public class GetLicenseRequest extends MasterNodeReadRequest<GetLicenseRequest> {
|
||||
|
||||
public GetLicenseRequest() {
|
||||
}
|
||||
|
||||
@Override
|
||||
public ActionRequestValidationException validate() {
|
||||
return null;
|
||||
}
|
||||
}
|
|
@ -1,21 +0,0 @@
|
|||
/*
|
||||
* 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.plugin.action.get;
|
||||
|
||||
import org.elasticsearch.action.support.master.MasterNodeReadOperationRequestBuilder;
|
||||
import org.elasticsearch.client.ElasticsearchClient;
|
||||
|
||||
public class GetLicenseRequestBuilder extends MasterNodeReadOperationRequestBuilder<GetLicenseRequest, GetLicenseResponse, GetLicenseRequestBuilder> {
|
||||
|
||||
/**
|
||||
* Creates new get licenses request builder
|
||||
*
|
||||
* @param client elasticsearch client
|
||||
*/
|
||||
public GetLicenseRequestBuilder(ElasticsearchClient client, GetLicenseAction action) {
|
||||
super(client, action, new GetLicenseRequest());
|
||||
}
|
||||
}
|
|
@ -1,45 +0,0 @@
|
|||
/*
|
||||
* 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.plugin.action.get;
|
||||
|
||||
import org.elasticsearch.action.ActionResponse;
|
||||
import org.elasticsearch.common.io.stream.StreamInput;
|
||||
import org.elasticsearch.common.io.stream.StreamOutput;
|
||||
import org.elasticsearch.license.core.License;
|
||||
import org.elasticsearch.license.core.Licenses;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
|
||||
public class GetLicenseResponse extends ActionResponse {
|
||||
|
||||
private List<License> licenses = new ArrayList<>();
|
||||
|
||||
GetLicenseResponse() {
|
||||
}
|
||||
|
||||
GetLicenseResponse(List<License> licenses) {
|
||||
this.licenses = licenses;
|
||||
}
|
||||
|
||||
public List<License> licenses() {
|
||||
return licenses;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void readFrom(StreamInput in) throws IOException {
|
||||
super.readFrom(in);
|
||||
licenses = Licenses.readFrom(in);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void writeTo(StreamOutput out) throws IOException {
|
||||
super.writeTo(out);
|
||||
Licenses.writeTo(licenses, out);
|
||||
}
|
||||
|
||||
}
|
|
@ -1,53 +0,0 @@
|
|||
/*
|
||||
* 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.plugin.action.get;
|
||||
|
||||
import org.elasticsearch.ElasticsearchException;
|
||||
import org.elasticsearch.action.ActionListener;
|
||||
import org.elasticsearch.action.support.ActionFilters;
|
||||
import org.elasticsearch.action.support.master.TransportMasterNodeReadAction;
|
||||
import org.elasticsearch.cluster.ClusterService;
|
||||
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.common.inject.Inject;
|
||||
import org.elasticsearch.common.settings.Settings;
|
||||
import org.elasticsearch.license.plugin.core.LicensesManagerService;
|
||||
import org.elasticsearch.threadpool.ThreadPool;
|
||||
import org.elasticsearch.transport.TransportService;
|
||||
|
||||
public class TransportGetLicenseAction extends TransportMasterNodeReadAction<GetLicenseRequest, GetLicenseResponse> {
|
||||
|
||||
private final LicensesManagerService licensesManagerService;
|
||||
|
||||
@Inject
|
||||
public TransportGetLicenseAction(Settings settings, TransportService transportService, ClusterService clusterService, LicensesManagerService licensesManagerService,
|
||||
ThreadPool threadPool, ActionFilters actionFilters, IndexNameExpressionResolver indexNameExpressionResolver) {
|
||||
super(settings, GetLicenseAction.NAME, transportService, clusterService, threadPool, actionFilters, indexNameExpressionResolver, GetLicenseRequest.class);
|
||||
this.licensesManagerService = licensesManagerService;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected String executor() {
|
||||
return ThreadPool.Names.MANAGEMENT;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected GetLicenseResponse newResponse() {
|
||||
return new GetLicenseResponse();
|
||||
}
|
||||
|
||||
@Override
|
||||
protected ClusterBlockException checkBlock(GetLicenseRequest request, ClusterState state) {
|
||||
return state.blocks().indexBlockedException(ClusterBlockLevel.METADATA_READ, "");
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void masterOperation(final GetLicenseRequest request, ClusterState state, final ActionListener<GetLicenseResponse> listener) throws ElasticsearchException {
|
||||
listener.onResponse(new GetLicenseResponse(licensesManagerService.getLicenses()));
|
||||
}
|
||||
}
|
|
@ -1,29 +0,0 @@
|
|||
/*
|
||||
* 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.plugin.action.put;
|
||||
|
||||
import org.elasticsearch.action.Action;
|
||||
import org.elasticsearch.client.ElasticsearchClient;
|
||||
|
||||
public class PutLicenseAction extends Action<PutLicenseRequest, PutLicenseResponse, PutLicenseRequestBuilder> {
|
||||
|
||||
public static final PutLicenseAction INSTANCE = new PutLicenseAction();
|
||||
public static final String NAME = "cluster:admin/plugin/license/put";
|
||||
|
||||
private PutLicenseAction() {
|
||||
super(NAME);
|
||||
}
|
||||
|
||||
@Override
|
||||
public PutLicenseResponse newResponse() {
|
||||
return new PutLicenseResponse();
|
||||
}
|
||||
|
||||
@Override
|
||||
public PutLicenseRequestBuilder newRequestBuilder(ElasticsearchClient client) {
|
||||
return new PutLicenseRequestBuilder(client, this);
|
||||
}
|
||||
}
|
|
@ -1,66 +0,0 @@
|
|||
/*
|
||||
* 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.plugin.action.put;
|
||||
|
||||
import org.elasticsearch.action.ActionRequestValidationException;
|
||||
import org.elasticsearch.action.support.master.AcknowledgedRequest;
|
||||
import org.elasticsearch.common.io.stream.StreamInput;
|
||||
import org.elasticsearch.common.io.stream.StreamOutput;
|
||||
import org.elasticsearch.license.core.License;
|
||||
import org.elasticsearch.license.core.Licenses;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.util.List;
|
||||
|
||||
|
||||
public class PutLicenseRequest extends AcknowledgedRequest<PutLicenseRequest> {
|
||||
|
||||
private List<License> licenses;
|
||||
|
||||
public PutLicenseRequest() {
|
||||
}
|
||||
|
||||
@Override
|
||||
public ActionRequestValidationException validate() {
|
||||
return null;
|
||||
}
|
||||
|
||||
/**
|
||||
* Parses licenses from json format to an instance of {@link org.elasticsearch.license.core.Licenses}
|
||||
*
|
||||
* @param licenseDefinition licenses definition
|
||||
*/
|
||||
public PutLicenseRequest licenses(String licenseDefinition) {
|
||||
try {
|
||||
return licenses(Licenses.fromSource(licenseDefinition));
|
||||
} catch (IOException e) {
|
||||
throw new IllegalArgumentException("failed to parse licenses source", e);
|
||||
}
|
||||
}
|
||||
|
||||
public PutLicenseRequest licenses(List<License> licenses) {
|
||||
this.licenses = licenses;
|
||||
return this;
|
||||
}
|
||||
|
||||
public List<License> licenses() {
|
||||
return licenses;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void readFrom(StreamInput in) throws IOException {
|
||||
super.readFrom(in);
|
||||
licenses = Licenses.readFrom(in);
|
||||
readTimeout(in);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void writeTo(StreamOutput out) throws IOException {
|
||||
super.writeTo(out);
|
||||
Licenses.writeTo(licenses, out);
|
||||
writeTimeout(out);
|
||||
}
|
||||
}
|
|
@ -1,43 +0,0 @@
|
|||
/*
|
||||
* 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.plugin.action.put;
|
||||
|
||||
import org.elasticsearch.action.support.master.AcknowledgedRequestBuilder;
|
||||
import org.elasticsearch.client.ElasticsearchClient;
|
||||
import org.elasticsearch.license.core.License;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* Register license request builder
|
||||
*/
|
||||
public class PutLicenseRequestBuilder extends AcknowledgedRequestBuilder<PutLicenseRequest, PutLicenseResponse, PutLicenseRequestBuilder> {
|
||||
|
||||
/**
|
||||
* Constructs register license request
|
||||
*
|
||||
* @param client elasticsearch client
|
||||
*/
|
||||
public PutLicenseRequestBuilder(ElasticsearchClient client, PutLicenseAction action) {
|
||||
super(client, action, new PutLicenseRequest());
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the license
|
||||
*
|
||||
* @param licenses license
|
||||
* @return this builder
|
||||
*/
|
||||
public PutLicenseRequestBuilder setLicense(List<License> licenses) {
|
||||
request.licenses(licenses);
|
||||
return this;
|
||||
}
|
||||
|
||||
public PutLicenseRequestBuilder setLicense(String licenseSource) {
|
||||
request.licenses(licenseSource);
|
||||
return this;
|
||||
}
|
||||
}
|
|
@ -1,45 +0,0 @@
|
|||
/*
|
||||
* 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.plugin.action.put;
|
||||
|
||||
import org.elasticsearch.action.support.master.AcknowledgedResponse;
|
||||
import org.elasticsearch.common.io.stream.StreamInput;
|
||||
import org.elasticsearch.common.io.stream.StreamOutput;
|
||||
import org.elasticsearch.license.plugin.core.LicensesStatus;
|
||||
|
||||
import java.io.IOException;
|
||||
|
||||
public class PutLicenseResponse extends AcknowledgedResponse {
|
||||
|
||||
private LicensesStatus status;
|
||||
|
||||
PutLicenseResponse() {
|
||||
}
|
||||
|
||||
PutLicenseResponse(boolean acknowledged, LicensesStatus status) {
|
||||
super(acknowledged);
|
||||
this.status = status;
|
||||
}
|
||||
|
||||
public LicensesStatus status() {
|
||||
return status;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void readFrom(StreamInput in) throws IOException {
|
||||
super.readFrom(in);
|
||||
readAcknowledged(in);
|
||||
status = LicensesStatus.fromId(in.readVInt());
|
||||
}
|
||||
|
||||
@Override
|
||||
public void writeTo(StreamOutput out) throws IOException {
|
||||
super.writeTo(out);
|
||||
writeAcknowledged(out);
|
||||
out.writeVInt(status.id());
|
||||
}
|
||||
|
||||
}
|
|
@ -1,68 +0,0 @@
|
|||
/*
|
||||
* 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.plugin.action.put;
|
||||
|
||||
import org.elasticsearch.ElasticsearchException;
|
||||
import org.elasticsearch.action.ActionListener;
|
||||
import org.elasticsearch.action.support.ActionFilters;
|
||||
import org.elasticsearch.action.support.master.TransportMasterNodeAction;
|
||||
import org.elasticsearch.cluster.ClusterService;
|
||||
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.common.inject.Inject;
|
||||
import org.elasticsearch.common.settings.Settings;
|
||||
import org.elasticsearch.license.plugin.core.LicensesManagerService;
|
||||
import org.elasticsearch.threadpool.ThreadPool;
|
||||
import org.elasticsearch.transport.TransportService;
|
||||
|
||||
import static org.elasticsearch.license.plugin.core.LicensesService.LicensesUpdateResponse;
|
||||
import static org.elasticsearch.license.plugin.core.LicensesService.PutLicenseRequestHolder;
|
||||
|
||||
public class TransportPutLicenseAction extends TransportMasterNodeAction<PutLicenseRequest, PutLicenseResponse> {
|
||||
|
||||
private final LicensesManagerService licensesManagerService;
|
||||
|
||||
@Inject
|
||||
public TransportPutLicenseAction(Settings settings, TransportService transportService, ClusterService clusterService,
|
||||
LicensesManagerService licensesManagerService, ThreadPool threadPool, ActionFilters actionFilters, IndexNameExpressionResolver indexNameExpressionResolver) {
|
||||
super(settings, PutLicenseAction.NAME, transportService, clusterService, threadPool, actionFilters, indexNameExpressionResolver, PutLicenseRequest.class);
|
||||
this.licensesManagerService = licensesManagerService;
|
||||
}
|
||||
|
||||
|
||||
@Override
|
||||
protected String executor() {
|
||||
return ThreadPool.Names.MANAGEMENT;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected PutLicenseResponse newResponse() {
|
||||
return new PutLicenseResponse();
|
||||
}
|
||||
|
||||
@Override
|
||||
protected ClusterBlockException checkBlock(PutLicenseRequest request, ClusterState state) {
|
||||
return state.blocks().indexBlockedException(ClusterBlockLevel.METADATA_WRITE, "");
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void masterOperation(final PutLicenseRequest request, ClusterState state, final ActionListener<PutLicenseResponse> listener) throws ElasticsearchException {
|
||||
licensesManagerService.registerLicenses(new PutLicenseRequestHolder(request, "put licenses []"), new ActionListener<LicensesUpdateResponse>() {
|
||||
@Override
|
||||
public void onResponse(LicensesUpdateResponse licensesUpdateResponse) {
|
||||
listener.onResponse(new PutLicenseResponse(licensesUpdateResponse.isAcknowledged(), licensesUpdateResponse.status()));
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onFailure(Throwable e) {
|
||||
listener.onFailure(e);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
}
|
|
@ -1,43 +0,0 @@
|
|||
/*
|
||||
* 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.plugin.core;
|
||||
|
||||
import org.elasticsearch.action.ActionListener;
|
||||
import org.elasticsearch.cluster.ack.ClusterStateUpdateResponse;
|
||||
import org.elasticsearch.common.inject.ImplementedBy;
|
||||
import org.elasticsearch.license.core.License;
|
||||
|
||||
import java.util.List;
|
||||
import java.util.Set;
|
||||
|
||||
import static org.elasticsearch.license.plugin.core.LicensesService.*;
|
||||
|
||||
@ImplementedBy(LicensesService.class)
|
||||
public interface LicensesManagerService {
|
||||
|
||||
/**
|
||||
* Registers new licenses in the cluster
|
||||
* <p/>
|
||||
* This method can be only called on the master node. It tries to create a new licenses on the master
|
||||
* and if provided license(s) is VALID it is added to cluster state metadata {@link org.elasticsearch.license.plugin.core.LicensesMetaData}
|
||||
*/
|
||||
public void registerLicenses(final PutLicenseRequestHolder requestHolder, final ActionListener<LicensesService.LicensesUpdateResponse> listener);
|
||||
|
||||
/**
|
||||
* Remove only signed license(s) for provided features from the cluster state metadata
|
||||
*/
|
||||
public void removeLicenses(final DeleteLicenseRequestHolder requestHolder, final ActionListener<ClusterStateUpdateResponse> listener);
|
||||
|
||||
/**
|
||||
* @return the set of features that are currently enabled
|
||||
*/
|
||||
public Set<String> enabledFeatures();
|
||||
|
||||
/**
|
||||
* @return a list of licenses, contains one license (with the latest expiryDate) per registered features sorted by latest issueDate
|
||||
*/
|
||||
public List<License> getLicenses();
|
||||
}
|
|
@ -1,155 +0,0 @@
|
|||
/*
|
||||
* 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.plugin.core;
|
||||
|
||||
import com.google.common.collect.ImmutableList;
|
||||
import org.elasticsearch.cluster.AbstractDiffable;
|
||||
import org.elasticsearch.cluster.metadata.MetaData;
|
||||
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.core.License;
|
||||
import org.elasticsearch.license.core.Licenses;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Collections;
|
||||
import java.util.EnumSet;
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* Contains metadata about registered licenses
|
||||
*/
|
||||
public class LicensesMetaData extends AbstractDiffable<MetaData.Custom> implements MetaData.Custom {
|
||||
|
||||
public static final String TYPE = "licenses";
|
||||
|
||||
public static final LicensesMetaData PROTO = new LicensesMetaData(Collections.<License>emptyList(), Collections.<License>emptyList());
|
||||
|
||||
private final ImmutableList<License> signedLicenses;
|
||||
|
||||
private final ImmutableList<License> trialLicenses;
|
||||
|
||||
/**
|
||||
* Constructs new licenses metadata
|
||||
*
|
||||
* @param signedLicenses list of signed Licenses
|
||||
* @param trialLicenses set of encoded trial licenses
|
||||
*/
|
||||
public LicensesMetaData(List<License> signedLicenses, List<License> trialLicenses) {
|
||||
this.signedLicenses = ImmutableList.copyOf(signedLicenses);
|
||||
this.trialLicenses = ImmutableList.copyOf(trialLicenses);
|
||||
}
|
||||
|
||||
public List<License> getSignedLicenses() {
|
||||
return signedLicenses;
|
||||
}
|
||||
|
||||
public List<License> getTrialLicenses() {
|
||||
return trialLicenses;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean equals(Object obj) {
|
||||
if (this == obj) return true;
|
||||
if (obj == null || getClass() != obj.getClass()) {
|
||||
return false;
|
||||
}
|
||||
LicensesMetaData that = (LicensesMetaData) obj;
|
||||
return signedLicenses.equals(that.signedLicenses)
|
||||
&& trialLicenses.equals(that.trialLicenses);
|
||||
}
|
||||
|
||||
@Override
|
||||
public int hashCode() {
|
||||
return signedLicenses.hashCode() + 31 * trialLicenses.hashCode();
|
||||
}
|
||||
|
||||
@Override
|
||||
public String type() {
|
||||
return TYPE;
|
||||
}
|
||||
|
||||
@Override
|
||||
public LicensesMetaData fromXContent(XContentParser parser) throws IOException {
|
||||
List<License> trialLicenses = new ArrayList<>();
|
||||
List<License> signedLicenses = new ArrayList<>();
|
||||
XContentParser.Token token;
|
||||
while (parser.currentToken() != XContentParser.Token.END_OBJECT) {
|
||||
token = parser.nextToken();
|
||||
if (token == XContentParser.Token.FIELD_NAME) {
|
||||
String fieldName = parser.text();
|
||||
if (fieldName != null) {
|
||||
if (fieldName.equals(Fields.TRIAL_LICENSES)) {
|
||||
if (parser.nextToken() == XContentParser.Token.START_ARRAY) {
|
||||
while (parser.nextToken() != XContentParser.Token.END_ARRAY) {
|
||||
if (parser.currentToken().isValue()) {
|
||||
trialLicenses.add(TrialLicenseUtils.fromEncodedTrialLicense(parser.text()));
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
if (fieldName.equals(Fields.SIGNED_LICENCES)) {
|
||||
if (parser.nextToken() == XContentParser.Token.START_ARRAY) {
|
||||
while (parser.nextToken() != XContentParser.Token.END_ARRAY) {
|
||||
License.Builder builder = License.builder().fromXContent(parser);
|
||||
signedLicenses.add(builder.build());
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
return new LicensesMetaData(signedLicenses, trialLicenses);
|
||||
}
|
||||
|
||||
@Override
|
||||
public EnumSet<MetaData.XContentContext> context() {
|
||||
return EnumSet.of(MetaData.XContentContext.GATEWAY);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void writeTo(StreamOutput streamOutput) throws IOException {
|
||||
Licenses.writeTo(signedLicenses, streamOutput);
|
||||
streamOutput.writeVInt(trialLicenses.size());
|
||||
for (License trialLicense : trialLicenses) {
|
||||
streamOutput.writeString(TrialLicenseUtils.toEncodedTrialLicense(trialLicense));
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public MetaData.Custom readFrom(StreamInput streamInput) throws IOException {
|
||||
List<License> signedLicenses = Licenses.readFrom(streamInput);
|
||||
int numTrialLicenses = streamInput.readVInt();
|
||||
List<License> trialLicenses = new ArrayList<>(numTrialLicenses);
|
||||
for (int i = 0; i < numTrialLicenses; i++) {
|
||||
trialLicenses.add(TrialLicenseUtils.fromEncodedTrialLicense(streamInput.readString()));
|
||||
}
|
||||
return new LicensesMetaData(signedLicenses, trialLicenses);
|
||||
}
|
||||
|
||||
@Override
|
||||
public XContentBuilder toXContent(XContentBuilder builder, Params params) throws IOException {
|
||||
builder.startArray(Fields.TRIAL_LICENSES);
|
||||
for (License trailLicense : trialLicenses) {
|
||||
builder.value(TrialLicenseUtils.toEncodedTrialLicense(trailLicense));
|
||||
}
|
||||
builder.endArray();
|
||||
|
||||
builder.startArray(Fields.SIGNED_LICENCES);
|
||||
for (License license : signedLicenses) {
|
||||
license.toXContent(builder, params);
|
||||
}
|
||||
builder.endArray();
|
||||
return builder;
|
||||
}
|
||||
|
||||
private final static class Fields {
|
||||
private static final String SIGNED_LICENCES = "signed_licenses";
|
||||
private static final String TRIAL_LICENSES = "trial_licenses";
|
||||
}
|
||||
}
|
|
@ -1,986 +0,0 @@
|
|||
/*
|
||||
* 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.plugin.core;
|
||||
|
||||
import com.google.common.collect.ImmutableList;
|
||||
import com.google.common.collect.ImmutableMap;
|
||||
import com.google.common.collect.Iterables;
|
||||
import com.google.common.collect.Sets;
|
||||
import org.apache.lucene.util.CollectionUtil;
|
||||
import org.elasticsearch.ElasticsearchException;
|
||||
import org.elasticsearch.action.ActionListener;
|
||||
import org.elasticsearch.cluster.*;
|
||||
import org.elasticsearch.cluster.ack.ClusterStateUpdateResponse;
|
||||
import org.elasticsearch.cluster.metadata.MetaData;
|
||||
import org.elasticsearch.cluster.node.DiscoveryNode;
|
||||
import org.elasticsearch.common.Nullable;
|
||||
import org.elasticsearch.common.component.AbstractLifecycleComponent;
|
||||
import org.elasticsearch.common.component.Lifecycle;
|
||||
import org.elasticsearch.common.inject.Inject;
|
||||
import org.elasticsearch.common.inject.Singleton;
|
||||
import org.elasticsearch.common.io.stream.StreamInput;
|
||||
import org.elasticsearch.common.io.stream.StreamOutput;
|
||||
import org.elasticsearch.common.settings.Settings;
|
||||
import org.elasticsearch.common.unit.TimeValue;
|
||||
import org.elasticsearch.common.util.concurrent.EsRejectedExecutionException;
|
||||
import org.elasticsearch.common.util.concurrent.FutureUtils;
|
||||
import org.elasticsearch.gateway.GatewayService;
|
||||
import org.elasticsearch.license.core.License;
|
||||
import org.elasticsearch.license.core.LicenseVerifier;
|
||||
import org.elasticsearch.license.plugin.action.delete.DeleteLicenseRequest;
|
||||
import org.elasticsearch.license.plugin.action.put.PutLicenseRequest;
|
||||
import org.elasticsearch.threadpool.ThreadPool;
|
||||
import org.elasticsearch.transport.*;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.util.*;
|
||||
import java.util.concurrent.ConcurrentLinkedQueue;
|
||||
import java.util.concurrent.CopyOnWriteArrayList;
|
||||
import java.util.concurrent.ScheduledFuture;
|
||||
import java.util.concurrent.TimeUnit;
|
||||
import java.util.concurrent.atomic.AtomicLong;
|
||||
import java.util.concurrent.atomic.AtomicReference;
|
||||
|
||||
import static org.elasticsearch.license.core.Licenses.reduceAndMap;
|
||||
|
||||
/**
|
||||
* Service responsible for managing {@link LicensesMetaData}
|
||||
* Interfaces through which this is exposed are:
|
||||
* - LicensesManagerService - responsible for managing signed and one-time-trial licenses
|
||||
* - LicensesClientService - responsible for feature registration and notification to consumer plugin(s)
|
||||
* <p/>
|
||||
* <p/>
|
||||
* Registration Scheme:
|
||||
* <p/>
|
||||
* A consumer plugin (feature) is registered with {@link LicensesClientService#register(String, TrialLicenseOptions, java.util.Collection<ExpirationCallback>, LicensesClientService.Listener)}
|
||||
* This method can be called at any time during the life-cycle of the consumer plugin.
|
||||
* If the feature can not be registered immediately, it is queued up and registered on the first clusterChanged event with
|
||||
* no {@link org.elasticsearch.gateway.GatewayService#STATE_NOT_RECOVERED_BLOCK} block
|
||||
* Upon successful registration, the feature(s) are notified appropriately using the notification scheme
|
||||
* <p/>
|
||||
* <p/>
|
||||
* Notification Scheme:
|
||||
* <p/>
|
||||
* All registered feature(s) are notified using {@link #notifyFeatures(LicensesMetaData)} (depends on the current
|
||||
* {@link #registeredListeners}). It is idempotent with respect to all the feature listeners.
|
||||
* <p/>
|
||||
* The notification scheduling is done by {@link #notifyFeaturesAndScheduleNotification(LicensesMetaData)} which does the following:
|
||||
* - calls {@link #notifyFeatures(LicensesMetaData)} to notify all registered feature(s)
|
||||
* - if there is any license(s) with a future expiry date in the current cluster state:
|
||||
* - schedules a delayed {@link LicensingClientNotificationJob} on the MIN of all the expiry dates of all the registered feature(s)
|
||||
* <p/>
|
||||
* The {@link LicensingClientNotificationJob} calls {@link #notifyFeaturesAndScheduleNotification(LicensesMetaData)} to schedule
|
||||
* another delayed {@link LicensingClientNotificationJob} as stated above. It is a no-op in case of a global block on
|
||||
* {@link org.elasticsearch.gateway.GatewayService#STATE_NOT_RECOVERED_BLOCK}
|
||||
* <p/>
|
||||
* Upon successful registration of a new feature:
|
||||
* - {@link #notifyFeaturesAndScheduleNotification(LicensesMetaData)} is called
|
||||
* <p/>
|
||||
* Upon clusterChanged():
|
||||
* - {@link #notifyFeaturesAndScheduleNotification(LicensesMetaData)} is called if:
|
||||
* - new trial/signed license(s) are found in the cluster state meta data
|
||||
* - if new feature(s) are added to the registeredListener
|
||||
* - if the previous cluster state had a global block on {@link org.elasticsearch.gateway.GatewayService#STATE_NOT_RECOVERED_BLOCK}
|
||||
* - no-op in case of global block on {@link org.elasticsearch.gateway.GatewayService#STATE_NOT_RECOVERED_BLOCK}
|
||||
*/
|
||||
@Singleton
|
||||
public class LicensesService extends AbstractLifecycleComponent<LicensesService> implements ClusterStateListener, LicensesManagerService, LicensesClientService {
|
||||
|
||||
public static final String REGISTER_TRIAL_LICENSE_ACTION_NAME = "internal:plugin/license/cluster/register_trial_license";
|
||||
|
||||
private final ClusterService clusterService;
|
||||
|
||||
private final ThreadPool threadPool;
|
||||
|
||||
private final TransportService transportService;
|
||||
|
||||
/**
|
||||
* Currently active consumers to notify to
|
||||
*/
|
||||
private final List<ListenerHolder> registeredListeners = new CopyOnWriteArrayList<>();
|
||||
|
||||
/**
|
||||
* Currently pending consumers that has to be registered
|
||||
*/
|
||||
private final Queue<ListenerHolder> pendingListeners = new ConcurrentLinkedQueue<>();
|
||||
|
||||
/**
|
||||
* Currently active scheduledNotifications
|
||||
* All finished notifications will be cleared by {@link #scheduleNextNotification(long)}
|
||||
*/
|
||||
private final Queue<ScheduledFuture> scheduledNotifications = new ConcurrentLinkedQueue<>();
|
||||
|
||||
/**
|
||||
* Currently active event notifications for every registered feature
|
||||
*/
|
||||
private final Map<String, Queue<ScheduledFuture>> eventNotificationsMap = new HashMap<>();
|
||||
|
||||
/**
|
||||
* The last licensesMetaData that has been notified by {@link #notifyFeatures(LicensesMetaData)}
|
||||
*/
|
||||
private final AtomicReference<LicensesMetaData> lastObservedLicensesState;
|
||||
|
||||
@Inject
|
||||
public LicensesService(Settings settings, ClusterService clusterService, ThreadPool threadPool, TransportService transportService) {
|
||||
super(settings);
|
||||
this.clusterService = clusterService;
|
||||
this.threadPool = threadPool;
|
||||
this.transportService = transportService;
|
||||
this.lastObservedLicensesState = new AtomicReference<>(null);
|
||||
if (DiscoveryNode.masterNode(settings)) {
|
||||
transportService.registerRequestHandler(REGISTER_TRIAL_LICENSE_ACTION_NAME, RegisterTrialLicenseRequest.class,
|
||||
ThreadPool.Names.SAME, new RegisterTrialLicenseRequestHandler());
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritDoc}
|
||||
*/
|
||||
@Override
|
||||
public void registerLicenses(final PutLicenseRequestHolder requestHolder, final ActionListener<LicensesUpdateResponse> listener) {
|
||||
final PutLicenseRequest request = requestHolder.request;
|
||||
final Set<License> newLicenses = Sets.newHashSet(request.licenses());
|
||||
LicensesStatus status = checkLicenses(newLicenses);
|
||||
if (status == LicensesStatus.VALID) {
|
||||
clusterService.submitStateUpdateTask(requestHolder.source, new AckedClusterStateUpdateTask<LicensesUpdateResponse>(request, listener) {
|
||||
@Override
|
||||
protected LicensesUpdateResponse newResponse(boolean acknowledged) {
|
||||
return new LicensesUpdateResponse(acknowledged, LicensesStatus.VALID);
|
||||
}
|
||||
|
||||
@Override
|
||||
public ClusterState execute(ClusterState currentState) throws Exception {
|
||||
MetaData metaData = currentState.metaData();
|
||||
MetaData.Builder mdBuilder = MetaData.builder(currentState.metaData());
|
||||
LicensesMetaData currentLicenses = metaData.custom(LicensesMetaData.TYPE);
|
||||
final LicensesWrapper licensesWrapper = LicensesWrapper.wrap(currentLicenses);
|
||||
List<License> updatedSignedLicenses = licensesWrapper.addAndGetSignedLicenses(newLicenses);
|
||||
if (updatedSignedLicenses.size() != licensesWrapper.signedLicenses.size()) {
|
||||
LicensesMetaData newLicensesMetaData = new LicensesMetaData(updatedSignedLicenses, licensesWrapper.trialLicenses);
|
||||
mdBuilder.putCustom(LicensesMetaData.TYPE, newLicensesMetaData);
|
||||
return ClusterState.builder(currentState).metaData(mdBuilder).build();
|
||||
}
|
||||
return currentState;
|
||||
}
|
||||
});
|
||||
} else {
|
||||
listener.onResponse(new LicensesUpdateResponse(true, status));
|
||||
}
|
||||
}
|
||||
|
||||
public static class LicensesUpdateResponse extends ClusterStateUpdateResponse {
|
||||
private final LicensesStatus status;
|
||||
|
||||
public LicensesUpdateResponse(boolean acknowledged, LicensesStatus status) {
|
||||
super(acknowledged);
|
||||
this.status = status;
|
||||
}
|
||||
|
||||
public LicensesStatus status() {
|
||||
return status;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritDoc}
|
||||
*/
|
||||
@Override
|
||||
public void removeLicenses(final DeleteLicenseRequestHolder requestHolder, final ActionListener<ClusterStateUpdateResponse> listener) {
|
||||
final DeleteLicenseRequest request = requestHolder.request;
|
||||
clusterService.submitStateUpdateTask(requestHolder.source, new AckedClusterStateUpdateTask<ClusterStateUpdateResponse>(request, listener) {
|
||||
@Override
|
||||
protected ClusterStateUpdateResponse newResponse(boolean acknowledged) {
|
||||
return new ClusterStateUpdateResponse(acknowledged);
|
||||
}
|
||||
|
||||
@Override
|
||||
public ClusterState execute(ClusterState currentState) throws Exception {
|
||||
MetaData metaData = currentState.metaData();
|
||||
LicensesMetaData currentLicenses = metaData.custom(LicensesMetaData.TYPE);
|
||||
final LicensesWrapper licensesWrapper = LicensesWrapper.wrap(currentLicenses);
|
||||
List<License> updatedSignedLicenses = licensesWrapper.removeAndGetSignedLicenses(request.features());
|
||||
if (updatedSignedLicenses.size() != licensesWrapper.signedLicenses.size()) {
|
||||
LicensesMetaData newLicensesMetaData = new LicensesMetaData(updatedSignedLicenses, licensesWrapper.trialLicenses);
|
||||
MetaData.Builder mdBuilder = MetaData.builder(currentState.metaData());
|
||||
mdBuilder.putCustom(LicensesMetaData.TYPE, newLicensesMetaData);
|
||||
return ClusterState.builder(currentState).metaData(mdBuilder).build();
|
||||
} else {
|
||||
return currentState;
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritDoc}
|
||||
*/
|
||||
@Override
|
||||
public Set<String> enabledFeatures() {
|
||||
Set<String> enabledFeatures = Sets.newHashSet();
|
||||
for (ListenerHolder holder : registeredListeners) {
|
||||
if (holder.enabled) {
|
||||
enabledFeatures.add(holder.feature);
|
||||
}
|
||||
}
|
||||
if (logger.isDebugEnabled()) {
|
||||
StringBuilder stringBuilder = new StringBuilder();
|
||||
for (String feature: enabledFeatures) {
|
||||
if (stringBuilder.length() != 0) {
|
||||
stringBuilder.append(", ");
|
||||
}
|
||||
stringBuilder.append(feature);
|
||||
}
|
||||
logger.debug("LicensesManagerService: Enabled Features: [" + stringBuilder.toString() + "]");
|
||||
}
|
||||
return enabledFeatures;
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritDoc}
|
||||
*/
|
||||
@Override
|
||||
public List<License> getLicenses() {
|
||||
final LicensesMetaData currentMetaData = clusterService.state().metaData().custom(LicensesMetaData.TYPE);
|
||||
if (currentMetaData != null) {
|
||||
// don't use ESLicenses.reduceAndMap, as it will merge out expired licenses
|
||||
List<License> currentLicenses = new ArrayList<>();
|
||||
currentLicenses.addAll(currentMetaData.getSignedLicenses());
|
||||
currentLicenses.addAll(currentMetaData.getTrialLicenses());
|
||||
|
||||
// bucket license for feature with the latest expiry date
|
||||
Map<String, License> licenseMap = new HashMap<>();
|
||||
for (License license : currentLicenses) {
|
||||
if (!licenseMap.containsKey(license.feature())) {
|
||||
licenseMap.put(license.feature(), license);
|
||||
} else {
|
||||
License prevLicense = licenseMap.get(license.feature());
|
||||
if (license.expiryDate() > prevLicense.expiryDate()) {
|
||||
licenseMap.put(license.feature(), license);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// sort the licenses by issue date
|
||||
List<License> reducedLicenses = new ArrayList<>(licenseMap.values());
|
||||
CollectionUtil.introSort(reducedLicenses, new Comparator<License>() {
|
||||
@Override
|
||||
public int compare(License license1, License license2) {
|
||||
return (int) (license2.issueDate() - license1.issueDate());
|
||||
}
|
||||
});
|
||||
return reducedLicenses;
|
||||
}
|
||||
return Collections.emptyList();
|
||||
}
|
||||
|
||||
private LicensesStatus checkLicenses(Set<License> licenses) {
|
||||
final ImmutableMap<String, License> map = reduceAndMap(licenses);
|
||||
if (LicenseVerifier.verifyLicenses(map.values())) {
|
||||
return LicensesStatus.VALID;
|
||||
} else {
|
||||
return LicensesStatus.INVALID;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Master-only operation to generate a one-time trial license for a feature.
|
||||
* The trial license is only generated and stored if the current cluster state metaData
|
||||
* has no signed/one-time-trial license for the feature in question
|
||||
*/
|
||||
private void registerTrialLicense(final RegisterTrialLicenseRequest request) {
|
||||
clusterService.submitStateUpdateTask("register trial license []", new ProcessedClusterStateUpdateTask() {
|
||||
@Override
|
||||
public void clusterStateProcessed(String source, ClusterState oldState, ClusterState newState) {
|
||||
LicensesMetaData licensesMetaData = newState.metaData().custom(LicensesMetaData.TYPE);
|
||||
logLicenseMetaDataStats("after trial license registration", licensesMetaData);
|
||||
}
|
||||
|
||||
@Override
|
||||
public ClusterState execute(ClusterState currentState) throws Exception {
|
||||
MetaData metaData = currentState.metaData();
|
||||
MetaData.Builder mdBuilder = MetaData.builder(currentState.metaData());
|
||||
LicensesMetaData currentLicensesMetaData = metaData.custom(LicensesMetaData.TYPE);
|
||||
final LicensesWrapper licensesWrapper = LicensesWrapper.wrap(currentLicensesMetaData);
|
||||
// do not generate a trial license for a feature that already has a signed/trial license
|
||||
if (checkTrialLicenseGenerationCondition(request.feature, licensesWrapper)) {
|
||||
List<License> currentTrailLicenses = new ArrayList<>(licensesWrapper.trialLicenses);
|
||||
currentTrailLicenses.add(generateEncodedTrialLicense(request.feature, request.duration, request.maxNodes));
|
||||
final LicensesMetaData newLicensesMetaData = new LicensesMetaData(
|
||||
licensesWrapper.signedLicenses, ImmutableList.copyOf(currentTrailLicenses));
|
||||
mdBuilder.putCustom(LicensesMetaData.TYPE, newLicensesMetaData);
|
||||
return ClusterState.builder(currentState).metaData(mdBuilder).build();
|
||||
}
|
||||
return currentState;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onFailure(String source, @Nullable Throwable t) {
|
||||
logger.debug("LicensesService: " + source, t);
|
||||
}
|
||||
|
||||
private boolean checkTrialLicenseGenerationCondition(String feature, LicensesWrapper licensesWrapper) {
|
||||
final List<License> currentLicenses = new ArrayList<>();
|
||||
currentLicenses.addAll(licensesWrapper.signedLicenses);
|
||||
currentLicenses.addAll(licensesWrapper.trialLicenses);
|
||||
for (License license : currentLicenses) {
|
||||
if (license.feature().equals(feature)) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
private License generateEncodedTrialLicense(String feature, TimeValue duration, int maxNodes) {
|
||||
return TrialLicenseUtils.builder()
|
||||
.issuedTo(clusterService.state().getClusterName().value())
|
||||
.issueDate(System.currentTimeMillis())
|
||||
.duration(duration)
|
||||
.feature(feature)
|
||||
.maxNodes(maxNodes)
|
||||
.build();
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void doStart() throws ElasticsearchException {
|
||||
clusterService.add(this);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void doStop() throws ElasticsearchException {
|
||||
clusterService.remove(this);
|
||||
|
||||
// cancel all notifications
|
||||
for (ScheduledFuture scheduledNotification : scheduledNotifications) {
|
||||
FutureUtils.cancel(scheduledNotification);
|
||||
}
|
||||
|
||||
for (Queue<ScheduledFuture> queue : eventNotificationsMap.values()) {
|
||||
for (ScheduledFuture scheduledFuture : queue) {
|
||||
FutureUtils.cancel(scheduledFuture);
|
||||
}
|
||||
queue.clear();
|
||||
}
|
||||
|
||||
LicensesMetaData currentLicensesMetaData = clusterService.state().metaData().custom(LicensesMetaData.TYPE);
|
||||
final Map<String, License> effectiveLicenses = getEffectiveLicenses(currentLicensesMetaData);
|
||||
// notify features to be disabled
|
||||
for (ListenerHolder holder : registeredListeners) {
|
||||
holder.disableFeatureIfNeeded(effectiveLicenses.get(holder.feature), false);
|
||||
}
|
||||
// clear all handlers
|
||||
registeredListeners.clear();
|
||||
|
||||
// empty out notification queue
|
||||
scheduledNotifications.clear();
|
||||
|
||||
lastObservedLicensesState.set(null);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void doClose() throws ElasticsearchException {
|
||||
transportService.removeHandler(REGISTER_TRIAL_LICENSE_ACTION_NAME);
|
||||
}
|
||||
|
||||
/**
|
||||
* When there is no global block on {@link org.elasticsearch.gateway.GatewayService#STATE_NOT_RECOVERED_BLOCK}:
|
||||
* - tries to register any {@link #pendingListeners} by calling {@link #registeredListeners}
|
||||
* - if any {@link #pendingListeners} are registered successfully or if previous cluster state had a block on
|
||||
* {@link org.elasticsearch.gateway.GatewayService#STATE_NOT_RECOVERED_BLOCK}, calls
|
||||
* {@link #notifyFeaturesAndScheduleNotification(LicensesMetaData)}
|
||||
* - else calls {@link #notifyFeaturesAndScheduleNotificationIfNeeded(LicensesMetaData)}
|
||||
*/
|
||||
@Override
|
||||
public void clusterChanged(ClusterChangedEvent event) {
|
||||
final ClusterState previousClusterState = event.previousState();
|
||||
final ClusterState currentClusterState = event.state();
|
||||
if (!currentClusterState.blocks().hasGlobalBlock(GatewayService.STATE_NOT_RECOVERED_BLOCK)) {
|
||||
final LicensesMetaData oldLicensesMetaData = previousClusterState.getMetaData().custom(LicensesMetaData.TYPE);
|
||||
final LicensesMetaData currentLicensesMetaData = currentClusterState.getMetaData().custom(LicensesMetaData.TYPE);
|
||||
logLicenseMetaDataStats("old", oldLicensesMetaData);
|
||||
logLicenseMetaDataStats("new", currentLicensesMetaData);
|
||||
|
||||
// register any pending listeners
|
||||
if (!pendingListeners.isEmpty()) {
|
||||
ListenerHolder pendingRegistrationListener;
|
||||
while ((pendingRegistrationListener = pendingListeners.poll()) != null) {
|
||||
boolean masterAvailable = registerListener(pendingRegistrationListener);
|
||||
if (logger.isDebugEnabled()) {
|
||||
logger.debug("trying to register pending listener for " + pendingRegistrationListener.feature + " masterAvailable: " + masterAvailable);
|
||||
}
|
||||
if (!masterAvailable) {
|
||||
// if the master is not available do not, break out of trying pendingListeners
|
||||
pendingListeners.add(pendingRegistrationListener);
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// notify all interested plugins
|
||||
if (previousClusterState.blocks().hasGlobalBlock(GatewayService.STATE_NOT_RECOVERED_BLOCK)) {
|
||||
notifyFeaturesAndScheduleNotification(currentLicensesMetaData);
|
||||
} else {
|
||||
notifyFeaturesAndScheduleNotificationIfNeeded(currentLicensesMetaData);
|
||||
}
|
||||
final Map<String, License> effectiveLicenses = getEffectiveLicenses(currentLicensesMetaData);
|
||||
for (ListenerHolder listenerHolder : registeredListeners) {
|
||||
listenerHolder.scheduleNotificationIfNeeded(effectiveLicenses.get(listenerHolder.feature));
|
||||
}
|
||||
} else if (logger.isDebugEnabled()) {
|
||||
logger.debug("clusterChanged: no action [has STATE_NOT_RECOVERED_BLOCK]");
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Calls {@link #notifyFeaturesAndScheduleNotification(LicensesMetaData)} with <code>currentLicensesMetaData</code>
|
||||
* if it was not already notified on.
|
||||
* <p/>
|
||||
* Upon completion sets <code>currentLicensesMetaData</code> to {@link #lastObservedLicensesState}
|
||||
* to ensure the same license(s) are not notified on from
|
||||
* {@link #clusterChanged(org.elasticsearch.cluster.ClusterChangedEvent)}
|
||||
*/
|
||||
private void notifyFeaturesAndScheduleNotificationIfNeeded(final LicensesMetaData currentLicensesMetaData) {
|
||||
final LicensesMetaData lastNotifiedLicensesMetaData = lastObservedLicensesState.get();
|
||||
if (lastNotifiedLicensesMetaData != null && lastNotifiedLicensesMetaData.equals(currentLicensesMetaData)) {
|
||||
if (logger.isDebugEnabled()) {
|
||||
logger.debug("currentLicensesMetaData has been already notified on");
|
||||
}
|
||||
return;
|
||||
}
|
||||
notifyFeaturesAndScheduleNotification(currentLicensesMetaData);
|
||||
lastObservedLicensesState.set(currentLicensesMetaData);
|
||||
logLicenseMetaDataStats("Setting last observed metaData", currentLicensesMetaData);
|
||||
}
|
||||
|
||||
/**
|
||||
* Calls {@link #notifyFeatures(LicensesMetaData)} with <code>currentLicensesMetaData</code>
|
||||
* and schedules the earliest expiry (if any) notification for registered feature(s)
|
||||
*/
|
||||
private void notifyFeaturesAndScheduleNotification(final LicensesMetaData currentLicensesMetaData) {
|
||||
long nextScheduleFrequency = notifyFeatures(currentLicensesMetaData);
|
||||
if (nextScheduleFrequency != -1l) {
|
||||
scheduleNextNotification(nextScheduleFrequency);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Checks license expiry for all the registered feature(s)
|
||||
*
|
||||
* @return -1 if there are no expiring license(s) for any registered feature(s), else
|
||||
* returns the minimum of the expiry times of all the registered feature(s) to
|
||||
* schedule an expiry notification
|
||||
*/
|
||||
private long notifyFeatures(final LicensesMetaData currentLicensesMetaData) {
|
||||
long nextScheduleFrequency = -1l;
|
||||
for (ListenerHolder listenerHolder : registeredListeners) {
|
||||
final Map<String, License> effectiveLicenses = getEffectiveLicenses(currentLicensesMetaData);
|
||||
License license = effectiveLicenses.get(listenerHolder.feature);
|
||||
if (license == null) {
|
||||
continue;
|
||||
}
|
||||
long expiryDate = license.expiryDate();
|
||||
long issueDate = license.issueDate();
|
||||
long now = System.currentTimeMillis();
|
||||
long expiryDuration = expiryDate - now;
|
||||
|
||||
if (expiryDuration > 0l && (now - issueDate) >= 0l) {
|
||||
listenerHolder.enableFeatureIfNeeded(license);
|
||||
if (nextScheduleFrequency == -1l) {
|
||||
nextScheduleFrequency = expiryDuration;
|
||||
} else {
|
||||
nextScheduleFrequency = Math.min(expiryDuration, nextScheduleFrequency);
|
||||
}
|
||||
} else {
|
||||
listenerHolder.disableFeatureIfNeeded(license, true);
|
||||
}
|
||||
|
||||
if (logger.isDebugEnabled()) {
|
||||
String status;
|
||||
if (expiryDate != -1l) {
|
||||
status = " status: license expires in : " + TimeValue.timeValueMillis(expiryDate - System.currentTimeMillis());
|
||||
} else {
|
||||
status = " status: no trial/signed license found";
|
||||
}
|
||||
if (expiryDuration > 0l) {
|
||||
status += " action: enableFeatureIfNeeded";
|
||||
} else {
|
||||
status += " action: disableFeatureIfNeeded";
|
||||
}
|
||||
logger.debug(listenerHolder.toString() + "" + status);
|
||||
}
|
||||
}
|
||||
|
||||
if (logger.isDebugEnabled()) {
|
||||
if (nextScheduleFrequency == -1l) {
|
||||
logger.debug("no need to schedule next notification");
|
||||
} else {
|
||||
logger.debug("next notification time: " + TimeValue.timeValueMillis(nextScheduleFrequency).toString());
|
||||
}
|
||||
}
|
||||
|
||||
return nextScheduleFrequency;
|
||||
|
||||
}
|
||||
|
||||
private void logLicenseMetaDataStats(String prefix, LicensesMetaData licensesMetaData) {
|
||||
if (logger.isDebugEnabled()) {
|
||||
if (licensesMetaData != null) {
|
||||
StringBuilder signedFeatures = new StringBuilder();
|
||||
for (License license : licensesMetaData.getSignedLicenses()) {
|
||||
if (signedFeatures.length() != 0) {
|
||||
signedFeatures.append(", ");
|
||||
}
|
||||
signedFeatures.append(license.feature());
|
||||
}
|
||||
StringBuilder trialFeatures = new StringBuilder();
|
||||
for (License license : licensesMetaData.getTrialLicenses()) {
|
||||
if (trialFeatures.length() != 0) {
|
||||
trialFeatures.append(", ");
|
||||
}
|
||||
trialFeatures.append(license.feature());
|
||||
}
|
||||
logger.debug(prefix + " LicensesMetaData: signedLicenses: [" + signedFeatures.toString() + "] trialLicenses: [" + trialFeatures.toString() + "]");
|
||||
} else {
|
||||
logger.debug(prefix + " LicensesMetaData: signedLicenses: [] trialLicenses: []");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritDoc}
|
||||
*/
|
||||
@Override
|
||||
public void register(String feature, TrialLicenseOptions trialLicenseOptions, Collection<ExpirationCallback> expirationCallbacks, Listener listener) {
|
||||
for (final ListenerHolder listenerHolder : Iterables.concat(registeredListeners, pendingListeners)) {
|
||||
if (listenerHolder.feature.equals(feature)) {
|
||||
throw new IllegalStateException("feature: [" + feature + "] has been already registered");
|
||||
}
|
||||
}
|
||||
Queue<ScheduledFuture> notificationQueue = new ConcurrentLinkedQueue<>();
|
||||
eventNotificationsMap.put(feature, notificationQueue);
|
||||
final ListenerHolder listenerHolder = new ListenerHolder(feature, trialLicenseOptions, expirationCallbacks, listener, notificationQueue);
|
||||
// don't trust the clusterState for blocks just yet!
|
||||
final Lifecycle.State clusterServiceState = clusterService.lifecycleState();
|
||||
if (clusterServiceState != Lifecycle.State.STARTED) {
|
||||
pendingListeners.add(listenerHolder);
|
||||
} else {
|
||||
if (!registerListener(listenerHolder)) {
|
||||
pendingListeners.add(listenerHolder);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Notifies new feature listener if it already has a signed license
|
||||
* if new feature has a non-null trial license option, a master node request is made to generate the trial license
|
||||
* then notifies features if needed
|
||||
*
|
||||
* @param listenerHolder of the feature to register
|
||||
* @return true if registration has been completed, false otherwise (if masterNode is not available & trail license spec is provided)
|
||||
* or if there is a global block on {@link org.elasticsearch.gateway.GatewayService#STATE_NOT_RECOVERED_BLOCK}
|
||||
*/
|
||||
private boolean registerListener(final ListenerHolder listenerHolder) {
|
||||
logger.debug("Registering listener for " + listenerHolder.feature);
|
||||
final ClusterState currentState = clusterService.state();
|
||||
if (currentState.blocks().hasGlobalBlock(GatewayService.STATE_NOT_RECOVERED_BLOCK)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
final LicensesMetaData currentMetaData = currentState.metaData().custom(LicensesMetaData.TYPE);
|
||||
if (expiryDateForFeature(listenerHolder.feature, currentMetaData) == -1l) {
|
||||
// does not have any license so generate a trial license
|
||||
TrialLicenseOptions options = listenerHolder.trialLicenseOptions;
|
||||
if (options != null) {
|
||||
// Trial license option is provided
|
||||
RegisterTrialLicenseRequest request = new RegisterTrialLicenseRequest(listenerHolder.feature,
|
||||
options.duration, options.maxNodes);
|
||||
if (currentState.nodes().localNodeMaster()) {
|
||||
registerTrialLicense(request);
|
||||
} else {
|
||||
DiscoveryNode masterNode = currentState.nodes().masterNode();
|
||||
if (masterNode != null) {
|
||||
transportService.sendRequest(masterNode,
|
||||
REGISTER_TRIAL_LICENSE_ACTION_NAME, request, EmptyTransportResponseHandler.INSTANCE_SAME);
|
||||
} else {
|
||||
// could not sent register trial license request to master
|
||||
logger.debug("Store as pendingRegistration [master not available yet]");
|
||||
}
|
||||
// make sure trial license is available before registration
|
||||
return false;
|
||||
}
|
||||
} else if (logger.isDebugEnabled()) {
|
||||
// notify feature as clusterChangedEvent may not happen
|
||||
// as no trial or signed license has been found for feature
|
||||
logger.debug("Calling notifyFeaturesAndScheduleNotification [no trial license spec provided]");
|
||||
}
|
||||
} else if (logger.isDebugEnabled()) {
|
||||
// signed license already found for the new registered
|
||||
// feature, notify feature on registration
|
||||
logger.debug("Calling notifyFeaturesAndScheduleNotification [signed/trial license available]");
|
||||
}
|
||||
|
||||
if (currentMetaData == null) {
|
||||
return false;
|
||||
}
|
||||
registeredListeners.add(listenerHolder);
|
||||
notifyFeaturesAndScheduleNotification(currentMetaData);
|
||||
final Map<String, License> effectiveLicenses = getEffectiveLicenses(currentMetaData);
|
||||
listenerHolder.scheduleNotificationIfNeeded(effectiveLicenses.get(listenerHolder.feature));
|
||||
return true;
|
||||
}
|
||||
|
||||
private static long expiryDateForFeature(String feature, final LicensesMetaData currentLicensesMetaData) {
|
||||
final Map<String, License> effectiveLicenses = getEffectiveLicenses(currentLicensesMetaData);
|
||||
License featureLicense;
|
||||
if ((featureLicense = effectiveLicenses.get(feature)) != null) {
|
||||
return featureLicense.expiryDate();
|
||||
}
|
||||
return -1l;
|
||||
}
|
||||
|
||||
private static Map<String, License> getEffectiveLicenses(final LicensesMetaData metaData) {
|
||||
Map<String, License> map = new HashMap<>();
|
||||
if (metaData != null) {
|
||||
Set<License> licenses = new HashSet<>();
|
||||
for (License license : metaData.getSignedLicenses()) {
|
||||
if (LicenseVerifier.verifyLicense(license)) {
|
||||
licenses.add(license);
|
||||
}
|
||||
}
|
||||
licenses.addAll(metaData.getTrialLicenses());
|
||||
return reduceAndMap(licenses);
|
||||
}
|
||||
return ImmutableMap.copyOf(map);
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* Clears out any completed notification futures
|
||||
*/
|
||||
private static void clearFinishedNotifications(Queue<ScheduledFuture> scheduledNotifications) {
|
||||
while (!scheduledNotifications.isEmpty()) {
|
||||
ScheduledFuture notification = scheduledNotifications.peek();
|
||||
if (notification != null && notification.isDone()) {
|
||||
// remove the notifications that are done
|
||||
scheduledNotifications.poll();
|
||||
} else {
|
||||
// stop emptying out the queue as soon as the first undone future hits
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private String executorName() {
|
||||
return ThreadPool.Names.GENERIC;
|
||||
}
|
||||
|
||||
/**
|
||||
* Schedules an expiry notification with a delay of <code>nextScheduleDelay</code>
|
||||
*/
|
||||
private void scheduleNextNotification(long nextScheduleDelay) {
|
||||
clearFinishedNotifications(scheduledNotifications);
|
||||
|
||||
try {
|
||||
final TimeValue delay = TimeValue.timeValueMillis(nextScheduleDelay);
|
||||
scheduledNotifications.add(threadPool.schedule(delay, executorName(), new LicensingClientNotificationJob()));
|
||||
if (logger.isDebugEnabled()) {
|
||||
logger.debug("Scheduling next notification after: " + delay);
|
||||
}
|
||||
} catch (EsRejectedExecutionException ex) {
|
||||
logger.debug("Couldn't re-schedule licensing client notification job", ex);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Job for notifying on expired license(s) to registered feature(s)
|
||||
* In case of a global block on {@link org.elasticsearch.gateway.GatewayService#STATE_NOT_RECOVERED_BLOCK},
|
||||
* the notification is not run, instead the feature(s) would be notified on the next
|
||||
* {@link #clusterChanged(org.elasticsearch.cluster.ClusterChangedEvent)} with no global block
|
||||
*/
|
||||
private class LicensingClientNotificationJob implements Runnable {
|
||||
|
||||
public LicensingClientNotificationJob() {
|
||||
}
|
||||
|
||||
@Override
|
||||
public void run() {
|
||||
logger.debug("Performing LicensingClientNotificationJob");
|
||||
|
||||
// next clusterChanged event will deal with the missed notifications
|
||||
final ClusterState currentClusterState = clusterService.state();
|
||||
if (!currentClusterState.blocks().hasGlobalBlock(GatewayService.STATE_NOT_RECOVERED_BLOCK)) {
|
||||
final LicensesMetaData currentLicensesMetaData = currentClusterState.metaData().custom(LicensesMetaData.TYPE);
|
||||
notifyFeaturesAndScheduleNotification(currentLicensesMetaData);
|
||||
} else if (logger.isDebugEnabled()) {
|
||||
logger.debug("skip notification [STATE_NOT_RECOVERED_BLOCK]");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public static class PutLicenseRequestHolder {
|
||||
private final PutLicenseRequest request;
|
||||
private final String source;
|
||||
|
||||
public PutLicenseRequestHolder(PutLicenseRequest request, String source) {
|
||||
this.request = request;
|
||||
this.source = source;
|
||||
}
|
||||
}
|
||||
|
||||
public static class DeleteLicenseRequestHolder {
|
||||
private final DeleteLicenseRequest request;
|
||||
private final String source;
|
||||
|
||||
public DeleteLicenseRequestHolder(DeleteLicenseRequest request, String source) {
|
||||
this.request = request;
|
||||
this.source = source;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
/**
|
||||
* Stores configuration and listener for a feature
|
||||
*/
|
||||
private class ListenerHolder {
|
||||
final String feature;
|
||||
final TrialLicenseOptions trialLicenseOptions;
|
||||
final Collection<ExpirationCallback> expirationCallbacks;
|
||||
final Listener listener;
|
||||
final AtomicLong currentExpiryDate = new AtomicLong(-1l);
|
||||
final Queue<ScheduledFuture> notificationQueue;
|
||||
|
||||
volatile boolean initialized = false;
|
||||
volatile boolean enabled = false; // by default, a consumer plugin should be disabled
|
||||
|
||||
private ListenerHolder(String feature, TrialLicenseOptions trialLicenseOptions, Collection<ExpirationCallback> expirationCallbacks, Listener listener, Queue<ScheduledFuture> notificationQueue) {
|
||||
this.feature = feature;
|
||||
this.trialLicenseOptions = trialLicenseOptions;
|
||||
this.expirationCallbacks = expirationCallbacks;
|
||||
this.listener = listener;
|
||||
this.notificationQueue = notificationQueue;
|
||||
}
|
||||
|
||||
private synchronized void enableFeatureIfNeeded(License license) {
|
||||
if (!initialized || !enabled) {
|
||||
listener.onEnabled(license);
|
||||
initialized = true;
|
||||
enabled = true;
|
||||
logger.info("license for [" + feature + "] - valid");
|
||||
}
|
||||
}
|
||||
|
||||
private synchronized void disableFeatureIfNeeded(License license, boolean log) {
|
||||
if (!initialized || enabled) {
|
||||
listener.onDisabled(license);
|
||||
initialized = true;
|
||||
enabled = false;
|
||||
if (log) {
|
||||
logger.info("license for [" + feature + "] - expired");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private Runnable triggerJob(final ExpirationCallback callback) {
|
||||
return new Runnable() {
|
||||
@Override
|
||||
public void run() {
|
||||
LicensesMetaData currentLicensesMetaData = clusterService.state().metaData().custom(LicensesMetaData.TYPE);
|
||||
final Map<String, License> effectiveLicenses = getEffectiveLicenses(currentLicensesMetaData);
|
||||
triggerEvent(effectiveLicenses.get(feature), System.currentTimeMillis(), callback);
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
/**
|
||||
* schedules all {@link ExpirationCallback} associated with <code>license</code>
|
||||
* If callbacks are already scheduled clear up finished notifications from the queue
|
||||
*/
|
||||
private void scheduleNotificationIfNeeded(final License license) {
|
||||
long expiryDate = ((license != null) ? license.expiryDate() : -1l);
|
||||
if (currentExpiryDate.get() == expiryDate) {
|
||||
clearFinishedNotifications(notificationQueue);
|
||||
return;
|
||||
}
|
||||
currentExpiryDate.set(expiryDate);
|
||||
|
||||
// clear out notification queue
|
||||
while (!notificationQueue.isEmpty()) {
|
||||
ScheduledFuture notification = notificationQueue.peek();
|
||||
if (notification != null) {
|
||||
// cancel
|
||||
FutureUtils.cancel(notification);
|
||||
notificationQueue.poll();
|
||||
}
|
||||
}
|
||||
|
||||
long now = System.currentTimeMillis();
|
||||
// Schedule the first for all the callbacks
|
||||
long expiryDuration = expiryDate - now;
|
||||
|
||||
//schedule first event of callbacks that will be activated in the future
|
||||
for (ExpirationCallback expirationCallback : expirationCallbacks) {
|
||||
if (!expirationCallback.matches(expiryDate, now)) {
|
||||
long delay = -1l;
|
||||
switch (expirationCallback.orientation()) {
|
||||
case PRE:
|
||||
delay = expiryDuration - expirationCallback.max().getMillis();
|
||||
break;
|
||||
case POST:
|
||||
if (expiryDuration >= 0l) {
|
||||
delay = expiryDuration + expirationCallback.min().getMillis();
|
||||
} else {
|
||||
delay = (-1l * expiryDuration) - expirationCallback.min().getMillis();
|
||||
}
|
||||
break;
|
||||
}
|
||||
if (delay > 0l) {
|
||||
if (logger.isDebugEnabled()) {
|
||||
logger.debug("Adding first notification for: orientation: " + expirationCallback.orientation().name()
|
||||
+ " min: " + expirationCallback.min()
|
||||
+ " max: " + expirationCallback.max()
|
||||
+ " with delay: " + TimeValue.timeValueMillis(delay)
|
||||
+ " license expiry duration: " + TimeValue.timeValueMillis(expiryDuration));
|
||||
}
|
||||
notificationQueue.add(threadPool.schedule(TimeValue.timeValueMillis(delay), executorName(), triggerJob(expirationCallback)));
|
||||
}
|
||||
}
|
||||
}
|
||||
if (license != null) {
|
||||
// schedule first event of callbacks that match
|
||||
logger.debug("Calling TRIGGER_EVENTS with license for " + license.feature() + " expiry: " + license.expiryDate());
|
||||
for (ExpirationCallback expirationCallback : expirationCallbacks) {
|
||||
triggerEvent(license, now, expirationCallback);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private void triggerEvent(final License license, long now, final ExpirationCallback expirationCallback) {
|
||||
if (expirationCallback.matches(license.expiryDate(), now)) {
|
||||
long expiryDuration = license.expiryDate() - now;
|
||||
boolean expired = expiryDuration <= 0l;
|
||||
if (logger.isDebugEnabled()) {
|
||||
logger.debug("Calling notification on: orientation: " + expirationCallback.orientation().name()
|
||||
+ " min: " + expirationCallback.min()
|
||||
+ " max: " + expirationCallback.max()
|
||||
+ " scheduled after: " + expirationCallback.frequency().getMillis()
|
||||
+ " next interval match: " + expirationCallback.matches(license.expiryDate(), System.currentTimeMillis() + expirationCallback.frequency().getMillis()));
|
||||
}
|
||||
expirationCallback.on(license, new ExpirationStatus(expired, TimeValue.timeValueMillis((!expired) ? expiryDuration : (-1l * expiryDuration))));
|
||||
notificationQueue.add(threadPool.schedule(expirationCallback.frequency(), executorName(), triggerJob(expirationCallback)));
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return "(feature: " + feature + ", enabled: " + enabled + ")";
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Thin wrapper to work with {@link LicensesMetaData}
|
||||
* Never mutates the wrapped metaData
|
||||
*/
|
||||
private static class LicensesWrapper {
|
||||
|
||||
public static LicensesWrapper wrap(LicensesMetaData licensesMetaData) {
|
||||
return new LicensesWrapper(licensesMetaData);
|
||||
}
|
||||
|
||||
private ImmutableList<License> signedLicenses = ImmutableList.of();
|
||||
private ImmutableList<License> trialLicenses = ImmutableList.of();
|
||||
|
||||
private LicensesWrapper(LicensesMetaData licensesMetaData) {
|
||||
if (licensesMetaData != null) {
|
||||
this.signedLicenses = ImmutableList.copyOf(licensesMetaData.getSignedLicenses());
|
||||
this.trialLicenses = ImmutableList.copyOf(licensesMetaData.getTrialLicenses());
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns existingLicenses + newLicenses.
|
||||
* A new license is added if:
|
||||
* - there is no current license for the feature
|
||||
* - current license for feature has a earlier expiry date
|
||||
*/
|
||||
private List<License> addAndGetSignedLicenses(Set<License> newLicenses) {
|
||||
final ImmutableMap<String, License> newLicensesMap = reduceAndMap(newLicenses);
|
||||
List<License> newSignedLicenses = new ArrayList<>(signedLicenses);
|
||||
final ImmutableMap<String, License> oldLicenseMap = reduceAndMap(Sets.newHashSet(signedLicenses));
|
||||
for (String newFeature : newLicensesMap.keySet()) {
|
||||
final License newFeatureLicense = newLicensesMap.get(newFeature);
|
||||
if (oldLicenseMap.containsKey(newFeature)) {
|
||||
final License oldFeatureLicense = oldLicenseMap.get(newFeature);
|
||||
if (oldFeatureLicense.expiryDate() < newFeatureLicense.expiryDate()) {
|
||||
newSignedLicenses.add(newFeatureLicense);
|
||||
}
|
||||
} else {
|
||||
newSignedLicenses.add(newFeatureLicense);
|
||||
}
|
||||
}
|
||||
return ImmutableList.copyOf(newSignedLicenses);
|
||||
}
|
||||
|
||||
private List<License> removeAndGetSignedLicenses(Set<String> features) {
|
||||
List<License> updatedSignedLicenses = new ArrayList<>();
|
||||
for (License license : signedLicenses) {
|
||||
if (!features.contains(license.feature())) {
|
||||
updatedSignedLicenses.add(license);
|
||||
}
|
||||
}
|
||||
return ImmutableList.copyOf(updatedSignedLicenses);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Request for trial license generation to master
|
||||
*/
|
||||
private static class RegisterTrialLicenseRequest extends TransportRequest {
|
||||
private int maxNodes;
|
||||
private String feature;
|
||||
private TimeValue duration;
|
||||
|
||||
private RegisterTrialLicenseRequest() {
|
||||
}
|
||||
|
||||
private RegisterTrialLicenseRequest(String feature, TimeValue duration, int maxNodes) {
|
||||
this.maxNodes = maxNodes;
|
||||
this.feature = feature;
|
||||
this.duration = duration;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void readFrom(StreamInput in) throws IOException {
|
||||
super.readFrom(in);
|
||||
maxNodes = in.readVInt();
|
||||
feature = in.readString();
|
||||
duration = new TimeValue(in.readVLong(), TimeUnit.MILLISECONDS);
|
||||
}
|
||||
|
||||
|
||||
@Override
|
||||
public void writeTo(StreamOutput out) throws IOException {
|
||||
super.writeTo(out);
|
||||
out.writeVInt(maxNodes);
|
||||
out.writeString(feature);
|
||||
out.writeVLong(duration.getMillis());
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Request handler for trial license generation to master
|
||||
*/
|
||||
private class RegisterTrialLicenseRequestHandler implements TransportRequestHandler<RegisterTrialLicenseRequest> {
|
||||
|
||||
@Override
|
||||
public void messageReceived(RegisterTrialLicenseRequest request, TransportChannel channel) throws Exception {
|
||||
registerTrialLicense(request);
|
||||
channel.sendResponse(TransportResponse.Empty.INSTANCE);
|
||||
}
|
||||
}
|
||||
}
|
|
@ -1,34 +0,0 @@
|
|||
/*
|
||||
* 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.plugin.core;
|
||||
|
||||
public enum LicensesStatus {
|
||||
VALID((byte) 0),
|
||||
INVALID((byte) 1),
|
||||
EXPIRED((byte) 2);
|
||||
|
||||
private final byte id;
|
||||
|
||||
LicensesStatus(byte id) {
|
||||
this.id = id;
|
||||
}
|
||||
|
||||
public int id() {
|
||||
return id;
|
||||
}
|
||||
|
||||
public static LicensesStatus fromId(int id) {
|
||||
if (id == 0) {
|
||||
return VALID;
|
||||
} else if (id == 1) {
|
||||
return INVALID;
|
||||
} else if (id == 2) {
|
||||
return EXPIRED;
|
||||
} else {
|
||||
throw new IllegalStateException("no valid LicensesStatus for id=" + id);
|
||||
}
|
||||
}
|
||||
}
|
|
@ -1,113 +0,0 @@
|
|||
/*
|
||||
* 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.plugin.core;
|
||||
|
||||
import org.elasticsearch.common.Base64;
|
||||
import org.elasticsearch.common.unit.TimeValue;
|
||||
import org.elasticsearch.common.xcontent.*;
|
||||
import org.elasticsearch.license.core.License;
|
||||
import org.elasticsearch.license.core.Licenses;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.util.Collections;
|
||||
import java.util.UUID;
|
||||
|
||||
import static org.elasticsearch.license.core.CryptUtils.decrypt;
|
||||
import static org.elasticsearch.license.core.CryptUtils.encrypt;
|
||||
|
||||
public class TrialLicenseUtils {
|
||||
|
||||
public static TrialLicenseBuilder builder() {
|
||||
return new TrialLicenseBuilder();
|
||||
}
|
||||
|
||||
public static class TrialLicenseBuilder {
|
||||
private static final String DEFAULT_ISSUER = "elasticsearch";
|
||||
private static final String DEFAULT_TYPE = "trial";
|
||||
private static final String DEFAULT_SUBSCRIPTION_TYPE = "none";
|
||||
|
||||
private String feature;
|
||||
private long expiryDate = -1;
|
||||
private long issueDate = -1;
|
||||
private TimeValue duration;
|
||||
private int maxNodes = -1;
|
||||
private String uid = null;
|
||||
private String issuedTo;
|
||||
|
||||
public TrialLicenseBuilder() {
|
||||
}
|
||||
|
||||
public TrialLicenseBuilder uid(String uid) {
|
||||
this.uid = uid;
|
||||
return this;
|
||||
}
|
||||
|
||||
public TrialLicenseBuilder issuedTo(String issuedTo) {
|
||||
this.issuedTo = issuedTo;
|
||||
return this;
|
||||
}
|
||||
|
||||
public TrialLicenseBuilder maxNodes(int maxNodes) {
|
||||
this.maxNodes = maxNodes;
|
||||
return this;
|
||||
}
|
||||
|
||||
public TrialLicenseBuilder feature(String featureType) {
|
||||
this.feature = featureType;
|
||||
return this;
|
||||
}
|
||||
|
||||
public TrialLicenseBuilder issueDate(long issueDate) {
|
||||
this.issueDate = issueDate;
|
||||
return this;
|
||||
}
|
||||
|
||||
public TrialLicenseBuilder duration(TimeValue duration) {
|
||||
this.duration = duration;
|
||||
return this;
|
||||
}
|
||||
|
||||
public TrialLicenseBuilder expiryDate(long expiryDate) {
|
||||
this.expiryDate = expiryDate;
|
||||
return this;
|
||||
}
|
||||
|
||||
public License build() {
|
||||
if (expiryDate == -1) {
|
||||
expiryDate = issueDate + duration.millis();
|
||||
}
|
||||
if (uid == null) {
|
||||
uid = UUID.randomUUID().toString();
|
||||
}
|
||||
return License.builder()
|
||||
.type(DEFAULT_TYPE)
|
||||
.subscriptionType(DEFAULT_SUBSCRIPTION_TYPE)
|
||||
.issuer(DEFAULT_ISSUER)
|
||||
.uid(uid)
|
||||
.issuedTo(issuedTo)
|
||||
.issueDate(issueDate)
|
||||
.feature(feature)
|
||||
.maxNodes(maxNodes)
|
||||
.expiryDate(expiryDate)
|
||||
.build();
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
public static License fromEncodedTrialLicense(String encodedTrialLicense) throws IOException {
|
||||
byte[] data = decrypt(Base64.decode(encodedTrialLicense));
|
||||
XContentParser parser = XContentFactory.xContent(XContentType.JSON).createParser(data);
|
||||
parser.nextToken();
|
||||
return License.builder().fromXContent(parser).build();
|
||||
}
|
||||
|
||||
public static String toEncodedTrialLicense(License trialLicense) throws IOException {
|
||||
XContentBuilder contentBuilder = XContentFactory.contentBuilder(XContentType.JSON);
|
||||
// trial license is equivalent to a license spec (no signature)
|
||||
trialLicense.toXContent(contentBuilder, new ToXContent.MapParams(Collections.singletonMap(Licenses.LICENSE_SPEC_VIEW_MODE, "true")));
|
||||
return Base64.encodeBytes(encrypt(contentBuilder.bytes().toBytes()));
|
||||
}
|
||||
}
|
|
@ -1,41 +0,0 @@
|
|||
/*
|
||||
* 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.plugin.rest;
|
||||
|
||||
import org.elasticsearch.client.Client;
|
||||
import org.elasticsearch.common.Strings;
|
||||
import org.elasticsearch.common.inject.Inject;
|
||||
import org.elasticsearch.common.settings.Settings;
|
||||
import org.elasticsearch.license.plugin.action.delete.DeleteLicenseAction;
|
||||
import org.elasticsearch.license.plugin.action.delete.DeleteLicenseRequest;
|
||||
import org.elasticsearch.license.plugin.action.delete.DeleteLicenseResponse;
|
||||
import org.elasticsearch.rest.BaseRestHandler;
|
||||
import org.elasticsearch.rest.RestChannel;
|
||||
import org.elasticsearch.rest.RestController;
|
||||
import org.elasticsearch.rest.RestRequest;
|
||||
import org.elasticsearch.rest.action.support.AcknowledgedRestListener;
|
||||
|
||||
import static org.elasticsearch.rest.RestRequest.Method.DELETE;
|
||||
|
||||
public class RestDeleteLicenseAction extends BaseRestHandler {
|
||||
|
||||
@Inject
|
||||
public RestDeleteLicenseAction(Settings settings, RestController controller, Client client) {
|
||||
super(settings, controller, client);
|
||||
controller.registerHandler(DELETE, "/_licenses/{features}", this);
|
||||
}
|
||||
|
||||
|
||||
@Override
|
||||
public void handleRequest(final RestRequest request, final RestChannel channel, final Client client) {
|
||||
final String[] features = Strings.splitStringByCommaToArray(request.param("features"));
|
||||
if (features.length == 0) {
|
||||
throw new IllegalArgumentException("no feature specified for license deletion");
|
||||
}
|
||||
DeleteLicenseRequest deleteLicenseRequest = new DeleteLicenseRequest(features);
|
||||
client.admin().cluster().execute(DeleteLicenseAction.INSTANCE, deleteLicenseRequest, new AcknowledgedRestListener<DeleteLicenseResponse>(channel));
|
||||
}
|
||||
}
|
|
@ -1,59 +0,0 @@
|
|||
/*
|
||||
* 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.plugin.rest;
|
||||
|
||||
import com.google.common.collect.ImmutableMap;
|
||||
import org.elasticsearch.client.Client;
|
||||
import org.elasticsearch.common.inject.Inject;
|
||||
import org.elasticsearch.common.settings.Settings;
|
||||
import org.elasticsearch.common.xcontent.ToXContent;
|
||||
import org.elasticsearch.common.xcontent.XContentBuilder;
|
||||
import org.elasticsearch.license.core.Licenses;
|
||||
import org.elasticsearch.license.plugin.action.get.GetLicenseAction;
|
||||
import org.elasticsearch.license.plugin.action.get.GetLicenseRequest;
|
||||
import org.elasticsearch.license.plugin.action.get.GetLicenseResponse;
|
||||
import org.elasticsearch.rest.*;
|
||||
import org.elasticsearch.rest.action.support.RestBuilderListener;
|
||||
|
||||
import java.util.Map;
|
||||
|
||||
import static org.elasticsearch.rest.RestRequest.Method.GET;
|
||||
import static org.elasticsearch.rest.RestStatus.OK;
|
||||
|
||||
public class RestGetLicenseAction extends BaseRestHandler {
|
||||
|
||||
@Inject
|
||||
public RestGetLicenseAction(Settings settings, RestController controller, Client client) {
|
||||
super(settings, controller, client);
|
||||
controller.registerHandler(GET, "/_licenses", this);
|
||||
}
|
||||
|
||||
/**
|
||||
* There will be only one license displayed per feature, the selected license will have the latest expiry_date
|
||||
* out of all other licenses for the feature.
|
||||
* <p/>
|
||||
* The licenses are sorted by latest issue_date
|
||||
*/
|
||||
@Override
|
||||
public void handleRequest(final RestRequest request, final RestChannel channel, final Client client) {
|
||||
final Map<String, String> overrideParams = ImmutableMap.of(Licenses.REST_VIEW_MODE, "true");
|
||||
final ToXContent.Params params = new ToXContent.DelegatingMapParams(overrideParams, request);
|
||||
GetLicenseRequest getLicenseRequest = new GetLicenseRequest();
|
||||
getLicenseRequest.local(request.paramAsBoolean("local", getLicenseRequest.local()));
|
||||
client.admin().cluster().execute(GetLicenseAction.INSTANCE, getLicenseRequest, new RestBuilderListener<GetLicenseResponse>(channel) {
|
||||
@Override
|
||||
public RestResponse buildResponse(GetLicenseResponse response, XContentBuilder builder) throws Exception {
|
||||
// Default to pretty printing, but allow ?pretty=false to disable
|
||||
if (!request.hasParam("pretty")) {
|
||||
builder.prettyPrint().lfAtEnd();
|
||||
}
|
||||
Licenses.toXContent(response.licenses(), builder, params);
|
||||
return new BytesRestResponse(OK, builder);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
}
|
|
@ -1,69 +0,0 @@
|
|||
/*
|
||||
* 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.plugin.rest;
|
||||
|
||||
import org.elasticsearch.client.Client;
|
||||
import org.elasticsearch.common.inject.Inject;
|
||||
import org.elasticsearch.common.settings.Settings;
|
||||
import org.elasticsearch.common.xcontent.XContentBuilder;
|
||||
import org.elasticsearch.license.plugin.action.put.PutLicenseAction;
|
||||
import org.elasticsearch.license.plugin.action.put.PutLicenseRequest;
|
||||
import org.elasticsearch.license.plugin.action.put.PutLicenseResponse;
|
||||
import org.elasticsearch.license.plugin.core.LicensesStatus;
|
||||
import org.elasticsearch.rest.BaseRestHandler;
|
||||
import org.elasticsearch.rest.RestChannel;
|
||||
import org.elasticsearch.rest.RestController;
|
||||
import org.elasticsearch.rest.RestRequest;
|
||||
import org.elasticsearch.rest.action.support.AcknowledgedRestListener;
|
||||
|
||||
import java.io.IOException;
|
||||
|
||||
import static org.elasticsearch.rest.RestRequest.Method.POST;
|
||||
import static org.elasticsearch.rest.RestRequest.Method.PUT;
|
||||
|
||||
public class RestPutLicenseAction extends BaseRestHandler {
|
||||
|
||||
@Inject
|
||||
public RestPutLicenseAction(Settings settings, RestController controller, Client client) {
|
||||
super(settings, controller, client);
|
||||
controller.registerHandler(PUT, "/_licenses", this);
|
||||
controller.registerHandler(POST, "/_licenses", this);
|
||||
}
|
||||
|
||||
|
||||
@Override
|
||||
public void handleRequest(final RestRequest request, final RestChannel channel, final Client client) {
|
||||
PutLicenseRequest putLicenseRequest = new PutLicenseRequest();
|
||||
putLicenseRequest.licenses(request.content().toUtf8());
|
||||
client.admin().cluster().execute(PutLicenseAction.INSTANCE, putLicenseRequest, new LicensesAcknowledgedListener(channel));
|
||||
}
|
||||
|
||||
private class LicensesAcknowledgedListener extends AcknowledgedRestListener<PutLicenseResponse> {
|
||||
|
||||
|
||||
public LicensesAcknowledgedListener(RestChannel channel) {
|
||||
super(channel);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void addCustomFields(XContentBuilder builder, PutLicenseResponse response) throws IOException {
|
||||
LicensesStatus status = response.status();
|
||||
String statusString = null;
|
||||
switch (status) {
|
||||
case VALID:
|
||||
statusString = "valid";
|
||||
break;
|
||||
case INVALID:
|
||||
statusString = "invalid";
|
||||
break;
|
||||
case EXPIRED:
|
||||
statusString = "expired";
|
||||
break;
|
||||
}
|
||||
builder.field("licenses_status", statusString);
|
||||
}
|
||||
}
|
||||
}
|
|
@ -1,2 +0,0 @@
|
|||
plugin=org.elasticsearch.license.plugin.LicensePlugin
|
||||
version=${project.version}
|
|
@ -1,154 +0,0 @@
|
|||
/*
|
||||
* 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.plugin;
|
||||
|
||||
import com.google.common.base.Predicate;
|
||||
import org.elasticsearch.common.settings.Settings;
|
||||
import org.elasticsearch.common.unit.TimeValue;
|
||||
import org.elasticsearch.gateway.GatewayService;
|
||||
import org.elasticsearch.license.plugin.consumer.TestConsumerPluginBase;
|
||||
import org.elasticsearch.license.plugin.consumer.TestPluginServiceBase;
|
||||
import org.elasticsearch.test.InternalTestCluster;
|
||||
import org.junit.After;
|
||||
import org.junit.Test;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
|
||||
import static org.elasticsearch.test.ElasticsearchIntegrationTest.ClusterScope;
|
||||
import static org.elasticsearch.test.ElasticsearchIntegrationTest.Scope.TEST;
|
||||
import static org.hamcrest.CoreMatchers.equalTo;
|
||||
|
||||
/**
|
||||
* Framework to test licensing plugin integration for existing/new consumer plugins
|
||||
* see {@link org.elasticsearch.license.plugin.LicensesEagerConsumerPluginIntegrationTests} and {@link org.elasticsearch.license.plugin.LicensesLazyConsumerPluginIntegrationTests}
|
||||
* for example usage
|
||||
*/
|
||||
@ClusterScope(scope = TEST, numDataNodes = 10, numClientNodes = 0, transportClientRatio = 0.0)
|
||||
public abstract class AbstractLicensesConsumerPluginIntegrationTests extends AbstractLicensesIntegrationTests {
|
||||
|
||||
protected final TestConsumerPluginBase consumerPlugin;
|
||||
|
||||
public AbstractLicensesConsumerPluginIntegrationTests(TestConsumerPluginBase consumerPlugin) {
|
||||
this.consumerPlugin = consumerPlugin;
|
||||
}
|
||||
|
||||
private final int trialLicenseDurationInSeconds = 10;
|
||||
|
||||
protected Settings nodeSettings(int nodeOrdinal) {
|
||||
return Settings.settingsBuilder()
|
||||
.put(super.nodeSettings(nodeOrdinal))
|
||||
.put(consumerPlugin.name()
|
||||
+ ".trial_license_duration_in_seconds", trialLicenseDurationInSeconds)
|
||||
.putArray("plugin.types", LicensePlugin.class.getName(), consumerPlugin.getClass().getName())
|
||||
.build();
|
||||
}
|
||||
|
||||
@After
|
||||
public void afterTest() throws Exception {
|
||||
wipeAllLicenses();
|
||||
assertThat(awaitBusy(new Predicate<Object>() {
|
||||
@Override
|
||||
public boolean apply(Object o) {
|
||||
return !clusterService().state().blocks().hasGlobalBlock(GatewayService.STATE_NOT_RECOVERED_BLOCK);
|
||||
}
|
||||
}), equalTo(true));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testTrialLicenseAndSignedLicenseNotification() throws Exception {
|
||||
logger.info("using " + consumerPlugin.getClass().getName() + " consumer plugin");
|
||||
logger.info(" --> trial license generated");
|
||||
// managerService should report feature to be enabled on all data nodes
|
||||
assertLicenseManagerEnabledFeatureFor(consumerPlugin.featureName());
|
||||
// consumer plugin service should return enabled on all data nodes
|
||||
assertConsumerPluginEnabledNotification(2);
|
||||
|
||||
logger.info(" --> check trial license expiry notification");
|
||||
// consumer plugin should notify onDisabled on all data nodes (expired trial license)
|
||||
assertConsumerPluginDisabledNotification(trialLicenseDurationInSeconds * 2);
|
||||
assertLicenseManagerDisabledFeatureFor(consumerPlugin.featureName());
|
||||
|
||||
logger.info(" --> put signed license");
|
||||
putLicense(consumerPlugin.featureName(), TimeValue.timeValueSeconds(trialLicenseDurationInSeconds));
|
||||
|
||||
logger.info(" --> check signed license enabled notification");
|
||||
// consumer plugin should notify onEnabled on all data nodes (signed license)
|
||||
assertConsumerPluginEnabledNotification(1);
|
||||
assertLicenseManagerEnabledFeatureFor(consumerPlugin.featureName());
|
||||
|
||||
logger.info(" --> check signed license expiry notification");
|
||||
// consumer plugin should notify onDisabled on all data nodes (expired signed license)
|
||||
assertConsumerPluginDisabledNotification(trialLicenseDurationInSeconds * 2);
|
||||
assertLicenseManagerDisabledFeatureFor(consumerPlugin.featureName());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testTrialLicenseNotification() throws Exception {
|
||||
logger.info(" --> check onEnabled for trial license");
|
||||
// managerService should report feature to be enabled on all data nodes
|
||||
assertLicenseManagerEnabledFeatureFor(consumerPlugin.featureName());
|
||||
// consumer plugin service should return enabled on all data nodes
|
||||
assertConsumerPluginEnabledNotification(1);
|
||||
|
||||
logger.info(" --> sleep for rest of trailLicense duration");
|
||||
Thread.sleep(trialLicenseDurationInSeconds * 1000l);
|
||||
|
||||
logger.info(" --> check trial license expiry notification");
|
||||
// consumer plugin should notify onDisabled on all data nodes (expired signed license)
|
||||
assertConsumerPluginDisabledNotification(trialLicenseDurationInSeconds);
|
||||
assertLicenseManagerDisabledFeatureFor(consumerPlugin.featureName());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testOverlappingTrialAndSignedLicenseNotification() throws Exception {
|
||||
logger.info(" --> check onEnabled for trial license");
|
||||
// managerService should report feature to be enabled on all data nodes
|
||||
assertLicenseManagerEnabledFeatureFor(consumerPlugin.featureName());
|
||||
// consumer plugin service should return enabled on all data nodes
|
||||
assertConsumerPluginEnabledNotification(1);
|
||||
|
||||
logger.info(" --> put signed license while trial license is in effect");
|
||||
putLicense(consumerPlugin.featureName(), TimeValue.timeValueSeconds(trialLicenseDurationInSeconds * 2));
|
||||
|
||||
logger.info(" --> check signed license enabled notification");
|
||||
// consumer plugin should notify onEnabled on all data nodes (signed license)
|
||||
assertConsumerPluginEnabledNotification(1);
|
||||
assertLicenseManagerEnabledFeatureFor(consumerPlugin.featureName());
|
||||
|
||||
logger.info(" --> sleep for rest of trailLicense duration");
|
||||
Thread.sleep(trialLicenseDurationInSeconds * 1000l);
|
||||
|
||||
logger.info(" --> check consumer is still enabled [signed license]");
|
||||
// consumer plugin should notify onEnabled on all data nodes (signed license)
|
||||
assertConsumerPluginEnabledNotification(1);
|
||||
assertLicenseManagerEnabledFeatureFor(consumerPlugin.featureName());
|
||||
|
||||
logger.info(" --> check signed license expiry notification");
|
||||
// consumer plugin should notify onDisabled on all data nodes (expired signed license)
|
||||
assertConsumerPluginDisabledNotification(trialLicenseDurationInSeconds * 2 * 2);
|
||||
assertLicenseManagerDisabledFeatureFor(consumerPlugin.featureName());
|
||||
}
|
||||
|
||||
private void assertConsumerPluginEnabledNotification(int timeoutInSec) throws InterruptedException {
|
||||
assertConsumerPluginNotification(consumerPluginServices(), true, timeoutInSec);
|
||||
}
|
||||
|
||||
private void assertConsumerPluginDisabledNotification(int timeoutInSec) throws InterruptedException {
|
||||
assertConsumerPluginNotification(consumerPluginServices(), false, timeoutInSec);
|
||||
}
|
||||
|
||||
|
||||
private List<TestPluginServiceBase> consumerPluginServices() {
|
||||
final InternalTestCluster clients = internalCluster();
|
||||
List<TestPluginServiceBase> consumerPluginServices = new ArrayList<>();
|
||||
for (TestPluginServiceBase service : clients.getDataNodeInstances(consumerPlugin.service())) {
|
||||
consumerPluginServices.add(service);
|
||||
}
|
||||
return consumerPluginServices;
|
||||
}
|
||||
|
||||
}
|
|
@ -1,205 +0,0 @@
|
|||
/*
|
||||
* 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.plugin;
|
||||
|
||||
import com.google.common.base.Predicate;
|
||||
import com.google.common.collect.Lists;
|
||||
import org.elasticsearch.cluster.ClusterService;
|
||||
import org.elasticsearch.cluster.ClusterState;
|
||||
import org.elasticsearch.cluster.ProcessedClusterStateUpdateTask;
|
||||
import org.elasticsearch.cluster.metadata.MetaData;
|
||||
import org.elasticsearch.common.Nullable;
|
||||
import org.elasticsearch.common.joda.DateMathParser;
|
||||
import org.elasticsearch.common.joda.FormatDateTimeFormatter;
|
||||
import org.elasticsearch.common.joda.Joda;
|
||||
import org.elasticsearch.common.settings.Settings;
|
||||
import org.elasticsearch.common.unit.TimeValue;
|
||||
import org.elasticsearch.license.core.License;
|
||||
import org.elasticsearch.license.plugin.action.put.PutLicenseAction;
|
||||
import org.elasticsearch.license.plugin.action.put.PutLicenseRequestBuilder;
|
||||
import org.elasticsearch.license.plugin.action.put.PutLicenseResponse;
|
||||
import org.elasticsearch.license.plugin.consumer.EagerLicenseRegistrationPluginService;
|
||||
import org.elasticsearch.license.plugin.consumer.LazyLicenseRegistrationPluginService;
|
||||
import org.elasticsearch.license.plugin.consumer.TestPluginServiceBase;
|
||||
import org.elasticsearch.license.plugin.core.LicensesManagerService;
|
||||
import org.elasticsearch.license.plugin.core.LicensesMetaData;
|
||||
import org.elasticsearch.license.plugin.core.LicensesStatus;
|
||||
import org.elasticsearch.test.ElasticsearchIntegrationTest;
|
||||
import org.elasticsearch.test.InternalTestCluster;
|
||||
import org.joda.time.format.DateTimeFormatter;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
import java.util.concurrent.Callable;
|
||||
import java.util.concurrent.CountDownLatch;
|
||||
import java.util.concurrent.TimeUnit;
|
||||
|
||||
import static org.elasticsearch.license.plugin.TestUtils.generateSignedLicense;
|
||||
import static org.hamcrest.CoreMatchers.equalTo;
|
||||
import static org.hamcrest.Matchers.greaterThan;
|
||||
|
||||
public abstract class AbstractLicensesIntegrationTests extends ElasticsearchIntegrationTest {
|
||||
|
||||
private final static FormatDateTimeFormatter formatDateTimeFormatter = Joda.forPattern("yyyy-MM-dd");
|
||||
private final static DateTimeFormatter dateTimeFormatter = formatDateTimeFormatter.printer();
|
||||
private final static DateMathParser dateMathParser = new DateMathParser(formatDateTimeFormatter);
|
||||
|
||||
public static String dateMathString(String time, final long now) {
|
||||
return dateTimeFormatter.print(dateMathParser.parse(time, new Callable<Long>() {
|
||||
@Override
|
||||
public Long call() throws Exception {
|
||||
return now;
|
||||
}
|
||||
}));
|
||||
}
|
||||
|
||||
public static long dateMath(String time, final long now) {
|
||||
return dateMathParser.parse(time, new Callable<Long>() {
|
||||
@Override
|
||||
public Long call() throws Exception {
|
||||
return now;
|
||||
}
|
||||
});
|
||||
}
|
||||
@Override
|
||||
protected Settings nodeSettings(int nodeOrdinal) {
|
||||
return Settings.settingsBuilder()
|
||||
.put("plugins.load_classpath_plugins", false)
|
||||
.put("plugin.types", LicensePlugin.class.getName())
|
||||
.build();
|
||||
}
|
||||
|
||||
@Override
|
||||
protected Settings transportClientSettings() {
|
||||
// Plugin should be loaded on the transport client as well
|
||||
return nodeSettings(0);
|
||||
}
|
||||
|
||||
protected void wipeAllLicenses() throws InterruptedException {
|
||||
final CountDownLatch latch = new CountDownLatch(1);
|
||||
ClusterService clusterService = internalCluster().getInstance(ClusterService.class, internalCluster().getMasterName());
|
||||
clusterService.submitStateUpdateTask("delete licensing metadata", new ProcessedClusterStateUpdateTask() {
|
||||
@Override
|
||||
public void clusterStateProcessed(String source, ClusterState oldState, ClusterState newState) {
|
||||
latch.countDown();
|
||||
}
|
||||
|
||||
@Override
|
||||
public ClusterState execute(ClusterState currentState) throws Exception {
|
||||
MetaData.Builder mdBuilder = MetaData.builder(currentState.metaData());
|
||||
mdBuilder.removeCustom(LicensesMetaData.TYPE);
|
||||
return ClusterState.builder(currentState).metaData(mdBuilder).build();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onFailure(String source, @Nullable Throwable t) {
|
||||
logger.error("error on metaData cleanup after test", t);
|
||||
}
|
||||
});
|
||||
latch.await();
|
||||
}
|
||||
|
||||
protected void putLicense(String feature, TimeValue expiryDuration) throws Exception {
|
||||
License license1 = generateSignedLicense(feature, expiryDuration);
|
||||
final PutLicenseResponse putLicenseResponse = new PutLicenseRequestBuilder(client().admin().cluster(), PutLicenseAction.INSTANCE).setLicense(Lists.newArrayList(license1)).get();
|
||||
assertThat(putLicenseResponse.isAcknowledged(), equalTo(true));
|
||||
assertThat(putLicenseResponse.status(), equalTo(LicensesStatus.VALID));
|
||||
}
|
||||
|
||||
|
||||
protected void assertLicenseManagerEnabledFeatureFor(final String feature) throws InterruptedException {
|
||||
assertLicenseManagerStatusFor(feature, true);
|
||||
}
|
||||
|
||||
protected void assertLicenseManagerDisabledFeatureFor(final String feature) throws InterruptedException {
|
||||
assertLicenseManagerStatusFor(feature, false);
|
||||
}
|
||||
|
||||
protected void assertLicenseManagerStatusFor(final String feature, final boolean expectedEnabled) throws InterruptedException {
|
||||
assertThat("LicensesManagerService for feature " + feature + " should have enabled status of " + expectedEnabled, awaitBusy(new Predicate<Object>() {
|
||||
@Override
|
||||
public boolean apply(Object o) {
|
||||
for (LicensesManagerService managerService : licensesManagerServices()) {
|
||||
if (expectedEnabled != managerService.enabledFeatures().contains(feature)) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
}, 5, TimeUnit.SECONDS), equalTo(true));
|
||||
}
|
||||
|
||||
protected void assertEagerConsumerPluginDisableNotification(int timeoutInSec) throws InterruptedException {
|
||||
assertEagerConsumerPluginNotification(false, timeoutInSec);
|
||||
}
|
||||
|
||||
protected void assertEagerConsumerPluginEnableNotification(int timeoutInSec) throws InterruptedException {
|
||||
assertEagerConsumerPluginNotification(true, timeoutInSec);
|
||||
}
|
||||
|
||||
protected void assertLazyConsumerPluginDisableNotification(int timeoutInSec) throws InterruptedException {
|
||||
assertLazyConsumerPluginNotification(false, timeoutInSec);
|
||||
}
|
||||
|
||||
protected void assertLazyConsumerPluginEnableNotification(int timeoutInSec) throws InterruptedException {
|
||||
assertLazyConsumerPluginNotification(true, timeoutInSec);
|
||||
}
|
||||
|
||||
protected void assertLazyConsumerPluginNotification(final boolean expectedEnabled, int timeoutInSec) throws InterruptedException {
|
||||
final List<TestPluginServiceBase> consumerPluginServices = consumerLazyPluginServices();
|
||||
assertConsumerPluginNotification(consumerPluginServices, expectedEnabled, timeoutInSec);
|
||||
}
|
||||
|
||||
protected void assertEagerConsumerPluginNotification(final boolean expectedEnabled, int timeoutInSec) throws InterruptedException {
|
||||
final List<TestPluginServiceBase> consumerPluginServices = consumerEagerPluginServices();
|
||||
assertConsumerPluginNotification(consumerPluginServices, expectedEnabled, timeoutInSec);
|
||||
}
|
||||
|
||||
protected void assertConsumerPluginNotification(final List<TestPluginServiceBase> consumerPluginServices, final boolean expectedEnabled, int timeoutInSec) throws InterruptedException {
|
||||
assertThat("At least one instance has to be present", consumerPluginServices.size(), greaterThan(0));
|
||||
assertConsumerPluginNotification(consumerPluginServices.get(0).getClass().getName() + " should have license status of: " + expectedEnabled, consumerPluginServices, expectedEnabled, timeoutInSec);
|
||||
}
|
||||
|
||||
private void assertConsumerPluginNotification(String msg, final Iterable<TestPluginServiceBase> consumerPluginServices, final boolean expectedEnabled, int timeoutInSec) throws InterruptedException {
|
||||
boolean success = awaitBusy(new Predicate<Object>() {
|
||||
@Override
|
||||
public boolean apply(Object o) {
|
||||
for (TestPluginServiceBase pluginService : consumerPluginServices) {
|
||||
if (expectedEnabled != pluginService.enabled()) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
}, timeoutInSec + 1, TimeUnit.SECONDS);
|
||||
logger.debug("Notification assertion complete");
|
||||
assertThat(msg, success, equalTo(true));
|
||||
|
||||
}
|
||||
|
||||
private List<TestPluginServiceBase> consumerLazyPluginServices() {
|
||||
final InternalTestCluster clients = internalCluster();
|
||||
List<TestPluginServiceBase> consumerPluginServices = new ArrayList<>();
|
||||
for (TestPluginServiceBase service : clients.getDataNodeInstances(LazyLicenseRegistrationPluginService.class)) {
|
||||
consumerPluginServices.add(service);
|
||||
}
|
||||
return consumerPluginServices;
|
||||
}
|
||||
|
||||
private List<TestPluginServiceBase> consumerEagerPluginServices() {
|
||||
final InternalTestCluster clients = internalCluster();
|
||||
List<TestPluginServiceBase> consumerPluginServices = new ArrayList<>();
|
||||
for (TestPluginServiceBase service : clients.getDataNodeInstances(EagerLicenseRegistrationPluginService.class)) {
|
||||
consumerPluginServices.add(service);
|
||||
}
|
||||
return consumerPluginServices;
|
||||
}
|
||||
|
||||
private Iterable<LicensesManagerService> licensesManagerServices() {
|
||||
final InternalTestCluster clients = internalCluster();
|
||||
return clients.getDataNodeInstances(LicensesManagerService.class);
|
||||
}
|
||||
}
|
|
@ -1,195 +0,0 @@
|
|||
/*
|
||||
* 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.plugin;
|
||||
|
||||
import org.elasticsearch.action.ActionListener;
|
||||
import org.elasticsearch.cluster.ClusterChangedEvent;
|
||||
import org.elasticsearch.cluster.ClusterService;
|
||||
import org.elasticsearch.cluster.node.DiscoveryNode;
|
||||
import org.elasticsearch.cluster.node.DiscoveryNodes;
|
||||
import org.elasticsearch.common.unit.TimeValue;
|
||||
import org.elasticsearch.license.core.License;
|
||||
import org.elasticsearch.license.plugin.action.put.PutLicenseRequest;
|
||||
import org.elasticsearch.license.plugin.core.LicensesClientService;
|
||||
import org.elasticsearch.license.plugin.core.LicensesManagerService;
|
||||
import org.elasticsearch.license.plugin.core.LicensesService;
|
||||
import org.elasticsearch.license.plugin.core.LicensesStatus;
|
||||
import org.elasticsearch.test.InternalTestCluster;
|
||||
import org.junit.Before;
|
||||
|
||||
import java.util.*;
|
||||
import java.util.concurrent.CountDownLatch;
|
||||
import java.util.concurrent.atomic.AtomicBoolean;
|
||||
import java.util.concurrent.atomic.AtomicInteger;
|
||||
|
||||
import static org.elasticsearch.license.plugin.core.LicensesService.LicensesUpdateResponse;
|
||||
import static org.hamcrest.Matchers.equalTo;
|
||||
|
||||
public abstract class AbstractLicensesServiceTests extends AbstractLicensesIntegrationTests {
|
||||
|
||||
private static String node = null;
|
||||
private static String[] nodes;
|
||||
|
||||
@Before
|
||||
public void beforeTest() throws Exception {
|
||||
wipeAllLicenses();
|
||||
|
||||
DiscoveryNodes discoveryNodes = masterClusterService().state().getNodes();
|
||||
Set<String> dataNodeSet = new HashSet<>();
|
||||
for (DiscoveryNode discoveryNode : discoveryNodes) {
|
||||
if (discoveryNode.dataNode()) {
|
||||
dataNodeSet.add(discoveryNode.getName());
|
||||
}
|
||||
}
|
||||
nodes = dataNodeSet.toArray(new String[dataNodeSet.size()]);
|
||||
node = nodes[randomIntBetween(0, nodes.length - 1)];
|
||||
}
|
||||
|
||||
protected void registerAndAckSignedLicenses(final LicensesManagerService masterLicensesManagerService, final List<License> license, final LicensesStatus expectedStatus) {
|
||||
PutLicenseRequest putLicenseRequest = new PutLicenseRequest().licenses(license);
|
||||
LicensesService.PutLicenseRequestHolder requestHolder = new LicensesService.PutLicenseRequestHolder(putLicenseRequest, "test");
|
||||
final CountDownLatch latch = new CountDownLatch(1);
|
||||
final AtomicBoolean success = new AtomicBoolean(false);
|
||||
masterLicensesManagerService.registerLicenses(requestHolder, new ActionListener<LicensesUpdateResponse>() {
|
||||
@Override
|
||||
public void onResponse(LicensesUpdateResponse licensesUpdateResponse) {
|
||||
if (licensesUpdateResponse.isAcknowledged() && licensesUpdateResponse.status() == expectedStatus) {
|
||||
success.set(true);
|
||||
}
|
||||
latch.countDown();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onFailure(Throwable e) {
|
||||
latch.countDown();
|
||||
}
|
||||
});
|
||||
try {
|
||||
latch.await();
|
||||
} catch (InterruptedException e) {
|
||||
fail(e.getMessage());
|
||||
}
|
||||
assertThat("register license(s) failed", success.get(), equalTo(true));
|
||||
}
|
||||
|
||||
protected Action registerWithTrialLicense(final LicensesClientService clientService, final LicensesClientService.Listener clientListener, final String feature, final TimeValue expiryDuration) {
|
||||
return new Action(new Runnable() {
|
||||
@Override
|
||||
public void run() {
|
||||
clientService.register(feature, new LicensesService.TrialLicenseOptions(expiryDuration, 10), Collections.<LicensesService.ExpirationCallback>emptyList(),
|
||||
clientListener);
|
||||
|
||||
// invoke clusterChanged event to flush out pendingRegistration
|
||||
LicensesService licensesService = (LicensesService) clientService;
|
||||
ClusterChangedEvent event = new ClusterChangedEvent("", clusterService().state(), clusterService().state());
|
||||
licensesService.clusterChanged(event);
|
||||
}
|
||||
}, 0, 1, "should trigger onEnable for " + feature + " once [trial license]");
|
||||
}
|
||||
|
||||
protected Action registerWithEventNotification(final LicensesClientService clientService, final LicensesClientService.Listener clientListener, final String feature, final TimeValue expiryDuration, final Collection<LicensesService.ExpirationCallback> expirationCallbacks) {
|
||||
return new Action(new Runnable() {
|
||||
@Override
|
||||
public void run() {
|
||||
clientService.register(feature, new LicensesService.TrialLicenseOptions(expiryDuration, 10), expirationCallbacks,
|
||||
clientListener);
|
||||
|
||||
// invoke clusterChanged event to flush out pendingRegistration
|
||||
LicensesService licensesService = (LicensesService) clientService;
|
||||
ClusterChangedEvent event = new ClusterChangedEvent("", clusterService().state(), clusterService().state());
|
||||
licensesService.clusterChanged(event);
|
||||
}
|
||||
}, 0, 1, "should trigger onEnable for " + feature + " once [trial license]");
|
||||
}
|
||||
|
||||
protected class TestTrackingClientListener implements LicensesClientService.Listener {
|
||||
CountDownLatch enableLatch;
|
||||
CountDownLatch disableLatch;
|
||||
AtomicBoolean enabled = new AtomicBoolean(false);
|
||||
|
||||
final boolean track;
|
||||
final String featureName;
|
||||
|
||||
public TestTrackingClientListener(String featureName) {
|
||||
this(featureName, true);
|
||||
}
|
||||
|
||||
public TestTrackingClientListener(String featureName, boolean track) {
|
||||
this.track = track;
|
||||
this.featureName = featureName;
|
||||
}
|
||||
|
||||
public synchronized void latch(CountDownLatch enableLatch, CountDownLatch disableLatch) {
|
||||
this.enableLatch = enableLatch;
|
||||
this.disableLatch = disableLatch;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onEnabled(License license) {
|
||||
assertNotNull(license);
|
||||
assertThat(license.feature(), equalTo(featureName));
|
||||
enabled.set(true);
|
||||
if (track) {
|
||||
this.enableLatch.countDown();
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onDisabled(License license) {
|
||||
assertNotNull(license);
|
||||
assertThat(license.feature(), equalTo(featureName));
|
||||
enabled.set(false);
|
||||
if (track) {
|
||||
this.disableLatch.countDown();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
protected class Action {
|
||||
final int expectedDisabledCount;
|
||||
final int expectedEnabledCount;
|
||||
final TimeValue timeout;
|
||||
final Runnable action;
|
||||
final String msg;
|
||||
|
||||
protected Action(Runnable action, int expectedEnabledCount, int expectedDisabledCount, String msg) {
|
||||
this(action, expectedEnabledCount, expectedDisabledCount, TimeValue.timeValueSeconds(1), msg);
|
||||
}
|
||||
|
||||
protected Action(Runnable action, int expectedDisabledCount, int expectedEnabledCount, TimeValue timeout, String msg) {
|
||||
this.expectedDisabledCount = expectedDisabledCount;
|
||||
this.expectedEnabledCount = expectedEnabledCount;
|
||||
this.action = action;
|
||||
this.timeout = timeout;
|
||||
this.msg = msg;
|
||||
}
|
||||
|
||||
public void run() {
|
||||
action.run();
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
protected LicensesManagerService masterLicensesManagerService() {
|
||||
final InternalTestCluster clients = internalCluster();
|
||||
return clients.getInstance(LicensesManagerService.class, clients.getMasterName());
|
||||
}
|
||||
|
||||
protected LicensesClientService licensesClientService() {
|
||||
final InternalTestCluster clients = internalCluster();
|
||||
return internalCluster().getInstance(LicensesClientService.class, clients.getMasterName());
|
||||
}
|
||||
|
||||
protected LicensesService licensesService() {
|
||||
final InternalTestCluster clients = internalCluster();
|
||||
return internalCluster().getInstance(LicensesService.class, clients.getMasterName());
|
||||
}
|
||||
|
||||
protected static ClusterService masterClusterService() {
|
||||
final InternalTestCluster clients = internalCluster();
|
||||
return clients.getInstance(ClusterService.class, clients.getMasterName());
|
||||
}
|
||||
}
|
|
@ -1,467 +0,0 @@
|
|||
/*
|
||||
* 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.plugin;
|
||||
|
||||
import com.google.common.base.Predicate;
|
||||
import org.elasticsearch.common.unit.TimeValue;
|
||||
import org.elasticsearch.license.core.License;
|
||||
import org.elasticsearch.license.plugin.core.*;
|
||||
import org.junit.Test;
|
||||
|
||||
import java.util.*;
|
||||
import java.util.concurrent.CountDownLatch;
|
||||
import java.util.concurrent.TimeUnit;
|
||||
import java.util.concurrent.atomic.AtomicInteger;
|
||||
import java.util.concurrent.atomic.AtomicLong;
|
||||
|
||||
import static org.elasticsearch.license.plugin.TestUtils.generateSignedLicense;
|
||||
import static org.elasticsearch.test.ElasticsearchIntegrationTest.ClusterScope;
|
||||
import static org.elasticsearch.test.ElasticsearchIntegrationTest.Scope.TEST;
|
||||
import static org.hamcrest.Matchers.*;
|
||||
|
||||
@ClusterScope(scope = TEST, numDataNodes = 1)
|
||||
public class LicensesClientServiceTests extends AbstractLicensesServiceTests {
|
||||
|
||||
@Test
|
||||
public void testTrialLicenseEnforcement() throws Exception {
|
||||
// register with trial license and assert onEnable and onDisable notification
|
||||
|
||||
final LicensesClientService clientService = licensesClientService();
|
||||
String feature1 = "feature1";
|
||||
final TestTrackingClientListener clientListener = new TestTrackingClientListener(feature1);
|
||||
List<Action> actions = new ArrayList<>();
|
||||
|
||||
final TimeValue expiryDuration = TimeValue.timeValueSeconds(2);
|
||||
actions.add(registerWithTrialLicense(clientService, clientListener, feature1, expiryDuration));
|
||||
actions.add(assertExpiryAction(feature1, "trial", expiryDuration));
|
||||
assertClientListenerNotificationCount(clientListener, actions);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testLicenseWithFutureIssueDate() throws Exception {
|
||||
final LicensesClientService clientService = licensesClientService();
|
||||
String feature = "feature";
|
||||
final TestTrackingClientListener clientListener = new TestTrackingClientListener(feature);
|
||||
List<Action> actions = new ArrayList<>();
|
||||
long now = System.currentTimeMillis();
|
||||
long issueDate = dateMath("now+10d/d", now);
|
||||
|
||||
actions.add(registerWithoutTrialLicense(clientService, clientListener, feature));
|
||||
actions.add(generateAndPutSignedLicenseAction(masterLicensesManagerService(), feature, issueDate, TimeValue.timeValueHours(24 * 20)));
|
||||
actions.add(assertExpiryAction(feature, "signed", TimeValue.timeValueSeconds(4)));
|
||||
assertClientListenerNotificationCount(clientListener, actions);
|
||||
assertThat(clientListener.enabled.get(), equalTo(false));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testPostExpiration() throws Exception {
|
||||
int postExpirySeconds = randomIntBetween(5, 10);
|
||||
TimeValue postExpiryDuration = TimeValue.timeValueSeconds(postExpirySeconds);
|
||||
TimeValue min = TimeValue.timeValueSeconds(postExpirySeconds - randomIntBetween(1, 3));
|
||||
TimeValue max = TimeValue.timeValueSeconds(postExpirySeconds + randomIntBetween(1, 10));
|
||||
|
||||
final LicensesService.ExpirationCallback.Post post = new LicensesService.ExpirationCallback.Post(min, max, TimeValue.timeValueMillis(10)) {
|
||||
|
||||
@Override
|
||||
public void on(License license, LicensesService.ExpirationStatus status) {
|
||||
}
|
||||
};
|
||||
long now = System.currentTimeMillis();
|
||||
assertThat(post.matches(now - postExpiryDuration.millis(), now), equalTo(true));
|
||||
assertThat(post.matches(now + postExpiryDuration.getMillis(), now), equalTo(false));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testPostExpirationWithNullMax() throws Exception {
|
||||
int postExpirySeconds = randomIntBetween(5, 10);
|
||||
TimeValue postExpiryDuration = TimeValue.timeValueSeconds(postExpirySeconds);
|
||||
TimeValue min = TimeValue.timeValueSeconds(postExpirySeconds - randomIntBetween(1, 3));
|
||||
|
||||
final LicensesService.ExpirationCallback.Post post = new LicensesService.ExpirationCallback.Post(min, null, TimeValue.timeValueMillis(10)) {
|
||||
|
||||
@Override
|
||||
public void on(License license, LicensesService.ExpirationStatus status) {
|
||||
}
|
||||
};
|
||||
long now = System.currentTimeMillis();
|
||||
assertThat(post.matches(now - postExpiryDuration.millis(), now), equalTo(true));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testPreExpirationWithNullMin() throws Exception {
|
||||
int expirySeconds = randomIntBetween(5, 10);
|
||||
TimeValue expiryDuration = TimeValue.timeValueSeconds(expirySeconds);
|
||||
TimeValue max = TimeValue.timeValueSeconds(expirySeconds + randomIntBetween(1, 10));
|
||||
|
||||
final LicensesService.ExpirationCallback.Pre pre = new LicensesService.ExpirationCallback.Pre(null, max, TimeValue.timeValueMillis(10)) {
|
||||
|
||||
@Override
|
||||
public void on(License license, LicensesService.ExpirationStatus status) {
|
||||
}
|
||||
};
|
||||
long now = System.currentTimeMillis();
|
||||
assertThat(pre.matches(expiryDuration.millis() + now, now), equalTo(true));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testPreExpiration() throws Exception {
|
||||
int expirySeconds = randomIntBetween(5, 10);
|
||||
TimeValue expiryDuration = TimeValue.timeValueSeconds(expirySeconds);
|
||||
TimeValue min = TimeValue.timeValueSeconds(expirySeconds - randomIntBetween(0, 3));
|
||||
TimeValue max = TimeValue.timeValueSeconds(expirySeconds + randomIntBetween(1, 10));
|
||||
|
||||
final LicensesService.ExpirationCallback.Pre pre = new LicensesService.ExpirationCallback.Pre(min, max, TimeValue.timeValueMillis(10)) {
|
||||
|
||||
@Override
|
||||
public void on(License license, LicensesService.ExpirationStatus status) {
|
||||
}
|
||||
};
|
||||
long now = System.currentTimeMillis();
|
||||
assertThat(pre.matches(expiryDuration.millis() + now, now), equalTo(true));
|
||||
assertThat(pre.matches(now - expiryDuration.getMillis(), now), equalTo(false));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testMultipleEventNotification() throws Exception {
|
||||
final LicensesManagerService licensesManagerService = masterLicensesManagerService();
|
||||
final LicensesClientService clientService = licensesClientService();
|
||||
final String feature = "feature";
|
||||
TestTrackingClientListener clientListener = new TestTrackingClientListener(feature, true);
|
||||
|
||||
List<LicensesService.ExpirationCallback> callbacks = new ArrayList<>();
|
||||
final AtomicInteger preTriggerCountStartingFromOneSecond = new AtomicInteger(0);
|
||||
callbacks.add(preCallbackLatch(TimeValue.timeValueMillis(499), TimeValue.timeValueMillis(999), TimeValue.timeValueMillis(100), preTriggerCountStartingFromOneSecond));
|
||||
final AtomicInteger postTriggerCount = new AtomicInteger(0);
|
||||
callbacks.add(postCallbackLatch(TimeValue.timeValueMillis(10), null, TimeValue.timeValueMillis(200), postTriggerCount));
|
||||
final AtomicInteger preTriggerCountStartingFromTwoSeconds = new AtomicInteger(0);
|
||||
callbacks.add(preCallbackLatch(TimeValue.timeValueSeconds(1), TimeValue.timeValueSeconds(2), TimeValue.timeValueMillis(500), preTriggerCountStartingFromTwoSeconds));
|
||||
|
||||
List<Action> actions = new ArrayList<>();
|
||||
actions.add(registerWithEventNotification(clientService, clientListener, feature, TimeValue.timeValueSeconds(3), callbacks));
|
||||
actions.add(assertExpiryAction(feature, "trial", TimeValue.timeValueMinutes(1)));
|
||||
assertClientListenerNotificationCount(clientListener, actions);
|
||||
assertThat(preTriggerCountStartingFromTwoSeconds.get(), greaterThanOrEqualTo(2));
|
||||
assertThat(preTriggerCountStartingFromTwoSeconds.get(), lessThan(4));
|
||||
assertThat(preTriggerCountStartingFromOneSecond.get(), greaterThan(4));
|
||||
int initialPreTriggerStartingFromTwoSeconds = preTriggerCountStartingFromTwoSeconds.get();
|
||||
int initialPreTriggerCountStartingFromOneSecond = preTriggerCountStartingFromOneSecond.get();
|
||||
assertThat(awaitBusy(new Predicate<Object>() {
|
||||
@Override
|
||||
public boolean apply(Object o) {
|
||||
if (postTriggerCount.get() > 2) {
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
}, 2, TimeUnit.SECONDS), equalTo(true));
|
||||
int previousTriggerCount = postTriggerCount.get();
|
||||
|
||||
// Update license
|
||||
generateAndPutSignedLicenseAction(licensesManagerService, feature, TimeValue.timeValueSeconds(10)).run();
|
||||
assertThat(previousTriggerCount, lessThanOrEqualTo(postTriggerCount.get() + 1));
|
||||
assertThat(initialPreTriggerCountStartingFromOneSecond, equalTo(preTriggerCountStartingFromOneSecond.get()));
|
||||
assertThat(initialPreTriggerStartingFromTwoSeconds, equalTo(preTriggerCountStartingFromTwoSeconds.get()));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testPreEventNotification() throws Exception {
|
||||
final LicensesClientService clientService = licensesClientService();
|
||||
final String feature = "feature";
|
||||
TestTrackingClientListener clientListener = new TestTrackingClientListener(feature, true);
|
||||
AtomicInteger counter = new AtomicInteger(0);
|
||||
List<Action> actions = new ArrayList<>();
|
||||
actions.add(
|
||||
registerWithEventNotification(clientService, clientListener, feature, TimeValue.timeValueSeconds(3),
|
||||
Arrays.asList(
|
||||
preCallbackLatch(TimeValue.timeValueMillis(500), TimeValue.timeValueSeconds(2), TimeValue.timeValueMillis(500), counter)
|
||||
))
|
||||
);
|
||||
actions.add(assertExpiryAction(feature, "trial", TimeValue.timeValueSeconds(3)));
|
||||
assertClientListenerNotificationCount(clientListener, actions);
|
||||
assertThat(counter.get(), greaterThanOrEqualTo(3));
|
||||
assertThat(counter.get(), lessThan(5));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testPostEventNotification() throws Exception {
|
||||
final LicensesClientService clientService = licensesClientService();
|
||||
final String feature = "feature";
|
||||
TestTrackingClientListener clientListener = new TestTrackingClientListener(feature, true);
|
||||
AtomicInteger counter = new AtomicInteger(0);
|
||||
List<Action> actions = new ArrayList<>();
|
||||
actions.add(
|
||||
registerWithEventNotification(clientService, clientListener, feature, TimeValue.timeValueSeconds(1),
|
||||
Arrays.asList(
|
||||
postCallbackLatch(TimeValue.timeValueMillis(500), TimeValue.timeValueSeconds(2), TimeValue.timeValueMillis(500), counter)
|
||||
))
|
||||
);
|
||||
actions.add(assertExpiryAction(feature, "trial", TimeValue.timeValueSeconds(1)));
|
||||
assertClientListenerNotificationCount(clientListener, actions);
|
||||
Thread.sleep(50 + 2000);
|
||||
assertThat(counter.get(), greaterThanOrEqualTo(3));
|
||||
assertThat(counter.get(), lessThan(5));
|
||||
}
|
||||
|
||||
private LicensesService.ExpirationCallback preCallbackLatch(TimeValue min, TimeValue max, TimeValue frequency, final AtomicInteger triggerCount) {
|
||||
return new LicensesService.ExpirationCallback.Pre(min, max, frequency) {
|
||||
@Override
|
||||
public void on(License license, LicensesService.ExpirationStatus status) {
|
||||
triggerCount.incrementAndGet();
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
private LicensesService.ExpirationCallback postCallbackLatch(TimeValue min, TimeValue max, TimeValue frequency, final AtomicInteger triggerCount) {
|
||||
return new LicensesService.ExpirationCallback.Post(min, max, frequency) {
|
||||
@Override
|
||||
public void on(License license, LicensesService.ExpirationStatus status) {
|
||||
triggerCount.incrementAndGet();
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
|
||||
@Test
|
||||
public void testMultipleClientSignedLicenseEnforcement() throws Exception {
|
||||
// multiple client registration with null trial license and then different expiry signed license
|
||||
|
||||
final LicensesManagerService masterLicensesManagerService = masterLicensesManagerService();
|
||||
final LicensesService licensesService = licensesService();
|
||||
String feature1 = "feature1";
|
||||
String feature2 = "feature2";
|
||||
final TestTrackingClientListener clientListener1 = new TestTrackingClientListener(feature1);
|
||||
final TestTrackingClientListener clientListener2 = new TestTrackingClientListener(feature2);
|
||||
List<Action> firstClientActions = new ArrayList<>();
|
||||
List<Action> secondClientActions = new ArrayList<>();
|
||||
|
||||
final TimeValue firstExpiryDuration = TimeValue.timeValueSeconds(5);
|
||||
firstClientActions.add(registerWithoutTrialLicense(licensesService, clientListener1, feature1));
|
||||
firstClientActions.add(generateAndPutSignedLicenseAction(masterLicensesManagerService, feature1, firstExpiryDuration));
|
||||
firstClientActions.add(assertExpiryAction(feature1, "signed", firstExpiryDuration));
|
||||
|
||||
final TimeValue secondExpiryDuration = TimeValue.timeValueSeconds(4);
|
||||
secondClientActions.add(registerWithoutTrialLicense(licensesService, clientListener2, feature2));
|
||||
secondClientActions.add(generateAndPutSignedLicenseAction(masterLicensesManagerService, feature2, secondExpiryDuration));
|
||||
secondClientActions.add(assertExpiryAction(feature2, "signed", secondExpiryDuration));
|
||||
|
||||
if (randomBoolean()) {
|
||||
assertClientListenerNotificationCount(clientListener1, firstClientActions);
|
||||
assertClientListenerNotificationCount(clientListener2, secondClientActions);
|
||||
} else {
|
||||
assertClientListenerNotificationCount(clientListener2, secondClientActions);
|
||||
assertClientListenerNotificationCount(clientListener1, firstClientActions);
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testMultipleClientTrialAndSignedLicenseEnforcement() throws Exception {
|
||||
// multiple client registration: one with trial license and another with signed license (different expiry duration)
|
||||
|
||||
final LicensesManagerService masterLicensesManagerService = masterLicensesManagerService();
|
||||
final LicensesService licensesService = licensesService();
|
||||
String feature1 = "feature1";
|
||||
String feature2 = "feature2";
|
||||
final TestTrackingClientListener clientListener1 = new TestTrackingClientListener(feature1);
|
||||
final TestTrackingClientListener clientListener2 = new TestTrackingClientListener(feature2);
|
||||
List<Action> firstClientActions = new ArrayList<>();
|
||||
List<Action> secondClientActions = new ArrayList<>();
|
||||
|
||||
final TimeValue firstExpiryDuration = TimeValue.timeValueSeconds(5);
|
||||
firstClientActions.add(registerWithoutTrialLicense(licensesService, clientListener1, feature1));
|
||||
firstClientActions.add(generateAndPutSignedLicenseAction(masterLicensesManagerService, feature1, firstExpiryDuration));
|
||||
firstClientActions.add(assertExpiryAction(feature1, "signed", firstExpiryDuration));
|
||||
|
||||
final TimeValue secondExpiryDuration = TimeValue.timeValueSeconds(4);
|
||||
secondClientActions.add(registerWithTrialLicense(licensesService, clientListener2, feature2, secondExpiryDuration));
|
||||
secondClientActions.add(assertExpiryAction(feature2, "trial", secondExpiryDuration));
|
||||
|
||||
if (randomBoolean()) {
|
||||
assertClientListenerNotificationCount(clientListener1, firstClientActions);
|
||||
assertClientListenerNotificationCount(clientListener2, secondClientActions);
|
||||
} else {
|
||||
assertClientListenerNotificationCount(clientListener2, secondClientActions);
|
||||
assertClientListenerNotificationCount(clientListener1, firstClientActions);
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testMultipleClientTrialLicenseRegistration() throws Exception {
|
||||
// multiple client registration: both with trail license of different expiryDuration
|
||||
|
||||
final LicensesService licensesService = licensesService();
|
||||
String feature1 = "feature1";
|
||||
String feature2 = "feature2";
|
||||
final TestTrackingClientListener clientListener1 = new TestTrackingClientListener(feature1);
|
||||
final TestTrackingClientListener clientListener2 = new TestTrackingClientListener(feature2);
|
||||
List<Action> firstClientActions = new ArrayList<>();
|
||||
List<Action> secondClientActions = new ArrayList<>();
|
||||
|
||||
TimeValue firstExpiryDuration = TimeValue.timeValueSeconds(5);
|
||||
firstClientActions.add(registerWithTrialLicense(licensesService, clientListener1, feature1, firstExpiryDuration));
|
||||
firstClientActions.add(assertExpiryAction(feature1, "trial", firstExpiryDuration));
|
||||
|
||||
TimeValue secondExpiryDuration = TimeValue.timeValueSeconds(4);
|
||||
secondClientActions.add(registerWithTrialLicense(licensesService, clientListener2, feature2, secondExpiryDuration));
|
||||
secondClientActions.add(assertExpiryAction(feature2, "trial", secondExpiryDuration));
|
||||
|
||||
if (randomBoolean()) {
|
||||
assertClientListenerNotificationCount(clientListener1, firstClientActions);
|
||||
assertClientListenerNotificationCount(clientListener2, secondClientActions);
|
||||
} else {
|
||||
assertClientListenerNotificationCount(clientListener2, secondClientActions);
|
||||
assertClientListenerNotificationCount(clientListener1, firstClientActions);
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testFeatureWithoutLicense() throws Exception {
|
||||
// client registration with no trial license + no signed license
|
||||
final LicensesClientService clientService = licensesClientService();
|
||||
String feature = "feature1";
|
||||
final TestTrackingClientListener clientListener = new TestTrackingClientListener(feature);
|
||||
List<Action> actions = new ArrayList<>();
|
||||
|
||||
actions.add(registerWithoutTrialLicense(clientService, clientListener, feature));
|
||||
assertClientListenerNotificationCount(clientListener, actions);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testLicenseExpiry() throws Exception {
|
||||
final LicensesClientService clientService = licensesClientService();
|
||||
String feature = "feature1";
|
||||
final TestTrackingClientListener clientListener = new TestTrackingClientListener(feature);
|
||||
List<Action> actions = new ArrayList<>();
|
||||
|
||||
TimeValue expiryDuration = TimeValue.timeValueSeconds(2);
|
||||
actions.add(registerWithTrialLicense(clientService, clientListener, feature, expiryDuration));
|
||||
actions.add(assertExpiryAction(feature, "trial", expiryDuration));
|
||||
|
||||
assertClientListenerNotificationCount(clientListener, actions);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testRandomActionSequenceMultipleFeature() throws Exception {
|
||||
LicensesService licensesService = licensesService();
|
||||
LicensesManagerService masterLicensesManagerService = masterLicensesManagerService();
|
||||
Map<TestTrackingClientListener, List<Action>> clientListenersWithActions = new HashMap<>();
|
||||
|
||||
TimeValue expiryDuration = TimeValue.timeValueSeconds(0);
|
||||
for (int i = 0; i < randomIntBetween(5, 10); i++) {
|
||||
String feature = "feature_" + String.valueOf(i);
|
||||
final TestTrackingClientListener clientListener = new TestTrackingClientListener(feature);
|
||||
expiryDuration = TimeValue.timeValueMillis(randomIntBetween(2, 4) * 1000l + expiryDuration.millis());
|
||||
List<Action> actions = new ArrayList<>();
|
||||
|
||||
if (randomBoolean()) {
|
||||
actions.add(registerWithTrialLicense(licensesService, clientListener, feature, expiryDuration));
|
||||
actions.add(assertExpiryAction(feature, "trial", expiryDuration));
|
||||
} else {
|
||||
actions.add(registerWithoutTrialLicense(licensesService, clientListener, feature));
|
||||
actions.add(generateAndPutSignedLicenseAction(masterLicensesManagerService, feature, expiryDuration));
|
||||
actions.add(assertExpiryAction(feature, "signed", expiryDuration));
|
||||
}
|
||||
clientListenersWithActions.put(clientListener, actions);
|
||||
}
|
||||
|
||||
for (Map.Entry<TestTrackingClientListener, List<Action>> entry : clientListenersWithActions.entrySet()) {
|
||||
assertClientListenerNotificationCount(entry.getKey(), entry.getValue());
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
private Action generateAndPutSignedLicenseAction(final LicensesManagerService masterLicensesManagerService, final String feature, final TimeValue expiryDuration) throws Exception {
|
||||
return generateAndPutSignedLicenseAction(masterLicensesManagerService, feature, -1, expiryDuration);
|
||||
}
|
||||
private Action generateAndPutSignedLicenseAction(final LicensesManagerService masterLicensesManagerService, final String feature, final long issueDate, final TimeValue expiryDuration) throws Exception {
|
||||
return new Action(new Runnable() {
|
||||
@Override
|
||||
public void run() {
|
||||
License license;
|
||||
try {
|
||||
license = generateSignedLicense(feature, issueDate, expiryDuration);
|
||||
} catch (Exception e) {
|
||||
fail(e.getMessage());
|
||||
return;
|
||||
}
|
||||
registerAndAckSignedLicenses(masterLicensesManagerService, Arrays.asList(license), LicensesStatus.VALID);
|
||||
}
|
||||
}, 0, (issueDate <= System.currentTimeMillis()) ? 1 : 0, "should trigger onEnable for " + feature + " once [signed license]");
|
||||
}
|
||||
|
||||
private Action registerWithoutTrialLicense(final LicensesClientService clientService, final LicensesClientService.Listener clientListener, final String feature) {
|
||||
return new Action(new Runnable() {
|
||||
@Override
|
||||
public void run() {
|
||||
clientService.register(feature, null, Collections.<LicensesService.ExpirationCallback>emptyList(), clientListener);
|
||||
}
|
||||
}, 0, 0, "should not trigger any notification [disabled by default]");
|
||||
}
|
||||
|
||||
private Action assertExpiryAction(String feature, String licenseType, TimeValue expiryDuration) {
|
||||
return new Action(new Runnable() {
|
||||
@Override
|
||||
public void run() {
|
||||
}
|
||||
}, 1, 0, TimeValue.timeValueMillis(expiryDuration.getMillis() * 2),
|
||||
"should trigger onDisable for " + feature + " once [" + licenseType + " license expiry]");
|
||||
}
|
||||
|
||||
private void assertClientListenerNotificationCount(final TestTrackingClientListener clientListener, List<Action> actions) throws Exception {
|
||||
final AtomicInteger expectedEnabledCount = new AtomicInteger(0);
|
||||
final AtomicInteger expectedDisableCount = new AtomicInteger(0);
|
||||
|
||||
for (final Action action : actions) {
|
||||
expectedEnabledCount.addAndGet(action.expectedEnabledCount);
|
||||
expectedDisableCount.addAndGet(action.expectedDisabledCount);
|
||||
}
|
||||
|
||||
final CountDownLatch enableLatch = new CountDownLatch(expectedEnabledCount.get());
|
||||
final CountDownLatch disableLatch = new CountDownLatch(expectedDisableCount.get());
|
||||
final AtomicLong cumulativeTimeoutMillis = new AtomicLong(0);
|
||||
clientListener.latch(enableLatch, disableLatch);
|
||||
for (final Action action : actions) {
|
||||
action.run();
|
||||
cumulativeTimeoutMillis.addAndGet(action.timeout.getMillis());
|
||||
}
|
||||
|
||||
if (expectedDisableCount.get() > 0) {
|
||||
assertThat(getActionMsg(true, enableLatch.getCount(), actions), enableLatch.await((cumulativeTimeoutMillis.get() * 2), TimeUnit.MILLISECONDS), equalTo(true));
|
||||
}
|
||||
if (expectedDisableCount.get() > 0) {
|
||||
assertThat(getActionMsg(false, disableLatch.getCount(), actions), disableLatch.await((cumulativeTimeoutMillis.get() * 2), TimeUnit.MILLISECONDS), equalTo(true));
|
||||
}
|
||||
}
|
||||
|
||||
private static String getActionMsg(final boolean enabledCount, final long latchCount, final List<Action> actions) {
|
||||
LicensesMetaData licensesMetaData = masterClusterService().state().metaData().custom(LicensesMetaData.TYPE);
|
||||
Set<String> featureLicenses = new HashSet<>();
|
||||
if (licensesMetaData != null) {
|
||||
for (License license : licensesMetaData.getSignedLicenses()) {
|
||||
featureLicenses.add(license.feature());
|
||||
}
|
||||
for (License license : licensesMetaData.getTrialLicenses()) {
|
||||
featureLicenses.add(license.feature());
|
||||
}
|
||||
}
|
||||
|
||||
StringBuilder featureLicensesString = new StringBuilder();
|
||||
for (String featureLicense : featureLicenses) {
|
||||
if (featureLicensesString.length() != 0) {
|
||||
featureLicensesString.append(", ");
|
||||
}
|
||||
featureLicensesString.append(featureLicense);
|
||||
}
|
||||
|
||||
AtomicLong cumulativeCount = new AtomicLong(0);
|
||||
for (Action action : actions) {
|
||||
cumulativeCount.addAndGet((enabledCount) ? action.expectedEnabledCount : action.expectedDisabledCount);
|
||||
if (latchCount <= cumulativeCount.get()) {
|
||||
return action.msg + "\n Current licenses in cluster state: [" + featureLicensesString.toString() + "]";
|
||||
}
|
||||
}
|
||||
return "there should be no errors";
|
||||
}
|
||||
}
|
|
@ -1,16 +0,0 @@
|
|||
/*
|
||||
* 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.plugin;
|
||||
|
||||
import org.elasticsearch.common.settings.Settings;
|
||||
import org.elasticsearch.license.plugin.consumer.EagerLicenseRegistrationConsumerPlugin;
|
||||
|
||||
public class LicensesEagerConsumerPluginIntegrationTests extends AbstractLicensesConsumerPluginIntegrationTests {
|
||||
|
||||
public LicensesEagerConsumerPluginIntegrationTests() {
|
||||
super(new EagerLicenseRegistrationConsumerPlugin(Settings.EMPTY));
|
||||
}
|
||||
}
|
|
@ -1,16 +0,0 @@
|
|||
/*
|
||||
* 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.plugin;
|
||||
|
||||
import org.elasticsearch.common.settings.Settings;
|
||||
import org.elasticsearch.license.plugin.consumer.LazyLicenseRegistrationConsumerPlugin;
|
||||
|
||||
public class LicensesLazyConsumerPluginIntegrationTests extends AbstractLicensesConsumerPluginIntegrationTests {
|
||||
|
||||
public LicensesLazyConsumerPluginIntegrationTests() {
|
||||
super(new LazyLicenseRegistrationConsumerPlugin(Settings.EMPTY));
|
||||
}
|
||||
}
|
|
@ -1,135 +0,0 @@
|
|||
/*
|
||||
* 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.plugin;
|
||||
|
||||
import com.google.common.collect.Sets;
|
||||
import org.elasticsearch.action.ActionListener;
|
||||
import org.elasticsearch.cluster.ack.ClusterStateUpdateResponse;
|
||||
import org.elasticsearch.common.unit.TimeValue;
|
||||
import org.elasticsearch.license.core.License;
|
||||
import org.elasticsearch.license.plugin.action.delete.DeleteLicenseRequest;
|
||||
import org.elasticsearch.license.plugin.core.*;
|
||||
import org.junit.Test;
|
||||
|
||||
import java.util.Arrays;
|
||||
import java.util.Collections;
|
||||
import java.util.List;
|
||||
import java.util.Set;
|
||||
import java.util.concurrent.CountDownLatch;
|
||||
import java.util.concurrent.atomic.AtomicBoolean;
|
||||
|
||||
import static org.elasticsearch.license.plugin.TestUtils.generateSignedLicense;
|
||||
import static org.elasticsearch.test.ElasticsearchIntegrationTest.ClusterScope;
|
||||
import static org.elasticsearch.test.ElasticsearchIntegrationTest.Scope.TEST;
|
||||
import static org.hamcrest.Matchers.equalTo;
|
||||
|
||||
@ClusterScope(scope = TEST, numDataNodes = 10)
|
||||
public class LicensesManagerServiceTests extends AbstractLicensesServiceTests {
|
||||
|
||||
@Test
|
||||
public void testStoreAndGetLicenses() throws Exception {
|
||||
LicensesManagerService licensesManagerService = masterLicensesManagerService();
|
||||
License shieldShortLicense = generateSignedLicense("shield", TimeValue.timeValueHours(1));
|
||||
License shieldLongLicense = generateSignedLicense("shield", TimeValue.timeValueHours(2));
|
||||
License marvelShortLicense = generateSignedLicense("marvel", TimeValue.timeValueHours(1));
|
||||
License marvelLongLicense = generateSignedLicense("marvel", TimeValue.timeValueHours(2));
|
||||
|
||||
List<License> licenses = Arrays.asList(shieldLongLicense, shieldShortLicense, marvelLongLicense, marvelShortLicense);
|
||||
Collections.shuffle(licenses);
|
||||
registerAndAckSignedLicenses(licensesManagerService, licenses, LicensesStatus.VALID);
|
||||
|
||||
LicensesMetaData licensesMetaData = clusterService().state().metaData().custom(LicensesMetaData.TYPE);
|
||||
|
||||
// all licenses should be stored in the metaData
|
||||
TestUtils.isSame(licenses, licensesMetaData.getSignedLicenses());
|
||||
|
||||
// only the latest expiry date license for each feature should be returned by getLicenses()
|
||||
final List<License> getLicenses = licensesManagerService.getLicenses();
|
||||
TestUtils.isSame(getLicenses, Arrays.asList(shieldLongLicense, marvelLongLicense));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testInvalidLicenseStorage() throws Exception {
|
||||
LicensesManagerService licensesManagerService = masterLicensesManagerService();
|
||||
License signedLicense = generateSignedLicense("shield", TimeValue.timeValueMinutes(2));
|
||||
|
||||
// modify content of signed license
|
||||
License tamperedLicense = License.builder()
|
||||
.fromLicenseSpec(signedLicense, signedLicense.signature())
|
||||
.expiryDate(signedLicense.expiryDate() + 10 * 24 * 60 * 60 * 1000l)
|
||||
.validate()
|
||||
.build();
|
||||
|
||||
registerAndAckSignedLicenses(licensesManagerService, Arrays.asList(tamperedLicense), LicensesStatus.INVALID);
|
||||
|
||||
// ensure that the invalid license never made it to cluster state
|
||||
LicensesMetaData licensesMetaData = clusterService().state().metaData().custom(LicensesMetaData.TYPE);
|
||||
if (licensesMetaData != null) {
|
||||
assertThat(licensesMetaData.getSignedLicenses().size(), equalTo(0));
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testRemoveLicenses() throws Exception {
|
||||
LicensesManagerService licensesManagerService = masterLicensesManagerService();
|
||||
|
||||
// generate a trial license for one feature
|
||||
final LicensesClientService clientService = licensesClientService();
|
||||
final TestTrackingClientListener clientListener = new TestTrackingClientListener("shield", false);
|
||||
registerWithTrialLicense(clientService, clientListener, "shield", TimeValue.timeValueHours(1)).run();
|
||||
|
||||
// generate signed licenses for multiple features
|
||||
License shieldShortLicense = generateSignedLicense("shield", TimeValue.timeValueHours(1));
|
||||
License shieldLongLicense = generateSignedLicense("shield", TimeValue.timeValueHours(2));
|
||||
License marvelShortLicense = generateSignedLicense("marvel", TimeValue.timeValueHours(1));
|
||||
License marvelLongLicense = generateSignedLicense("marvel", TimeValue.timeValueHours(2));
|
||||
|
||||
List<License> licenses = Arrays.asList(shieldLongLicense, shieldShortLicense, marvelLongLicense, marvelShortLicense);
|
||||
Collections.shuffle(licenses);
|
||||
registerAndAckSignedLicenses(licensesManagerService, licenses, LicensesStatus.VALID);
|
||||
|
||||
// remove license(s) for one feature out of two
|
||||
removeAndAckSignedLicenses(licensesManagerService, Sets.newHashSet("shield"));
|
||||
LicensesMetaData licensesMetaData = clusterService().state().metaData().custom(LicensesMetaData.TYPE);
|
||||
TestUtils.isSame(Arrays.asList(marvelLongLicense, marvelShortLicense), licensesMetaData.getSignedLicenses());
|
||||
// check that trial license is not removed
|
||||
assertThat(licensesMetaData.getTrialLicenses().size(), equalTo(1));
|
||||
|
||||
// remove license(s) for all features
|
||||
removeAndAckSignedLicenses(licensesManagerService, Sets.newHashSet("shield", "marvel"));
|
||||
licensesMetaData = clusterService().state().metaData().custom(LicensesMetaData.TYPE);
|
||||
assertThat(licensesMetaData.getSignedLicenses().size(), equalTo(0));
|
||||
// check that trial license is not removed
|
||||
assertThat(licensesMetaData.getTrialLicenses().size(), equalTo(1));
|
||||
}
|
||||
|
||||
private void removeAndAckSignedLicenses(final LicensesManagerService masterLicensesManagerService, final Set<String> featuresToDelete) {
|
||||
DeleteLicenseRequest deleteLicenseRequest = new DeleteLicenseRequest(featuresToDelete.toArray(new String[featuresToDelete.size()]));
|
||||
LicensesService.DeleteLicenseRequestHolder requestHolder = new LicensesService.DeleteLicenseRequestHolder(deleteLicenseRequest, "test");
|
||||
final CountDownLatch latch = new CountDownLatch(1);
|
||||
final AtomicBoolean success = new AtomicBoolean(false);
|
||||
masterLicensesManagerService.removeLicenses(requestHolder, new ActionListener<ClusterStateUpdateResponse>() {
|
||||
@Override
|
||||
public void onResponse(ClusterStateUpdateResponse clusterStateUpdateResponse) {
|
||||
if (clusterStateUpdateResponse.isAcknowledged()) {
|
||||
success.set(true);
|
||||
}
|
||||
latch.countDown();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onFailure(Throwable throwable) {
|
||||
latch.countDown();
|
||||
}
|
||||
});
|
||||
try {
|
||||
latch.await();
|
||||
} catch (InterruptedException e) {
|
||||
fail(e.getMessage());
|
||||
}
|
||||
assertThat("remove license(s) failed", success.get(), equalTo(true));
|
||||
}
|
||||
}
|
|
@ -1,152 +0,0 @@
|
|||
/*
|
||||
* 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.plugin;
|
||||
|
||||
import org.elasticsearch.common.unit.TimeValue;
|
||||
import org.elasticsearch.common.xcontent.*;
|
||||
import org.elasticsearch.license.core.License;
|
||||
import org.elasticsearch.license.plugin.core.LicensesMetaData;
|
||||
import org.elasticsearch.license.plugin.core.TrialLicenseUtils;
|
||||
import org.elasticsearch.test.ElasticsearchTestCase;
|
||||
import org.junit.Test;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.Arrays;
|
||||
import java.util.List;
|
||||
|
||||
import static org.hamcrest.Matchers.equalTo;
|
||||
import static org.hamcrest.Matchers.nullValue;
|
||||
|
||||
public class LicensesMetaDataSerializationTests extends ElasticsearchTestCase {
|
||||
|
||||
@Test
|
||||
public void testXContentSerializationOneSignedLicense() throws Exception {
|
||||
License license = TestUtils.generateSignedLicense("feature", TimeValue.timeValueHours(2));
|
||||
LicensesMetaData licensesMetaData = new LicensesMetaData(Arrays.asList(license), new ArrayList<License>());
|
||||
XContentBuilder builder = XContentFactory.jsonBuilder();
|
||||
builder.startObject("licensesMetaData");
|
||||
licensesMetaData.toXContent(builder, ToXContent.EMPTY_PARAMS);
|
||||
builder.endObject();
|
||||
byte[] serializedBytes = builder.bytes().toBytes();
|
||||
|
||||
LicensesMetaData licensesMetaDataFromXContent = getLicensesMetaDataFromXContent(serializedBytes);
|
||||
|
||||
assertThat(licensesMetaDataFromXContent.getSignedLicenses().size(), equalTo(1));
|
||||
TestUtils.isSame(licensesMetaDataFromXContent.getSignedLicenses().get(0), license);
|
||||
assertThat(licensesMetaDataFromXContent.getTrialLicenses().size(), equalTo(0));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testXContentSerializationManySignedLicense() throws Exception {
|
||||
List<License> licenses = new ArrayList<>();
|
||||
int n = randomIntBetween(2, 5);
|
||||
for (int i = 0; i < n; i++) {
|
||||
licenses.add(TestUtils.generateSignedLicense("feature__" + String.valueOf(i), TimeValue.timeValueHours(2)));
|
||||
}
|
||||
LicensesMetaData licensesMetaData = new LicensesMetaData(licenses, new ArrayList<License>());
|
||||
XContentBuilder builder = XContentFactory.jsonBuilder();
|
||||
builder.startObject("licensesMetaData");
|
||||
licensesMetaData.toXContent(builder, ToXContent.EMPTY_PARAMS);
|
||||
builder.endObject();
|
||||
byte[] serializedBytes = builder.bytes().toBytes();
|
||||
|
||||
LicensesMetaData licensesMetaDataFromXContent = getLicensesMetaDataFromXContent(serializedBytes);
|
||||
|
||||
assertThat(licensesMetaDataFromXContent.getSignedLicenses().size(), equalTo(n));
|
||||
TestUtils.isSame(licensesMetaDataFromXContent.getSignedLicenses(), licenses);
|
||||
assertThat(licensesMetaDataFromXContent.getTrialLicenses().size(), equalTo(0));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testXContentSerializationOneTrial() throws Exception {
|
||||
final License trialLicense = TrialLicenseUtils.builder()
|
||||
.feature("feature")
|
||||
.duration(TimeValue.timeValueHours(2))
|
||||
.maxNodes(5)
|
||||
.issuedTo("customer")
|
||||
.issueDate(System.currentTimeMillis())
|
||||
.build();
|
||||
LicensesMetaData licensesMetaData = new LicensesMetaData(new ArrayList<License>(), Arrays.asList(trialLicense));
|
||||
XContentBuilder builder = XContentFactory.jsonBuilder();
|
||||
builder.startObject("licensesMetaData");
|
||||
licensesMetaData.toXContent(builder, ToXContent.EMPTY_PARAMS);
|
||||
builder.endObject();
|
||||
byte[] serializedBytes = builder.bytes().toBytes();
|
||||
|
||||
LicensesMetaData licensesMetaDataFromXContent = getLicensesMetaDataFromXContent(serializedBytes);
|
||||
|
||||
assertThat(licensesMetaDataFromXContent.getTrialLicenses().size(), equalTo(1));
|
||||
TestUtils.isSame(licensesMetaDataFromXContent.getTrialLicenses().iterator().next(), trialLicense);
|
||||
assertThat(licensesMetaDataFromXContent.getSignedLicenses().size(), equalTo(0));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testXContentSerializationManyTrial() throws Exception {
|
||||
final TrialLicenseUtils.TrialLicenseBuilder trialLicenseBuilder = TrialLicenseUtils.builder()
|
||||
.duration(TimeValue.timeValueHours(2))
|
||||
.maxNodes(5)
|
||||
.issuedTo("customer")
|
||||
.issueDate(System.currentTimeMillis());
|
||||
int n = randomIntBetween(2, 5);
|
||||
List<License> trialLicenses = new ArrayList<>(n);
|
||||
for (int i = 0; i < n; i++) {
|
||||
trialLicenses.add(trialLicenseBuilder.feature("feature__" + String.valueOf(i)).build());
|
||||
}
|
||||
LicensesMetaData licensesMetaData = new LicensesMetaData(new ArrayList<License>(), trialLicenses);
|
||||
XContentBuilder builder = XContentFactory.jsonBuilder();
|
||||
builder.startObject("licensesMetaData");
|
||||
licensesMetaData.toXContent(builder, ToXContent.EMPTY_PARAMS);
|
||||
builder.endObject();
|
||||
byte[] serializedBytes = builder.bytes().toBytes();
|
||||
|
||||
LicensesMetaData licensesMetaDataFromXContent = getLicensesMetaDataFromXContent(serializedBytes);
|
||||
|
||||
assertThat(licensesMetaDataFromXContent.getTrialLicenses().size(), equalTo(n));
|
||||
TestUtils.isSame(licensesMetaDataFromXContent.getTrialLicenses(), trialLicenses);
|
||||
assertThat(licensesMetaDataFromXContent.getSignedLicenses().size(), equalTo(0));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testXContentSerializationManyTrialAndSignedLicenses() throws Exception {
|
||||
final TrialLicenseUtils.TrialLicenseBuilder trialLicenseBuilder = TrialLicenseUtils.builder()
|
||||
.duration(TimeValue.timeValueHours(2))
|
||||
.maxNodes(5)
|
||||
.issuedTo("customer")
|
||||
.issueDate(System.currentTimeMillis());
|
||||
int n = randomIntBetween(2, 5);
|
||||
List<License> trialLicenses = new ArrayList<>(n);
|
||||
for (int i = 0; i < n; i++) {
|
||||
trialLicenses.add(trialLicenseBuilder.feature("feature__" + String.valueOf(i)).build());
|
||||
}
|
||||
List<License> licenses = new ArrayList<>();
|
||||
for (int i = 0; i < n; i++) {
|
||||
licenses.add(TestUtils.generateSignedLicense("feature__" + String.valueOf(i), TimeValue.timeValueHours(2)));
|
||||
}
|
||||
LicensesMetaData licensesMetaData = new LicensesMetaData(licenses, trialLicenses);
|
||||
XContentBuilder builder = XContentFactory.jsonBuilder();
|
||||
builder.startObject("licensesMetaData");
|
||||
licensesMetaData.toXContent(builder, ToXContent.EMPTY_PARAMS);
|
||||
builder.endObject();
|
||||
byte[] serializedBytes = builder.bytes().toBytes();
|
||||
|
||||
LicensesMetaData licensesMetaDataFromXContent = getLicensesMetaDataFromXContent(serializedBytes);
|
||||
|
||||
assertThat(licensesMetaDataFromXContent.getTrialLicenses().size(), equalTo(n));
|
||||
assertThat(licensesMetaDataFromXContent.getSignedLicenses().size(), equalTo(n));
|
||||
TestUtils.isSame(licensesMetaDataFromXContent.getTrialLicenses(), trialLicenses);
|
||||
TestUtils.isSame(licensesMetaDataFromXContent.getSignedLicenses(), licenses);
|
||||
}
|
||||
|
||||
private static LicensesMetaData getLicensesMetaDataFromXContent(byte[] bytes) throws Exception {
|
||||
final XContentParser parser = XContentFactory.xContent(XContentType.JSON).createParser(bytes);
|
||||
parser.nextToken(); // consume null
|
||||
parser.nextToken(); // consume "licensesMetaData"
|
||||
LicensesMetaData licensesMetaDataFromXContent = LicensesMetaData.PROTO.fromXContent(parser);
|
||||
parser.nextToken(); // consume endObject
|
||||
assertThat(parser.nextToken(), nullValue());
|
||||
return licensesMetaDataFromXContent;
|
||||
}
|
||||
}
|
|
@ -1,150 +0,0 @@
|
|||
/*
|
||||
* 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.plugin;
|
||||
|
||||
import com.google.common.base.Predicate;
|
||||
import org.elasticsearch.common.settings.Settings;
|
||||
import org.elasticsearch.common.unit.TimeValue;
|
||||
import org.elasticsearch.gateway.GatewayService;
|
||||
import org.elasticsearch.license.plugin.consumer.EagerLicenseRegistrationConsumerPlugin;
|
||||
import org.elasticsearch.license.plugin.consumer.EagerLicenseRegistrationPluginService;
|
||||
import org.elasticsearch.license.plugin.consumer.LazyLicenseRegistrationConsumerPlugin;
|
||||
import org.elasticsearch.license.plugin.consumer.LazyLicenseRegistrationPluginService;
|
||||
import org.junit.After;
|
||||
import org.junit.Test;
|
||||
|
||||
import static org.elasticsearch.test.ElasticsearchIntegrationTest.ClusterScope;
|
||||
import static org.elasticsearch.test.ElasticsearchIntegrationTest.Scope.TEST;
|
||||
import static org.hamcrest.CoreMatchers.equalTo;
|
||||
|
||||
@ClusterScope(scope = TEST, numDataNodes = 10, numClientNodes = 0)
|
||||
public class LicensesPluginIntegrationTests extends AbstractLicensesIntegrationTests {
|
||||
|
||||
private final boolean useEagerLicenseRegistrationPlugin = randomBoolean();
|
||||
|
||||
private final int trialLicenseDurationInSeconds = 10;
|
||||
|
||||
protected Settings nodeSettings(int nodeOrdinal) {
|
||||
return Settings.settingsBuilder()
|
||||
.put(super.nodeSettings(nodeOrdinal))
|
||||
.put(((useEagerLicenseRegistrationPlugin) ? EagerLicenseRegistrationConsumerPlugin.NAME : LazyLicenseRegistrationConsumerPlugin.NAME)
|
||||
+ ".trial_license_duration_in_seconds", trialLicenseDurationInSeconds)
|
||||
.putArray("plugin.types", LicensePlugin.class.getName(),
|
||||
(useEagerLicenseRegistrationPlugin) ? EagerLicenseRegistrationConsumerPlugin.class.getName() : LazyLicenseRegistrationConsumerPlugin.class.getName())
|
||||
.build();
|
||||
}
|
||||
|
||||
@After
|
||||
public void afterTest() throws Exception {
|
||||
wipeAllLicenses();
|
||||
assertThat(awaitBusy(new Predicate<Object>() {
|
||||
@Override
|
||||
public boolean apply(Object o) {
|
||||
return !clusterService().state().blocks().hasGlobalBlock(GatewayService.STATE_NOT_RECOVERED_BLOCK);
|
||||
}
|
||||
}), equalTo(true));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testTrialLicenseAndSignedLicenseNotification() throws Exception {
|
||||
logger.info("using " + ((useEagerLicenseRegistrationPlugin) ? "eager" : "lazy") + " consumer plugin");
|
||||
logger.info(" --> trial license generated");
|
||||
// managerService should report feature to be enabled on all data nodes
|
||||
assertLicenseManagerEnabledFeatureFor(getCurrentFeatureName());
|
||||
// consumer plugin service should return enabled on all data nodes
|
||||
assertConsumerPluginEnabledNotification(2);
|
||||
|
||||
logger.info(" --> check trial license expiry notification");
|
||||
// consumer plugin should notify onDisabled on all data nodes (expired trial license)
|
||||
assertConsumerPluginDisabledNotification(trialLicenseDurationInSeconds * 2);
|
||||
assertLicenseManagerDisabledFeatureFor(getCurrentFeatureName());
|
||||
|
||||
logger.info(" --> put signed license");
|
||||
putLicense(getCurrentFeatureName(), TimeValue.timeValueSeconds(trialLicenseDurationInSeconds));
|
||||
|
||||
logger.info(" --> check signed license enabled notification");
|
||||
// consumer plugin should notify onEnabled on all data nodes (signed license)
|
||||
assertConsumerPluginEnabledNotification(1);
|
||||
assertLicenseManagerEnabledFeatureFor(getCurrentFeatureName());
|
||||
|
||||
logger.info(" --> check signed license expiry notification");
|
||||
// consumer plugin should notify onDisabled on all data nodes (expired signed license)
|
||||
assertConsumerPluginDisabledNotification(trialLicenseDurationInSeconds * 2);
|
||||
assertLicenseManagerDisabledFeatureFor(getCurrentFeatureName());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testTrialLicenseNotification() throws Exception {
|
||||
logger.info(" --> check onEnabled for trial license");
|
||||
// managerService should report feature to be enabled on all data nodes
|
||||
assertLicenseManagerEnabledFeatureFor(getCurrentFeatureName());
|
||||
// consumer plugin service should return enabled on all data nodes
|
||||
assertConsumerPluginEnabledNotification(1);
|
||||
|
||||
logger.info(" --> sleep for rest of trailLicense duration");
|
||||
Thread.sleep(trialLicenseDurationInSeconds * 1000l);
|
||||
|
||||
logger.info(" --> check trial license expiry notification");
|
||||
// consumer plugin should notify onDisabled on all data nodes (expired signed license)
|
||||
assertConsumerPluginDisabledNotification(trialLicenseDurationInSeconds * 2);
|
||||
assertLicenseManagerDisabledFeatureFor(getCurrentFeatureName());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testOverlappingTrialAndSignedLicenseNotification() throws Exception {
|
||||
logger.info(" --> check onEnabled for trial license");
|
||||
// managerService should report feature to be enabled on all data nodes
|
||||
assertLicenseManagerEnabledFeatureFor(getCurrentFeatureName());
|
||||
// consumer plugin service should return enabled on all data nodes
|
||||
assertConsumerPluginEnabledNotification(1);
|
||||
|
||||
logger.info(" --> put signed license while trial license is in effect");
|
||||
putLicense(getCurrentFeatureName(), TimeValue.timeValueSeconds(trialLicenseDurationInSeconds * 2));
|
||||
|
||||
logger.info(" --> check signed license enabled notification");
|
||||
// consumer plugin should notify onEnabled on all data nodes (signed license)
|
||||
assertConsumerPluginEnabledNotification(1);
|
||||
assertLicenseManagerEnabledFeatureFor(getCurrentFeatureName());
|
||||
|
||||
logger.info(" --> sleep for rest of trailLicense duration");
|
||||
Thread.sleep(trialLicenseDurationInSeconds * 1000l);
|
||||
|
||||
logger.info(" --> check consumer is still enabled [signed license]");
|
||||
// consumer plugin should notify onEnabled on all data nodes (signed license)
|
||||
assertConsumerPluginEnabledNotification(1);
|
||||
assertLicenseManagerEnabledFeatureFor(getCurrentFeatureName());
|
||||
|
||||
logger.info(" --> check signed license expiry notification");
|
||||
// consumer plugin should notify onDisabled on all data nodes (expired signed license)
|
||||
assertConsumerPluginDisabledNotification(trialLicenseDurationInSeconds * 2 * 2);
|
||||
assertLicenseManagerDisabledFeatureFor(getCurrentFeatureName());
|
||||
}
|
||||
|
||||
private String getCurrentFeatureName() {
|
||||
if (useEagerLicenseRegistrationPlugin) {
|
||||
return EagerLicenseRegistrationPluginService.FEATURE_NAME;
|
||||
} else {
|
||||
return LazyLicenseRegistrationPluginService.FEATURE_NAME;
|
||||
}
|
||||
}
|
||||
|
||||
private void assertConsumerPluginEnabledNotification(int timeoutInSec) throws InterruptedException {
|
||||
if (useEagerLicenseRegistrationPlugin) {
|
||||
assertEagerConsumerPluginEnableNotification(timeoutInSec);
|
||||
} else {
|
||||
assertLazyConsumerPluginEnableNotification(timeoutInSec);
|
||||
}
|
||||
}
|
||||
|
||||
private void assertConsumerPluginDisabledNotification(int timeoutInSec) throws InterruptedException {
|
||||
if (useEagerLicenseRegistrationPlugin) {
|
||||
assertEagerConsumerPluginDisableNotification(timeoutInSec);
|
||||
} else {
|
||||
assertLazyConsumerPluginDisableNotification(timeoutInSec);
|
||||
}
|
||||
}
|
||||
|
||||
}
|
|
@ -1,176 +0,0 @@
|
|||
/*
|
||||
* 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.plugin;
|
||||
|
||||
import org.elasticsearch.common.settings.Settings;
|
||||
import org.elasticsearch.common.unit.TimeValue;
|
||||
import org.elasticsearch.license.plugin.consumer.EagerLicenseRegistrationConsumerPlugin;
|
||||
import org.elasticsearch.license.plugin.consumer.EagerLicenseRegistrationPluginService;
|
||||
import org.elasticsearch.license.plugin.consumer.LazyLicenseRegistrationConsumerPlugin;
|
||||
import org.elasticsearch.license.plugin.consumer.LazyLicenseRegistrationPluginService;
|
||||
import org.junit.After;
|
||||
import org.junit.Test;
|
||||
|
||||
import static org.elasticsearch.test.ElasticsearchIntegrationTest.ClusterScope;
|
||||
import static org.elasticsearch.test.ElasticsearchIntegrationTest.Scope.TEST;
|
||||
|
||||
@ClusterScope(scope = TEST, numDataNodes = 0, numClientNodes = 0)
|
||||
public class LicensesPluginsIntegrationTests extends AbstractLicensesIntegrationTests {
|
||||
|
||||
private final String FEATURE_NAME_1 = EagerLicenseRegistrationPluginService.FEATURE_NAME;
|
||||
private final String FEATURE_NAME_2 = LazyLicenseRegistrationPluginService.FEATURE_NAME;
|
||||
|
||||
protected Settings nodeSettings(int nodeOrdinal) {
|
||||
return Settings.settingsBuilder()
|
||||
.put(super.nodeSettings(nodeOrdinal))
|
||||
.putArray("plugin.types", LicensePlugin.class.getName(), EagerLicenseRegistrationConsumerPlugin.class.getName(), LazyLicenseRegistrationConsumerPlugin.class.getName())
|
||||
.build();
|
||||
}
|
||||
|
||||
private Settings nodeSettingsWithConsumerPlugin(int consumer1TrialLicenseDuration, int consumer2TrialLicenseDuration) {
|
||||
return Settings.settingsBuilder()
|
||||
.put(super.nodeSettings(0))
|
||||
.put(EagerLicenseRegistrationConsumerPlugin.NAME + ".trial_license_duration_in_seconds", consumer1TrialLicenseDuration)
|
||||
.put(LazyLicenseRegistrationConsumerPlugin.NAME + ".trial_license_duration_in_seconds", consumer2TrialLicenseDuration)
|
||||
.putArray("plugin.types", LicensePlugin.class.getName(), EagerLicenseRegistrationConsumerPlugin.class.getName(), LazyLicenseRegistrationConsumerPlugin.class.getName())
|
||||
.build();
|
||||
|
||||
}
|
||||
|
||||
@After
|
||||
public void afterTest() throws Exception {
|
||||
wipeAllLicenses();
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testWithNoTrialLicense() throws Exception {
|
||||
int nNodes = randomIntBetween(2, 10);
|
||||
startNodesWithConsumerPlugins(nNodes, -1, -1);
|
||||
|
||||
assertEagerConsumerPluginDisableNotification(1);
|
||||
assertLazyConsumerPluginDisableNotification(1);
|
||||
assertLicenseManagerDisabledFeatureFor(FEATURE_NAME_1);
|
||||
assertLicenseManagerDisabledFeatureFor(FEATURE_NAME_2);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testOneTrialAndNonTrialConsumer() throws Exception {
|
||||
int nNodes = randomIntBetween(2, 10);
|
||||
int consumer2TrialLicenseDuration = 5;
|
||||
startNodesWithConsumerPlugins(nNodes, -1, consumer2TrialLicenseDuration);
|
||||
|
||||
logger.info(" --> trial license generated for " + FEATURE_NAME_2 + " no trial license for " + FEATURE_NAME_1);
|
||||
// managerService should report feature to be enabled on all data nodes
|
||||
assertLicenseManagerDisabledFeatureFor(FEATURE_NAME_1);
|
||||
assertLicenseManagerEnabledFeatureFor(FEATURE_NAME_2);
|
||||
// consumer plugin service should return enabled on all data nodes
|
||||
assertEagerConsumerPluginDisableNotification(1);
|
||||
assertLazyConsumerPluginEnableNotification(1);
|
||||
|
||||
logger.info(" --> put signed license for " + FEATURE_NAME_1);
|
||||
putLicense(FEATURE_NAME_1, TimeValue.timeValueSeconds(consumer2TrialLicenseDuration));
|
||||
|
||||
logger.info(" --> check that both " + FEATURE_NAME_1 + " and " + FEATURE_NAME_2 + " are enabled");
|
||||
assertEagerConsumerPluginEnableNotification(1);
|
||||
assertLicenseManagerEnabledFeatureFor(FEATURE_NAME_1);
|
||||
|
||||
logger.info(" --> check signed license expiry notification");
|
||||
// consumer plugin should notify onDisabled on all data nodes (expired signed license)
|
||||
assertEagerConsumerPluginDisableNotification(consumer2TrialLicenseDuration * 2);
|
||||
assertLazyConsumerPluginDisableNotification(consumer2TrialLicenseDuration * 2);
|
||||
assertLicenseManagerDisabledFeatureFor(FEATURE_NAME_1);
|
||||
assertLicenseManagerDisabledFeatureFor(FEATURE_NAME_2);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testMultipleConsumerPlugins() throws Exception {
|
||||
|
||||
int nNodes = randomIntBetween(2, 10);
|
||||
int consumer1TrialLicenseExpiry = 5;
|
||||
int consumer2TrialLicenseExpiry = 5;
|
||||
startNodesWithConsumerPlugins(nNodes, consumer1TrialLicenseExpiry, consumer2TrialLicenseExpiry);
|
||||
|
||||
logger.info(" --> trial license generated");
|
||||
// managerService should report feature to be enabled on all data nodes
|
||||
assertLicenseManagerEnabledFeatureFor(FEATURE_NAME_1);
|
||||
assertLicenseManagerEnabledFeatureFor(FEATURE_NAME_2);
|
||||
// consumer plugin service should return enabled on all data nodes
|
||||
assertEagerConsumerPluginEnableNotification(1);
|
||||
assertLazyConsumerPluginEnableNotification(1);
|
||||
|
||||
logger.info(" --> check trial license expiry notification");
|
||||
// consumer plugin should notify onDisabled on all data nodes (expired trial license)
|
||||
assertEagerConsumerPluginDisableNotification(consumer1TrialLicenseExpiry * 2);
|
||||
assertLazyConsumerPluginDisableNotification(consumer2TrialLicenseExpiry * 2);
|
||||
assertLicenseManagerDisabledFeatureFor(FEATURE_NAME_1);
|
||||
assertLicenseManagerDisabledFeatureFor(FEATURE_NAME_2);
|
||||
|
||||
logger.info(" --> put signed license");
|
||||
putLicense(FEATURE_NAME_1, TimeValue.timeValueSeconds(consumer1TrialLicenseExpiry));
|
||||
putLicense(FEATURE_NAME_2, TimeValue.timeValueSeconds(consumer2TrialLicenseExpiry));
|
||||
|
||||
logger.info(" --> check signed license enabled notification");
|
||||
// consumer plugin should notify onEnabled on all data nodes (signed license)
|
||||
assertEagerConsumerPluginEnableNotification(1);
|
||||
assertLazyConsumerPluginEnableNotification(1);
|
||||
assertLicenseManagerEnabledFeatureFor(FEATURE_NAME_1);
|
||||
assertLicenseManagerEnabledFeatureFor(FEATURE_NAME_2);
|
||||
|
||||
logger.info(" --> check signed license expiry notification");
|
||||
// consumer plugin should notify onDisabled on all data nodes (expired signed license)
|
||||
assertEagerConsumerPluginDisableNotification(consumer1TrialLicenseExpiry * 2);
|
||||
assertLazyConsumerPluginDisableNotification(consumer2TrialLicenseExpiry * 2);
|
||||
assertLicenseManagerDisabledFeatureFor(FEATURE_NAME_1);
|
||||
assertLicenseManagerDisabledFeatureFor(FEATURE_NAME_2);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testRandomFeatureLicensesActions() throws Exception {
|
||||
int nNodes = randomIntBetween(2, 10);
|
||||
int trialLicenseDuration1 = rarely() ? -1 : randomIntBetween(6, 8);
|
||||
int trialLicenseDuration2 = rarely() ? -1 : randomIntBetween(6, 8);
|
||||
|
||||
startNodesWithConsumerPlugins(nNodes, trialLicenseDuration1, trialLicenseDuration2);
|
||||
|
||||
if (trialLicenseDuration1 != -1) {
|
||||
assertEagerConsumerPluginEnableNotification(trialLicenseDuration1);
|
||||
assertLicenseManagerEnabledFeatureFor(FEATURE_NAME_1);
|
||||
} else {
|
||||
assertEagerConsumerPluginDisableNotification(3 * 2);
|
||||
assertLicenseManagerDisabledFeatureFor(FEATURE_NAME_1);
|
||||
putLicense(FEATURE_NAME_1, TimeValue.timeValueSeconds(5));
|
||||
}
|
||||
|
||||
if (trialLicenseDuration2 != -1) {
|
||||
assertLazyConsumerPluginEnableNotification(trialLicenseDuration2);
|
||||
assertLicenseManagerEnabledFeatureFor(FEATURE_NAME_2);
|
||||
} else {
|
||||
assertLazyConsumerPluginDisableNotification(3 * 2);
|
||||
assertLicenseManagerDisabledFeatureFor(FEATURE_NAME_2);
|
||||
putLicense(FEATURE_NAME_2, TimeValue.timeValueSeconds(5));
|
||||
}
|
||||
|
||||
logger.info(" --> check license enabled notification");
|
||||
assertEagerConsumerPluginEnableNotification(1);
|
||||
assertLazyConsumerPluginEnableNotification(1);
|
||||
assertLicenseManagerEnabledFeatureFor(FEATURE_NAME_1);
|
||||
assertLicenseManagerEnabledFeatureFor(FEATURE_NAME_2);
|
||||
|
||||
logger.info(" --> check license expiry notification");
|
||||
// consumer plugin should notify onDisabled on all data nodes (expired signed license)
|
||||
assertEagerConsumerPluginDisableNotification(8 * 2);
|
||||
assertLazyConsumerPluginDisableNotification(8 * 2);
|
||||
assertLicenseManagerDisabledFeatureFor(FEATURE_NAME_1);
|
||||
assertLicenseManagerDisabledFeatureFor(FEATURE_NAME_2);
|
||||
|
||||
}
|
||||
|
||||
private void startNodesWithConsumerPlugins(int nNodes, int consumer1TrialLicenseDuration, int consumer2TrialLicenseDuration) {
|
||||
for (int i = 0; i < nNodes; i++) {
|
||||
internalCluster().startNode(nodeSettingsWithConsumerPlugin(consumer1TrialLicenseDuration, consumer2TrialLicenseDuration));
|
||||
}
|
||||
}
|
||||
}
|
|
@ -1,172 +0,0 @@
|
|||
/*
|
||||
* 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.plugin;
|
||||
|
||||
import org.elasticsearch.client.ClusterAdminClient;
|
||||
import org.elasticsearch.common.settings.Settings;
|
||||
import org.elasticsearch.common.unit.TimeValue;
|
||||
import org.elasticsearch.license.core.License;
|
||||
import org.elasticsearch.license.plugin.action.get.GetLicenseAction;
|
||||
import org.elasticsearch.license.plugin.action.get.GetLicenseRequestBuilder;
|
||||
import org.elasticsearch.license.plugin.action.get.GetLicenseResponse;
|
||||
import org.elasticsearch.license.plugin.action.put.PutLicenseAction;
|
||||
import org.elasticsearch.license.plugin.action.put.PutLicenseRequestBuilder;
|
||||
import org.elasticsearch.license.plugin.action.put.PutLicenseResponse;
|
||||
import org.elasticsearch.license.plugin.consumer.EagerLicenseRegistrationConsumerPlugin;
|
||||
import org.elasticsearch.license.plugin.consumer.EagerLicenseRegistrationPluginService;
|
||||
import org.elasticsearch.license.plugin.consumer.LazyLicenseRegistrationConsumerPlugin;
|
||||
import org.elasticsearch.license.plugin.consumer.LazyLicenseRegistrationPluginService;
|
||||
import org.elasticsearch.license.plugin.core.LicensesMetaData;
|
||||
import org.elasticsearch.license.plugin.core.LicensesStatus;
|
||||
import org.elasticsearch.node.Node;
|
||||
import org.junit.Test;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
|
||||
import static org.elasticsearch.license.plugin.TestUtils.generateSignedLicense;
|
||||
import static org.elasticsearch.test.ElasticsearchIntegrationTest.ClusterScope;
|
||||
import static org.elasticsearch.test.ElasticsearchIntegrationTest.Scope.TEST;
|
||||
import static org.hamcrest.CoreMatchers.equalTo;
|
||||
import static org.hamcrest.CoreMatchers.notNullValue;
|
||||
|
||||
@ClusterScope(scope = TEST, numDataNodes = 0, numClientNodes = 0, maxNumDataNodes = 0, transportClientRatio = 0)
|
||||
public class LicensesServiceClusterTest extends AbstractLicensesIntegrationTests {
|
||||
|
||||
private final String[] FEATURES = {EagerLicenseRegistrationPluginService.FEATURE_NAME, LazyLicenseRegistrationPluginService.FEATURE_NAME};
|
||||
|
||||
protected Settings transportClientSettings() {
|
||||
return super.transportClientSettings();
|
||||
}
|
||||
|
||||
@Override
|
||||
protected Settings nodeSettings(int nodeOrdinal) {
|
||||
return nodeSettingsBuilder(nodeOrdinal).build();
|
||||
}
|
||||
|
||||
private Settings.Builder nodeSettingsBuilder(int nodeOrdinal) {
|
||||
return Settings.builder()
|
||||
.put(super.nodeSettings(nodeOrdinal))
|
||||
.put("gateway.type", "local")
|
||||
.put("plugins.load_classpath_plugins", false)
|
||||
.put("node.data", true)
|
||||
.put("format", "json")
|
||||
.put(EagerLicenseRegistrationConsumerPlugin.NAME + ".trial_license_duration_in_seconds", 2)
|
||||
.put(LazyLicenseRegistrationConsumerPlugin.NAME + ".trial_license_duration_in_seconds", 2)
|
||||
.putArray("plugin.types", LicensePlugin.class.getName(), EagerLicenseRegistrationConsumerPlugin.class.getName(), LazyLicenseRegistrationConsumerPlugin.class.getName())
|
||||
.put(Node.HTTP_ENABLED, true);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testClusterRestart() throws Exception {
|
||||
wipeAllLicenses();
|
||||
|
||||
int numNodes = randomIntBetween(1, 5);
|
||||
logger.info("--> starting " + numNodes + " node(s)");
|
||||
for (int i = 0; i < numNodes; i++) {
|
||||
internalCluster().startNode();
|
||||
}
|
||||
ensureGreen();
|
||||
|
||||
logger.info("--> put signed license");
|
||||
final List<License> licenses = generateAndPutLicenses();
|
||||
getAndCheckLicense(licenses);
|
||||
logger.info("--> restart all nodes");
|
||||
internalCluster().fullRestart();
|
||||
ensureYellow();
|
||||
|
||||
logger.info("--> get and check signed license");
|
||||
getAndCheckLicense(licenses);
|
||||
|
||||
wipeAllLicenses();
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testClusterNotRecovered() throws Exception {
|
||||
logger.info("--> start one master out of two [recovery state]");
|
||||
internalCluster().startNode(nodeSettingsBuilder(0).put("discovery.zen.minimum_master_nodes", 2).put("node.master", true));
|
||||
// license plugin should not be active when cluster is still recovering
|
||||
assertLicenseManagerFeatureDisabled();
|
||||
assertConsumerPluginDisabledNotification(1);
|
||||
|
||||
logger.info("--> start second master out of two [recovered state]");
|
||||
internalCluster().startNode(nodeSettingsBuilder(1).put("discovery.zen.minimum_master_nodes", 2).put("node.master", true));
|
||||
assertLicenseManagerFeatureEnabled();
|
||||
assertConsumerPluginEnabledNotification(1);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testAtMostOnceTrialLicenseGeneration() throws Exception {
|
||||
wipeAllLicenses();
|
||||
logger.info("--> start one node [trial license should be generated & enabled]");
|
||||
internalCluster().startNode(nodeSettingsBuilder(0));
|
||||
assertLicenseManagerFeatureEnabled();
|
||||
assertConsumerPluginEnabledNotification(1);
|
||||
|
||||
logger.info("--> start another node [trial license should be propagated from the old master not generated]");
|
||||
internalCluster().startNode(nodeSettings(1));
|
||||
assertLicenseManagerFeatureEnabled();
|
||||
assertConsumerPluginEnabledNotification(1);
|
||||
|
||||
logger.info("--> check if multiple trial licenses are found for a feature");
|
||||
LicensesMetaData licensesMetaData = clusterService().state().metaData().custom(LicensesMetaData.TYPE);
|
||||
assertThat(licensesMetaData.getTrialLicenses().size(), equalTo(FEATURES.length));
|
||||
|
||||
wipeAllLicenses();
|
||||
}
|
||||
|
||||
private List<License> generateAndPutLicenses() throws Exception {
|
||||
ClusterAdminClient cluster = internalCluster().client().admin().cluster();
|
||||
List<License> putLicenses = new ArrayList<>(FEATURES.length);
|
||||
for (String feature : FEATURES) {
|
||||
putLicenses.add(generateSignedLicense(feature, TimeValue.timeValueMinutes(1)));
|
||||
}
|
||||
PutLicenseRequestBuilder putLicenseRequestBuilder = new PutLicenseRequestBuilder(cluster, PutLicenseAction.INSTANCE);
|
||||
putLicenseRequestBuilder.setLicense(putLicenses);
|
||||
ensureGreen();
|
||||
|
||||
final PutLicenseResponse putLicenseResponse = putLicenseRequestBuilder.get();
|
||||
|
||||
assertThat(putLicenseResponse.isAcknowledged(), equalTo(true));
|
||||
assertThat(putLicenseResponse.status(), equalTo(LicensesStatus.VALID));
|
||||
|
||||
return putLicenses;
|
||||
}
|
||||
|
||||
private void getAndCheckLicense(List<License> licenses) {
|
||||
ClusterAdminClient cluster = internalCluster().client().admin().cluster();
|
||||
final GetLicenseResponse response = new GetLicenseRequestBuilder(cluster, GetLicenseAction.INSTANCE).get();
|
||||
assertThat(response.licenses().size(), equalTo(licenses.size()));
|
||||
TestUtils.isSame(licenses, response.licenses());
|
||||
|
||||
LicensesMetaData licensesMetaData = clusterService().state().metaData().custom(LicensesMetaData.TYPE);
|
||||
assertThat(licensesMetaData, notNullValue());
|
||||
assertThat(licensesMetaData.getTrialLicenses().size(), equalTo(2));
|
||||
}
|
||||
|
||||
private void assertLicenseManagerFeatureEnabled() throws Exception {
|
||||
for (String feature : FEATURES) {
|
||||
assertLicenseManagerEnabledFeatureFor(feature);
|
||||
}
|
||||
}
|
||||
|
||||
private void assertLicenseManagerFeatureDisabled() throws Exception {
|
||||
for (String feature : FEATURES) {
|
||||
assertLicenseManagerDisabledFeatureFor(feature);
|
||||
}
|
||||
}
|
||||
|
||||
private void assertConsumerPluginEnabledNotification(int timeoutInSec) throws InterruptedException {
|
||||
assertEagerConsumerPluginEnableNotification(timeoutInSec);
|
||||
assertLazyConsumerPluginEnableNotification(timeoutInSec);
|
||||
}
|
||||
|
||||
private void assertConsumerPluginDisabledNotification(int timeoutInSec) throws InterruptedException {
|
||||
assertEagerConsumerPluginDisableNotification(timeoutInSec);
|
||||
assertLazyConsumerPluginDisableNotification(timeoutInSec);
|
||||
}
|
||||
|
||||
}
|
|
@ -1,55 +0,0 @@
|
|||
/*
|
||||
* 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.plugin;
|
||||
|
||||
import com.google.common.base.Predicate;
|
||||
import org.elasticsearch.common.settings.Settings;
|
||||
import org.elasticsearch.license.plugin.consumer.EagerLicenseRegistrationConsumerPlugin;
|
||||
import org.elasticsearch.license.plugin.consumer.EagerLicenseRegistrationPluginService;
|
||||
import org.elasticsearch.node.Node;
|
||||
import org.elasticsearch.test.ElasticsearchIntegrationTest;
|
||||
import org.elasticsearch.test.junit.annotations.TestLogging;
|
||||
import org.junit.Test;
|
||||
|
||||
import java.util.concurrent.TimeUnit;
|
||||
|
||||
import static org.elasticsearch.test.ElasticsearchIntegrationTest.Scope.TEST;
|
||||
import static org.hamcrest.Matchers.equalTo;
|
||||
|
||||
/**
|
||||
*/
|
||||
@ElasticsearchIntegrationTest.ClusterScope(scope = TEST, numDataNodes = 10, numClientNodes = 0)
|
||||
public class LicensesServiceNodeTests extends AbstractLicensesIntegrationTests {
|
||||
|
||||
@Override
|
||||
protected Settings nodeSettings(int nodeOrdinal) {
|
||||
return Settings.settingsBuilder()
|
||||
.put(super.nodeSettings(nodeOrdinal))
|
||||
.put(EagerLicenseRegistrationConsumerPlugin.NAME + ".trial_license_duration_in_seconds", 60 * 5)
|
||||
.putArray("plugin.types", LicensePlugin.class.getName(), EagerLicenseRegistrationConsumerPlugin.class.getName())
|
||||
.put(Node.HTTP_ENABLED, true)
|
||||
.build();
|
||||
}
|
||||
|
||||
@Test
|
||||
@TestLogging("_root:DEBUG")
|
||||
public void testPluginStatus() throws Exception {
|
||||
final Iterable<EagerLicenseRegistrationPluginService> testPluginServices = internalCluster().getDataNodeInstances(EagerLicenseRegistrationPluginService.class);
|
||||
assertThat(awaitBusy(new Predicate<Object>() {
|
||||
@Override
|
||||
public boolean apply(Object o) {
|
||||
for (EagerLicenseRegistrationPluginService pluginService : testPluginServices) {
|
||||
if (!pluginService.enabled()) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
}, 10, TimeUnit.SECONDS), equalTo(true));
|
||||
|
||||
}
|
||||
|
||||
}
|
|
@ -1,265 +0,0 @@
|
|||
/*
|
||||
* 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.plugin;
|
||||
|
||||
import com.google.common.collect.Sets;
|
||||
import org.elasticsearch.action.ActionFuture;
|
||||
import org.elasticsearch.common.unit.TimeValue;
|
||||
import org.elasticsearch.license.core.License;
|
||||
import org.elasticsearch.license.plugin.action.delete.DeleteLicenseAction;
|
||||
import org.elasticsearch.license.plugin.action.delete.DeleteLicenseRequestBuilder;
|
||||
import org.elasticsearch.license.plugin.action.delete.DeleteLicenseResponse;
|
||||
import org.elasticsearch.license.plugin.action.get.GetLicenseAction;
|
||||
import org.elasticsearch.license.plugin.action.get.GetLicenseRequestBuilder;
|
||||
import org.elasticsearch.license.plugin.action.get.GetLicenseResponse;
|
||||
import org.elasticsearch.license.plugin.action.put.PutLicenseAction;
|
||||
import org.elasticsearch.license.plugin.action.put.PutLicenseRequestBuilder;
|
||||
import org.elasticsearch.license.plugin.action.put.PutLicenseResponse;
|
||||
import org.elasticsearch.license.plugin.core.LicensesStatus;
|
||||
import org.junit.After;
|
||||
import org.junit.Test;
|
||||
|
||||
import java.util.Arrays;
|
||||
import java.util.Collections;
|
||||
import java.util.List;
|
||||
|
||||
import static org.elasticsearch.license.plugin.TestUtils.generateSignedLicense;
|
||||
import static org.elasticsearch.test.ElasticsearchIntegrationTest.ClusterScope;
|
||||
import static org.elasticsearch.test.ElasticsearchIntegrationTest.Scope.TEST;
|
||||
import static org.hamcrest.CoreMatchers.equalTo;
|
||||
import static org.hamcrest.CoreMatchers.notNullValue;
|
||||
|
||||
@ClusterScope(scope = TEST, numDataNodes = 10)
|
||||
public class LicensesTransportTests extends AbstractLicensesIntegrationTests {
|
||||
|
||||
@After
|
||||
public void beforeTest() throws Exception {
|
||||
wipeAllLicenses();
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testEmptyGetLicense() throws Exception {
|
||||
final ActionFuture<GetLicenseResponse> getLicenseFuture = new GetLicenseRequestBuilder(client().admin().cluster(), GetLicenseAction.INSTANCE).execute();
|
||||
final GetLicenseResponse getLicenseResponse = getLicenseFuture.get();
|
||||
assertThat("expected 0 licenses; but got: " + getLicenseResponse.licenses().size(), getLicenseResponse.licenses().size(), equalTo(0));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testPutLicense() throws Exception {
|
||||
License signedLicense = generateSignedLicense("shield", TimeValue.timeValueMinutes(2));
|
||||
List<License> actualLicenses = Collections.singletonList(signedLicense);
|
||||
|
||||
// put license
|
||||
PutLicenseRequestBuilder putLicenseRequestBuilder = new PutLicenseRequestBuilder(client().admin().cluster(), PutLicenseAction.INSTANCE)
|
||||
.setLicense(actualLicenses);
|
||||
PutLicenseResponse putLicenseResponse = putLicenseRequestBuilder.get();
|
||||
assertThat(putLicenseResponse.isAcknowledged(), equalTo(true));
|
||||
assertThat(putLicenseResponse.status(), equalTo(LicensesStatus.VALID));
|
||||
|
||||
// get license
|
||||
GetLicenseResponse getLicenseResponse = new GetLicenseRequestBuilder(client().admin().cluster(), GetLicenseAction.INSTANCE).get();
|
||||
assertThat(getLicenseResponse.licenses(), notNullValue());
|
||||
assertThat(getLicenseResponse.licenses().size(), equalTo(1));
|
||||
|
||||
// check license
|
||||
TestUtils.isSame(signedLicense, getLicenseResponse.licenses().get(0));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testPutLicenseFromString() throws Exception {
|
||||
License signedLicense = generateSignedLicense("shield", TimeValue.timeValueMinutes(2));
|
||||
String licenseString = TestUtils.dumpLicense(signedLicense);
|
||||
|
||||
// put license source
|
||||
PutLicenseRequestBuilder putLicenseRequestBuilder = new PutLicenseRequestBuilder(client().admin().cluster(), PutLicenseAction.INSTANCE)
|
||||
.setLicense(licenseString);
|
||||
PutLicenseResponse putLicenseResponse = putLicenseRequestBuilder.get();
|
||||
assertThat(putLicenseResponse.isAcknowledged(), equalTo(true));
|
||||
assertThat(putLicenseResponse.status(), equalTo(LicensesStatus.VALID));
|
||||
|
||||
// get license
|
||||
GetLicenseResponse getLicenseResponse = new GetLicenseRequestBuilder(client().admin().cluster(), GetLicenseAction.INSTANCE).get();
|
||||
assertThat(getLicenseResponse.licenses(), notNullValue());
|
||||
assertThat(getLicenseResponse.licenses().size(), equalTo(1));
|
||||
|
||||
// check license
|
||||
TestUtils.isSame(signedLicense, getLicenseResponse.licenses().get(0));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testPutInvalidLicense() throws Exception {
|
||||
License signedLicense = generateSignedLicense("shield", TimeValue.timeValueMinutes(2));
|
||||
|
||||
// modify content of signed license
|
||||
License tamperedLicense = License.builder()
|
||||
.fromLicenseSpec(signedLicense, signedLicense.signature())
|
||||
.expiryDate(signedLicense.expiryDate() + 10 * 24 * 60 * 60 * 1000l)
|
||||
.validate()
|
||||
.build();
|
||||
|
||||
PutLicenseRequestBuilder builder = new PutLicenseRequestBuilder(client().admin().cluster(), PutLicenseAction.INSTANCE);
|
||||
builder.setLicense(Collections.singletonList(tamperedLicense));
|
||||
|
||||
// try to put license (should be invalid)
|
||||
final PutLicenseResponse putLicenseResponse = builder.get();
|
||||
assertThat(putLicenseResponse.status(), equalTo(LicensesStatus.INVALID));
|
||||
|
||||
// try to get invalid license
|
||||
GetLicenseResponse getLicenseResponse = new GetLicenseRequestBuilder(client().admin().cluster(), GetLicenseAction.INSTANCE).get();
|
||||
assertThat(getLicenseResponse.licenses().size(), equalTo(0));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testPutExpiredLicense() throws Exception {
|
||||
License expiredLicense = generateSignedLicense("feature", dateMath("now-10d/d", System.currentTimeMillis()), TimeValue.timeValueMinutes(2));
|
||||
License signedLicense = generateSignedLicense("feature", TimeValue.timeValueMinutes(2));
|
||||
|
||||
PutLicenseRequestBuilder builder = new PutLicenseRequestBuilder(client().admin().cluster(), PutLicenseAction.INSTANCE);
|
||||
builder.setLicense(Arrays.asList(signedLicense, expiredLicense));
|
||||
|
||||
// put license should return valid (as there is one valid license)
|
||||
final PutLicenseResponse putLicenseResponse = builder.get();
|
||||
assertThat(putLicenseResponse.status(), equalTo(LicensesStatus.VALID));
|
||||
|
||||
// get license should not return the expired license
|
||||
GetLicenseResponse getLicenseResponse = new GetLicenseRequestBuilder(client().admin().cluster(), GetLicenseAction.INSTANCE).get();
|
||||
assertThat(getLicenseResponse.licenses().size(), equalTo(1));
|
||||
|
||||
TestUtils.isSame(getLicenseResponse.licenses().get(0), signedLicense);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testPutLicensesForSameFeature() throws Exception {
|
||||
License shortedSignedLicense = generateSignedLicense("shield", TimeValue.timeValueMinutes(2));
|
||||
License longerSignedLicense = generateSignedLicense("shield", TimeValue.timeValueMinutes(5));
|
||||
List<License> actualLicenses = Arrays.asList(longerSignedLicense, shortedSignedLicense);
|
||||
|
||||
// put license
|
||||
PutLicenseRequestBuilder putLicenseRequestBuilder = new PutLicenseRequestBuilder(client().admin().cluster(), PutLicenseAction.INSTANCE)
|
||||
.setLicense(actualLicenses);
|
||||
PutLicenseResponse putLicenseResponse = putLicenseRequestBuilder.get();
|
||||
assertThat(putLicenseResponse.isAcknowledged(), equalTo(true));
|
||||
assertThat(putLicenseResponse.status(), equalTo(LicensesStatus.VALID));
|
||||
|
||||
// get should return only one license (with longer expiry date)
|
||||
GetLicenseResponse getLicenseResponse = new GetLicenseRequestBuilder(client().admin().cluster(), GetLicenseAction.INSTANCE).get();
|
||||
assertThat(getLicenseResponse.licenses(), notNullValue());
|
||||
assertThat(getLicenseResponse.licenses().size(), equalTo(1));
|
||||
|
||||
// check license
|
||||
TestUtils.isSame(longerSignedLicense, getLicenseResponse.licenses().get(0));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testPutLicensesForMultipleFeatures() throws Exception {
|
||||
License shieldLicense = generateSignedLicense("shield", TimeValue.timeValueMinutes(2));
|
||||
License marvelLicense = generateSignedLicense("marvel", TimeValue.timeValueMinutes(5));
|
||||
List<License> actualLicenses = Arrays.asList(marvelLicense, shieldLicense);
|
||||
|
||||
// put license
|
||||
PutLicenseRequestBuilder putLicenseRequestBuilder = new PutLicenseRequestBuilder(client().admin().cluster(), PutLicenseAction.INSTANCE)
|
||||
.setLicense(actualLicenses);
|
||||
PutLicenseResponse putLicenseResponse = putLicenseRequestBuilder.get();
|
||||
assertThat(putLicenseResponse.isAcknowledged(), equalTo(true));
|
||||
assertThat(putLicenseResponse.status(), equalTo(LicensesStatus.VALID));
|
||||
|
||||
// get should return both the licenses
|
||||
GetLicenseResponse getLicenseResponse = new GetLicenseRequestBuilder(client().admin().cluster(), GetLicenseAction.INSTANCE).get();
|
||||
assertThat(getLicenseResponse.licenses(), notNullValue());
|
||||
|
||||
// check license
|
||||
TestUtils.isSame(actualLicenses, getLicenseResponse.licenses());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testPutMultipleLicensesForMultipleFeatures() throws Exception {
|
||||
License shortedSignedLicense = generateSignedLicense("shield", TimeValue.timeValueMinutes(2));
|
||||
License longerSignedLicense = generateSignedLicense("shield", TimeValue.timeValueMinutes(5));
|
||||
License marvelLicense = generateSignedLicense("marvel", TimeValue.timeValueMinutes(5));
|
||||
List<License> actualLicenses = Arrays.asList(marvelLicense, shortedSignedLicense, longerSignedLicense);
|
||||
|
||||
// put license
|
||||
PutLicenseRequestBuilder putLicenseRequestBuilder = new PutLicenseRequestBuilder(client().admin().cluster(), PutLicenseAction.INSTANCE)
|
||||
.setLicense(actualLicenses);
|
||||
PutLicenseResponse putLicenseResponse = putLicenseRequestBuilder.get();
|
||||
assertThat(putLicenseResponse.isAcknowledged(), equalTo(true));
|
||||
assertThat(putLicenseResponse.status(), equalTo(LicensesStatus.VALID));
|
||||
|
||||
// get should return both the licenses
|
||||
GetLicenseResponse getLicenseResponse = new GetLicenseRequestBuilder(client().admin().cluster(), GetLicenseAction.INSTANCE).get();
|
||||
assertThat(getLicenseResponse.licenses(), notNullValue());
|
||||
assertThat(getLicenseResponse.licenses().size(), equalTo(2));
|
||||
|
||||
// check license (should get the longest expiry time for all unique features)
|
||||
TestUtils.isSame(Arrays.asList(marvelLicense, longerSignedLicense), getLicenseResponse.licenses());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testRemoveLicenseSimple() throws Exception {
|
||||
License shieldLicense = generateSignedLicense("shield", TimeValue.timeValueMinutes(2));
|
||||
License marvelLicense = generateSignedLicense("marvel", TimeValue.timeValueMinutes(5));
|
||||
List<License> actualLicenses = Arrays.asList(marvelLicense, shieldLicense);
|
||||
|
||||
// put two licenses
|
||||
PutLicenseRequestBuilder putLicenseRequestBuilder = new PutLicenseRequestBuilder(client().admin().cluster(), PutLicenseAction.INSTANCE)
|
||||
.setLicense(actualLicenses);
|
||||
PutLicenseResponse putLicenseResponse = putLicenseRequestBuilder.get();
|
||||
assertThat(putLicenseResponse.isAcknowledged(), equalTo(true));
|
||||
assertThat(putLicenseResponse.status(), equalTo(LicensesStatus.VALID));
|
||||
|
||||
// get and check licenses
|
||||
GetLicenseResponse getLicenseResponse = new GetLicenseRequestBuilder(client().admin().cluster(), GetLicenseAction.INSTANCE).get();
|
||||
assertThat(getLicenseResponse.licenses(), notNullValue());
|
||||
assertThat(getLicenseResponse.licenses().size(), equalTo(2));
|
||||
|
||||
// delete all licenses
|
||||
DeleteLicenseRequestBuilder deleteLicenseRequestBuilder = new DeleteLicenseRequestBuilder(client().admin().cluster(), DeleteLicenseAction.INSTANCE)
|
||||
.setFeatures(Sets.newHashSet("shield", "marvel"));
|
||||
DeleteLicenseResponse deleteLicenseResponse = deleteLicenseRequestBuilder.get();
|
||||
assertThat(deleteLicenseResponse.isAcknowledged(), equalTo(true));
|
||||
|
||||
// get licenses (expected no licenses)
|
||||
getLicenseResponse = new GetLicenseRequestBuilder(client().admin().cluster(), GetLicenseAction.INSTANCE).get();
|
||||
assertThat(getLicenseResponse.licenses(), notNullValue());
|
||||
assertThat(getLicenseResponse.licenses().size(), equalTo(0));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testRemoveLicenses() throws Exception {
|
||||
License shieldLicense = generateSignedLicense("shield", TimeValue.timeValueMinutes(2));
|
||||
License marvelLicense = generateSignedLicense("marvel", TimeValue.timeValueMinutes(5));
|
||||
List<License> actualLicenses = Arrays.asList(marvelLicense, shieldLicense);
|
||||
|
||||
// put two licenses
|
||||
PutLicenseRequestBuilder putLicenseRequestBuilder = new PutLicenseRequestBuilder(client().admin().cluster(), PutLicenseAction.INSTANCE)
|
||||
.setLicense(actualLicenses);
|
||||
PutLicenseResponse putLicenseResponse = putLicenseRequestBuilder.get();
|
||||
assertThat(putLicenseResponse.isAcknowledged(), equalTo(true));
|
||||
assertThat(putLicenseResponse.status(), equalTo(LicensesStatus.VALID));
|
||||
|
||||
// delete one license
|
||||
DeleteLicenseRequestBuilder deleteLicenseRequestBuilder = new DeleteLicenseRequestBuilder(client().admin().cluster(), DeleteLicenseAction.INSTANCE)
|
||||
.setFeatures(Sets.newHashSet("shield"));
|
||||
DeleteLicenseResponse deleteLicenseResponse = deleteLicenseRequestBuilder.get();
|
||||
assertThat(deleteLicenseResponse.isAcknowledged(), equalTo(true));
|
||||
|
||||
// check other license
|
||||
GetLicenseResponse getLicenseResponse = new GetLicenseRequestBuilder(client().admin().cluster(), GetLicenseAction.INSTANCE).get();
|
||||
assertThat(getLicenseResponse.licenses(), notNullValue());
|
||||
assertThat(getLicenseResponse.licenses().size(), equalTo(1));
|
||||
|
||||
// delete another license
|
||||
deleteLicenseRequestBuilder = new DeleteLicenseRequestBuilder(client().admin().cluster(), DeleteLicenseAction.INSTANCE)
|
||||
.setFeatures(Sets.newHashSet("marvel"));
|
||||
deleteLicenseResponse = deleteLicenseRequestBuilder.get();
|
||||
assertThat(deleteLicenseResponse.isAcknowledged(), equalTo(true));
|
||||
|
||||
// check no license
|
||||
getLicenseResponse = new GetLicenseRequestBuilder(client().admin().cluster(), GetLicenseAction.INSTANCE).get();
|
||||
assertThat(getLicenseResponse.licenses(), notNullValue());
|
||||
assertThat(getLicenseResponse.licenses().size(), equalTo(0));
|
||||
}
|
||||
}
|
|
@ -1,99 +0,0 @@
|
|||
/*
|
||||
* 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.plugin;
|
||||
|
||||
import org.elasticsearch.common.unit.TimeValue;
|
||||
import org.elasticsearch.common.xcontent.ToXContent;
|
||||
import org.elasticsearch.common.xcontent.XContentBuilder;
|
||||
import org.elasticsearch.common.xcontent.XContentFactory;
|
||||
import org.elasticsearch.common.xcontent.XContentType;
|
||||
import org.elasticsearch.license.core.License;
|
||||
import org.elasticsearch.license.core.Licenses;
|
||||
import org.elasticsearch.license.licensor.LicenseSigner;
|
||||
|
||||
import java.net.URL;
|
||||
import java.util.*;
|
||||
|
||||
import static org.hamcrest.core.IsEqual.equalTo;
|
||||
import static org.junit.Assert.assertThat;
|
||||
|
||||
public class TestUtils {
|
||||
|
||||
public static String getTestPriKeyPath() throws Exception {
|
||||
return getResourcePath("/private.key");
|
||||
}
|
||||
|
||||
public static String getTestPubKeyPath() throws Exception {
|
||||
return getResourcePath("/public.key");
|
||||
}
|
||||
|
||||
public static void isSame(Collection<License> firstLicenses, Collection<License> secondLicenses) {
|
||||
isSame(new HashSet<>(firstLicenses), new HashSet<>(secondLicenses));
|
||||
}
|
||||
|
||||
public static void isSame(Set<License> firstLicenses, Set<License> secondLicenses) {
|
||||
|
||||
// we do the verifyAndBuild to make sure we weed out any expired licenses
|
||||
final Map<String, License> licenses1 = Licenses.reduceAndMap(firstLicenses);
|
||||
final Map<String, License> licenses2 = Licenses.reduceAndMap(secondLicenses);
|
||||
|
||||
// check if the effective licenses have the same feature set
|
||||
assertThat(licenses1.size(), equalTo(licenses2.size()));
|
||||
|
||||
// for every feature license, check if all the attributes are the same
|
||||
for (String featureType : licenses1.keySet()) {
|
||||
License license1 = licenses1.get(featureType);
|
||||
License license2 = licenses2.get(featureType);
|
||||
isSame(license1, license2);
|
||||
}
|
||||
}
|
||||
|
||||
public static void isSame(License license1, License license2) {
|
||||
assertThat(license1.uid(), equalTo(license2.uid()));
|
||||
assertThat(license1.feature(), equalTo(license2.feature()));
|
||||
assertThat(license1.subscriptionType(), equalTo(license2.subscriptionType()));
|
||||
assertThat(license1.type(), equalTo(license2.type()));
|
||||
assertThat(license1.issuedTo(), equalTo(license2.issuedTo()));
|
||||
assertThat(license1.signature(), equalTo(license2.signature()));
|
||||
assertThat(license1.expiryDate(), equalTo(license2.expiryDate()));
|
||||
assertThat(license1.issueDate(), equalTo(license2.issueDate()));
|
||||
assertThat(license1.maxNodes(), equalTo(license2.maxNodes()));
|
||||
}
|
||||
|
||||
public static String dumpLicense(License license) throws Exception {
|
||||
XContentBuilder builder = XContentFactory.contentBuilder(XContentType.JSON);
|
||||
Licenses.toXContent(Collections.singletonList(license), builder, ToXContent.EMPTY_PARAMS);
|
||||
builder.flush();
|
||||
return builder.string();
|
||||
}
|
||||
|
||||
public static License generateSignedLicense(String feature, TimeValue expiryDuration) throws Exception {
|
||||
return generateSignedLicense(feature, -1, expiryDuration);
|
||||
}
|
||||
|
||||
public static License generateSignedLicense(String feature, long issueDate, TimeValue expiryDuration) throws Exception {
|
||||
long issue = (issueDate != -1l) ? issueDate : System.currentTimeMillis();
|
||||
final License licenseSpec = License.builder()
|
||||
.uid(UUID.randomUUID().toString())
|
||||
.feature(feature)
|
||||
.expiryDate(issue + expiryDuration.getMillis())
|
||||
.issueDate(issue)
|
||||
.type("subscription")
|
||||
.subscriptionType("gold")
|
||||
.issuedTo("customer")
|
||||
.issuer("elasticsearch")
|
||||
.maxNodes(5)
|
||||
.build();
|
||||
|
||||
LicenseSigner signer = new LicenseSigner(getTestPriKeyPath(), getTestPubKeyPath());
|
||||
return signer.sign(licenseSpec);
|
||||
}
|
||||
|
||||
private static String getResourcePath(String resource) throws Exception {
|
||||
URL url = TestUtils.class.getResource(resource);
|
||||
return url.toURI().getPath();
|
||||
}
|
||||
}
|
|
@ -1,38 +0,0 @@
|
|||
/*
|
||||
* 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.plugin;
|
||||
|
||||
import org.elasticsearch.common.unit.TimeValue;
|
||||
import org.elasticsearch.license.core.License;
|
||||
import org.elasticsearch.license.plugin.core.TrialLicenseUtils;
|
||||
import org.elasticsearch.test.ElasticsearchTestCase;
|
||||
import org.junit.Test;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
|
||||
|
||||
public class TrailLicenseSerializationTests extends ElasticsearchTestCase {
|
||||
|
||||
@Test
|
||||
public void testSerialization() throws Exception {
|
||||
final TrialLicenseUtils.TrialLicenseBuilder trialLicenseBuilder = TrialLicenseUtils.builder()
|
||||
.duration(TimeValue.timeValueHours(2))
|
||||
.maxNodes(5)
|
||||
.issuedTo("customer")
|
||||
.issueDate(System.currentTimeMillis());
|
||||
int n = randomIntBetween(2, 5);
|
||||
List<License> trialLicenses = new ArrayList<>(n);
|
||||
for (int i = 0; i < n; i++) {
|
||||
trialLicenses.add(trialLicenseBuilder.feature("feature__" + String.valueOf(i)).build());
|
||||
}
|
||||
for (License trialLicense : trialLicenses) {
|
||||
String encodedTrialLicense = TrialLicenseUtils.toEncodedTrialLicense(trialLicense);
|
||||
final License fromEncodedTrialLicense = TrialLicenseUtils.fromEncodedTrialLicense(encodedTrialLicense);
|
||||
TestUtils.isSame(fromEncodedTrialLicense, trialLicense);
|
||||
}
|
||||
}
|
||||
}
|
|
@ -1,40 +0,0 @@
|
|||
/*
|
||||
* 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.plugin.consumer;
|
||||
|
||||
import org.elasticsearch.common.inject.Inject;
|
||||
import org.elasticsearch.common.settings.Settings;
|
||||
|
||||
/**
|
||||
* Registers licenses upon the start of the service lifecycle
|
||||
* see {@link EagerLicenseRegistrationPluginService}
|
||||
* <p/>
|
||||
* License registration might happen before clusterService start()
|
||||
*/
|
||||
public class EagerLicenseRegistrationConsumerPlugin extends TestConsumerPluginBase {
|
||||
|
||||
public final static String NAME = "test_consumer_plugin_1";
|
||||
|
||||
@Inject
|
||||
public EagerLicenseRegistrationConsumerPlugin(Settings settings) {
|
||||
super(settings);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Class<? extends TestPluginServiceBase> service() {
|
||||
return EagerLicenseRegistrationPluginService.class;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected String pluginName() {
|
||||
return NAME;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String featureName() {
|
||||
return EagerLicenseRegistrationPluginService.FEATURE_NAME;
|
||||
}
|
||||
}
|
|
@ -1,32 +0,0 @@
|
|||
/*
|
||||
* 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.plugin.consumer;
|
||||
|
||||
import org.elasticsearch.common.inject.Inject;
|
||||
import org.elasticsearch.common.inject.Singleton;
|
||||
import org.elasticsearch.common.settings.Settings;
|
||||
import org.elasticsearch.license.plugin.core.LicensesClientService;
|
||||
|
||||
@Singleton
|
||||
public class EagerLicenseRegistrationPluginService extends TestPluginServiceBase {
|
||||
|
||||
public static String FEATURE_NAME = "feature1";
|
||||
|
||||
@Inject
|
||||
public EagerLicenseRegistrationPluginService(Settings settings, LicensesClientService licensesClientService) {
|
||||
super(true, settings, licensesClientService, null);
|
||||
}
|
||||
|
||||
@Override
|
||||
public String featureName() {
|
||||
return FEATURE_NAME;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String settingPrefix() {
|
||||
return EagerLicenseRegistrationConsumerPlugin.NAME;
|
||||
}
|
||||
}
|
|
@ -1,40 +0,0 @@
|
|||
/*
|
||||
* 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.plugin.consumer;
|
||||
|
||||
import org.elasticsearch.common.inject.Inject;
|
||||
import org.elasticsearch.common.settings.Settings;
|
||||
|
||||
/**
|
||||
* Registers licenses only after cluster has recovered
|
||||
* see {@link LazyLicenseRegistrationPluginService}
|
||||
* <p/>
|
||||
* License registration happens after clusterservice start()
|
||||
*/
|
||||
public class LazyLicenseRegistrationConsumerPlugin extends TestConsumerPluginBase {
|
||||
|
||||
public static String NAME = "test_consumer_plugin_2";
|
||||
|
||||
@Inject
|
||||
public LazyLicenseRegistrationConsumerPlugin(Settings settings) {
|
||||
super(settings);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Class<? extends TestPluginServiceBase> service() {
|
||||
return LazyLicenseRegistrationPluginService.class;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected String pluginName() {
|
||||
return NAME;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String featureName() {
|
||||
return LazyLicenseRegistrationPluginService.FEATURE_NAME;
|
||||
}
|
||||
}
|
|
@ -1,34 +0,0 @@
|
|||
/*
|
||||
* 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.plugin.consumer;
|
||||
|
||||
import org.elasticsearch.cluster.ClusterService;
|
||||
import org.elasticsearch.common.inject.Inject;
|
||||
import org.elasticsearch.common.inject.Singleton;
|
||||
import org.elasticsearch.common.settings.Settings;
|
||||
import org.elasticsearch.license.plugin.core.LicensesClientService;
|
||||
|
||||
@Singleton
|
||||
public class LazyLicenseRegistrationPluginService extends TestPluginServiceBase {
|
||||
|
||||
|
||||
public static String FEATURE_NAME = "feature2";
|
||||
|
||||
@Inject
|
||||
public LazyLicenseRegistrationPluginService(Settings settings, LicensesClientService licensesClientService, ClusterService clusterService) {
|
||||
super(false, settings, licensesClientService, clusterService);
|
||||
}
|
||||
|
||||
@Override
|
||||
public String featureName() {
|
||||
return FEATURE_NAME;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String settingPrefix() {
|
||||
return LazyLicenseRegistrationConsumerPlugin.NAME;
|
||||
}
|
||||
}
|
|
@ -1,55 +0,0 @@
|
|||
/*
|
||||
* 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.plugin.consumer;
|
||||
|
||||
import com.google.common.collect.Lists;
|
||||
import org.elasticsearch.client.Client;
|
||||
import org.elasticsearch.cluster.node.DiscoveryNode;
|
||||
import org.elasticsearch.common.component.LifecycleComponent;
|
||||
import org.elasticsearch.common.settings.Settings;
|
||||
import org.elasticsearch.plugins.AbstractPlugin;
|
||||
|
||||
import java.util.Collection;
|
||||
|
||||
public abstract class TestConsumerPluginBase extends AbstractPlugin {
|
||||
|
||||
private final boolean isEnabled;
|
||||
|
||||
public TestConsumerPluginBase(Settings settings) {
|
||||
if (DiscoveryNode.clientNode(settings)) {
|
||||
// Enable plugin only on node clients
|
||||
this.isEnabled = "node".equals(settings.get(Client.CLIENT_TYPE_SETTING));
|
||||
} else {
|
||||
this.isEnabled = true;
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public String name() {
|
||||
return pluginName();
|
||||
}
|
||||
|
||||
@Override
|
||||
public String description() {
|
||||
return "test licensing consumer plugin";
|
||||
}
|
||||
|
||||
|
||||
@Override
|
||||
public Collection<Class<? extends LifecycleComponent>> services() {
|
||||
Collection<Class<? extends LifecycleComponent>> services = Lists.newArrayList();
|
||||
if (isEnabled) {
|
||||
services.add(service());
|
||||
}
|
||||
return services;
|
||||
}
|
||||
|
||||
public abstract Class<? extends TestPluginServiceBase> service();
|
||||
|
||||
protected abstract String pluginName();
|
||||
|
||||
public abstract String featureName();
|
||||
}
|
|
@ -1,147 +0,0 @@
|
|||
/*
|
||||
* 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.plugin.consumer;
|
||||
|
||||
import org.elasticsearch.ElasticsearchException;
|
||||
import org.elasticsearch.cluster.ClusterChangedEvent;
|
||||
import org.elasticsearch.cluster.ClusterService;
|
||||
import org.elasticsearch.cluster.ClusterStateListener;
|
||||
import org.elasticsearch.common.component.AbstractLifecycleComponent;
|
||||
import org.elasticsearch.common.settings.Settings;
|
||||
import org.elasticsearch.common.unit.TimeValue;
|
||||
import org.elasticsearch.gateway.GatewayService;
|
||||
import org.elasticsearch.license.core.License;
|
||||
import org.elasticsearch.license.plugin.core.LicensesClientService;
|
||||
import org.elasticsearch.license.plugin.core.LicensesService;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.Collection;
|
||||
import java.util.Collections;
|
||||
import java.util.concurrent.atomic.AtomicBoolean;
|
||||
|
||||
public abstract class TestPluginServiceBase extends AbstractLifecycleComponent<TestPluginServiceBase> implements ClusterStateListener {
|
||||
|
||||
private LicensesClientService licensesClientService;
|
||||
|
||||
private final ClusterService clusterService;
|
||||
// specify the trial license spec for the feature
|
||||
// example: 30 day trial on 1000 nodes
|
||||
final LicensesService.TrialLicenseOptions trialLicenseOptions;
|
||||
|
||||
final boolean eagerLicenseRegistration;
|
||||
|
||||
final static Collection<LicensesService.ExpirationCallback> expirationCallbacks;
|
||||
|
||||
static {
|
||||
// Callback triggered every 24 hours from 30 days to 7 days of license expiry
|
||||
final LicensesService.ExpirationCallback.Pre LEVEL_1 = new LicensesService.ExpirationCallback.Pre(TimeValue.timeValueHours(7 * 24), TimeValue.timeValueHours(30 * 24), TimeValue.timeValueHours(24)) {
|
||||
@Override
|
||||
public void on(License license, LicensesService.ExpirationStatus status) {
|
||||
}
|
||||
};
|
||||
|
||||
// Callback triggered every 10 minutes from 7 days to license expiry
|
||||
final LicensesService.ExpirationCallback.Pre LEVEL_2 = new LicensesService.ExpirationCallback.Pre(null, TimeValue.timeValueHours(7 * 24), TimeValue.timeValueMinutes(10)) {
|
||||
@Override
|
||||
public void on(License license, LicensesService.ExpirationStatus status) {
|
||||
}
|
||||
};
|
||||
|
||||
// Callback triggered every 10 minutes after license expiry
|
||||
final LicensesService.ExpirationCallback.Post LEVEL_3 = new LicensesService.ExpirationCallback.Post(TimeValue.timeValueMillis(0), null, TimeValue.timeValueMinutes(10)) {
|
||||
@Override
|
||||
public void on(License license, LicensesService.ExpirationStatus status) {
|
||||
}
|
||||
};
|
||||
|
||||
expirationCallbacks = new ArrayList<>();
|
||||
expirationCallbacks.add(LEVEL_1);
|
||||
expirationCallbacks.add(LEVEL_2);
|
||||
expirationCallbacks.add(LEVEL_3);
|
||||
}
|
||||
|
||||
|
||||
public final AtomicBoolean registered = new AtomicBoolean(false);
|
||||
|
||||
private volatile AtomicBoolean enabled = new AtomicBoolean(false);
|
||||
|
||||
public TestPluginServiceBase(boolean eagerLicenseRegistration, Settings settings, LicensesClientService licensesClientService, ClusterService clusterService) {
|
||||
super(settings);
|
||||
this.eagerLicenseRegistration = eagerLicenseRegistration;
|
||||
this.licensesClientService = licensesClientService;
|
||||
int durationInSec = settings.getAsInt(settingPrefix() + ".trial_license_duration_in_seconds", -1);
|
||||
if (durationInSec == -1) {
|
||||
this.trialLicenseOptions = null;
|
||||
} else {
|
||||
this.trialLicenseOptions = new LicensesService.TrialLicenseOptions(TimeValue.timeValueSeconds(durationInSec), 1000);
|
||||
}
|
||||
if (!eagerLicenseRegistration) {
|
||||
this.clusterService = clusterService;
|
||||
clusterService.add(this);
|
||||
} else {
|
||||
this.clusterService = null;
|
||||
}
|
||||
}
|
||||
|
||||
// should be the same string used by the license Manger to generate
|
||||
// signed license
|
||||
public abstract String featureName();
|
||||
|
||||
public abstract String settingPrefix();
|
||||
|
||||
// check if feature is enabled
|
||||
public boolean enabled() {
|
||||
return enabled.get();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void clusterChanged(ClusterChangedEvent event) {
|
||||
if (!eagerLicenseRegistration && !event.state().blocks().hasGlobalBlock(GatewayService.STATE_NOT_RECOVERED_BLOCK)) {
|
||||
if (registered.compareAndSet(false, true)) {
|
||||
logger.info("Registering to licensesService [lazy]");
|
||||
licensesClientService.register(featureName(),
|
||||
trialLicenseOptions, expirationCallbacks,
|
||||
new LicensingClientListener());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
protected void doStart() throws ElasticsearchException {
|
||||
if (eagerLicenseRegistration) {
|
||||
if (registered.compareAndSet(false, true)) {
|
||||
logger.info("Registering to licensesService [eager]");
|
||||
licensesClientService.register(featureName(),
|
||||
trialLicenseOptions, expirationCallbacks,
|
||||
new LicensingClientListener());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void doStop() throws ElasticsearchException {
|
||||
if (clusterService != null) {
|
||||
clusterService.remove(this);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void doClose() throws ElasticsearchException {
|
||||
}
|
||||
|
||||
private class LicensingClientListener implements LicensesClientService.Listener {
|
||||
|
||||
@Override
|
||||
public void onEnabled(License license) {
|
||||
enabled.set(true);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onDisabled(License license) {
|
||||
enabled.set(false);
|
||||
}
|
||||
|
||||
}
|
||||
}
|
|
@ -1,27 +0,0 @@
|
|||
/*
|
||||
* 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.plugin.rest;
|
||||
|
||||
|
||||
import com.carrotsearch.randomizedtesting.annotations.Name;
|
||||
import com.carrotsearch.randomizedtesting.annotations.ParametersFactory;
|
||||
import org.elasticsearch.test.rest.ElasticsearchRestTestCase;
|
||||
import org.elasticsearch.test.rest.RestTestCandidate;
|
||||
import org.elasticsearch.test.rest.parser.RestTestParseException;
|
||||
|
||||
import java.io.IOException;
|
||||
|
||||
public class LicensesRestIT extends ElasticsearchRestTestCase {
|
||||
|
||||
public LicensesRestIT(@Name("yaml") RestTestCandidate testCandidate) {
|
||||
super(testCandidate);
|
||||
}
|
||||
|
||||
@ParametersFactory
|
||||
public static Iterable<Object[]> parameters() throws IOException, RestTestParseException {
|
||||
return ElasticsearchRestTestCase.createParameters(0, 1);
|
||||
}
|
||||
}
|
|
@ -1,11 +0,0 @@
|
|||
es.logger.level=DEBUG
|
||||
log4j.rootLogger=${es.logger.level}, out
|
||||
|
||||
log4j.logger.org.apache.http=INFO, out
|
||||
log4j.additivity.org.apache.http=false
|
||||
|
||||
log4j.logger.org.elasticsearch.license=TRACE
|
||||
|
||||
log4j.appender.out=org.apache.log4j.ConsoleAppender
|
||||
log4j.appender.out.layout=org.apache.log4j.PatternLayout
|
||||
log4j.appender.out.layout.conversionPattern=[%d{ISO8601}][%-5p][%-25c] %m%n
|
Binary file not shown.
|
@ -1,3 +0,0 @@
|
|||
ýŽÇqÝnęÄĚgŠśwM}Ťą‡UiKŠ•0âbÖ2Řşqö]â쇴ŻÖĎĂcĚ+IŇđÔ &IJ†fÉ~ßlj <09>ş]d™}o§OčľId®Č
|
||||
5A(ěµ´^ŘöW©DŤŞJµë}ů-Oîë?u N5ľŰvpŰ{’Ľ˛Áôśát–ť¤7ůřĂę#˛Vqöó»ktwm’Ś]ĎLőŁz"| Q‹lźňQđsâ>ů<}Ź[Á2ÖÓŕZÖ|5‹ŻŤĘĘ7%ŘęD
|
||||
Yĺ‘xn:ĽlúLČćHň˘«Ë2<C38B>źHvEEWÇ\¦H:“6Žh9 [!š…Űć©Š¤+;Ö.w7Cě©_|ŢÓŞĎÁ*ń§D`<60>Ú?‚ůxU/3>xUÓ“+ č
|
82
pom.xml
82
pom.xml
|
@ -1,82 +0,0 @@
|
|||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<project xmlns="http://maven.apache.org/POM/4.0.0"
|
||||
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
|
||||
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
|
||||
<modelVersion>4.0.0</modelVersion>
|
||||
|
||||
<parent>
|
||||
<groupId>org.elasticsearch.plugin</groupId>
|
||||
<artifactId>elasticsearch-plugin</artifactId>
|
||||
<version>2.0.0.beta1-SNAPSHOT</version>
|
||||
</parent>
|
||||
|
||||
<groupId>org.elasticsearch</groupId>
|
||||
<artifactId>elasticsearch-license</artifactId>
|
||||
<packaging>pom</packaging>
|
||||
<version>2.0.0.beta1-SNAPSHOT</version>
|
||||
<modules>
|
||||
<module>core</module>
|
||||
<module>licensor</module>
|
||||
<module>plugin</module>
|
||||
<module>plugin-api</module>
|
||||
</modules>
|
||||
|
||||
<properties>
|
||||
<license.basedir combine.self="override">${project.basedir}</license.basedir>
|
||||
<elasticsearch.license.header>${license.basedir}/dev-tools/license/elasticsearch_license_header.txt</elasticsearch.license.header>
|
||||
<elasticsearch.license.headerDefinition>${license.basedir}/dev-tools/license/license_header_definition.xml</elasticsearch.license.headerDefinition>
|
||||
</properties>
|
||||
|
||||
<repositories>
|
||||
<repository>
|
||||
<id>oss-snapshots</id>
|
||||
<name>Sonatype OSS Snapshots</name>
|
||||
<url>https://oss.sonatype.org/content/repositories/snapshots/</url>
|
||||
</repository>
|
||||
</repositories>
|
||||
|
||||
<profiles>
|
||||
<profile>
|
||||
<id>deploy-internal</id>
|
||||
<distributionManagement>
|
||||
<repository>
|
||||
<id>elasticsearch-internal-releases</id>
|
||||
<name>Elasticsearch Internal Releases</name>
|
||||
<url>http://maven.elasticsearch.org/artifactory/internal-releases</url>
|
||||
</repository>
|
||||
<snapshotRepository>
|
||||
<id>elasticsearch-internal-snapshots</id>
|
||||
<name>Elasticsearch Internal Snapshots</name>
|
||||
<url>http://maven.elasticsearch.org/artifactory/internal-snapshots</url>
|
||||
</snapshotRepository>
|
||||
</distributionManagement>
|
||||
</profile>
|
||||
<profile>
|
||||
<id>deploy-public</id>
|
||||
<distributionManagement>
|
||||
<repository>
|
||||
<id>elasticsearch-public-releases</id>
|
||||
<name>Elasticsearch Public Releases</name>
|
||||
<url>http://maven.elasticsearch.org/artifactory/public-releases</url>
|
||||
</repository>
|
||||
</distributionManagement>
|
||||
</profile>
|
||||
<profile>
|
||||
<id>default</id>
|
||||
<activation>
|
||||
<activeByDefault>true</activeByDefault>
|
||||
</activation>
|
||||
<build>
|
||||
<plugins>
|
||||
<plugin>
|
||||
<groupId>com.carrotsearch.randomizedtesting</groupId>
|
||||
<artifactId>junit4-maven-plugin</artifactId>
|
||||
<configuration>
|
||||
<argLine>${tests.jvm.argline}</argLine>
|
||||
</configuration>
|
||||
</plugin>
|
||||
</plugins>
|
||||
</build>
|
||||
</profile>
|
||||
</profiles>
|
||||
</project>
|
Loading…
Reference in New Issue