Skip to content

Commit a1e80e1

Browse files
committed
Merge branch 'master' into security/redirect-to-login-on-401-xhr-response
Original commit: elastic/x-pack-elasticsearch@4a14381
2 parents 16793ad + 4089ae0 commit a1e80e1

File tree

449 files changed

+9451
-9330
lines changed

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

449 files changed

+9451
-9330
lines changed

elasticsearch/build.gradle

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,5 @@
1+
import org.elasticsearch.gradle.precommit.LicenseHeadersTask
2+
13
File checkstyleSuppressions = file('checkstyle_suppressions.xml')
24
subprojects {
35
tasks.withType(Checkstyle) {
@@ -7,4 +9,9 @@ subprojects {
79
suppressions: checkstyleSuppressions
810
]
911
}
12+
13+
tasks.withType(LicenseHeadersTask.class) {
14+
approvedLicenses = ['Elasticsearch Confidential']
15+
additionalLicense 'ESCON', 'Elasticsearch Confidential', 'ELASTICSEARCH CONFIDENTIAL'
16+
}
1017
}

elasticsearch/license/base/src/main/java/org/elasticsearch/license/core/License.java

Lines changed: 32 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -101,6 +101,7 @@ public static OperationMode resolve(String type) {
101101
case "gold":
102102
return GOLD;
103103
case "platinum":
104+
case "cloud_internal":
104105
case "internal": // bwc for 1.x subscription_type field
105106
return PLATINUM;
106107
default:
@@ -196,12 +197,42 @@ public String signature() {
196197
}
197198

198199
/**
199-
* @return the operation mode of the license as computed from the license type
200+
* @return the operation mode of the license as computed from the license type or from
201+
* the license mode file
200202
*/
201203
public OperationMode operationMode() {
204+
synchronized (this) {
205+
if (canReadOperationModeFromFile() && operationModeFileWatcher != null) {
206+
return operationModeFileWatcher.getCurrentOperationMode();
207+
}
208+
}
202209
return operationMode;
203210
}
204211

212+
private boolean canReadOperationModeFromFile() {
213+
return type.equals("cloud_internal");
214+
}
215+
216+
private volatile OperationModeFileWatcher operationModeFileWatcher;
217+
218+
/**
219+
* Sets the operation mode file watcher for the license and initializes the
220+
* file watcher when the license type allows to override operation mode from file
221+
*/
222+
public synchronized void setOperationModeFileWatcher(final OperationModeFileWatcher operationModeFileWatcher) {
223+
this.operationModeFileWatcher = operationModeFileWatcher;
224+
if (canReadOperationModeFromFile()) {
225+
this.operationModeFileWatcher.init();
226+
}
227+
}
228+
229+
/**
230+
* Removes operation mode file watcher, so unused license objects can be gc'ed
231+
*/
232+
public synchronized void removeOperationModeFileWatcher() {
233+
this.operationModeFileWatcher = null;
234+
}
235+
205236
/**
206237
* @return the current license's status
207238
*/
Lines changed: 113 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,113 @@
1+
/*
2+
* Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
3+
* or more contributor license agreements. Licensed under the Elastic License;
4+
* you may not use this file except in compliance with the Elastic License.
5+
*/
6+
package org.elasticsearch.license.core;
7+
8+
9+
import org.elasticsearch.common.logging.ESLogger;
10+
import org.elasticsearch.license.core.License.OperationMode;
11+
import org.elasticsearch.watcher.FileChangesListener;
12+
import org.elasticsearch.watcher.FileWatcher;
13+
import org.elasticsearch.watcher.ResourceWatcherService;
14+
15+
import java.io.IOException;
16+
import java.nio.charset.StandardCharsets;
17+
import java.nio.file.Files;
18+
import java.nio.file.Path;
19+
import java.util.concurrent.atomic.AtomicBoolean;
20+
21+
/**
22+
* File based watcher for license {@link OperationMode}
23+
* Watches for changes in <code>licenseModePath</code>, use
24+
* {@link #getCurrentOperationMode()} to access the latest mode
25+
*
26+
* In case of failure to read a valid operation mode from <code>licenseModePath</code>,
27+
* the operation mode will default to PLATINUM
28+
*/
29+
public final class OperationModeFileWatcher extends FileChangesListener {
30+
private final ResourceWatcherService resourceWatcherService;
31+
private final Path licenseModePath;
32+
private final AtomicBoolean initialized = new AtomicBoolean();
33+
private final OperationMode defaultOperationMode = OperationMode.PLATINUM;
34+
private volatile OperationMode currentOperationMode = defaultOperationMode;
35+
private final ESLogger logger;
36+
private final Runnable onChange;
37+
38+
public OperationModeFileWatcher(ResourceWatcherService resourceWatcherService, Path licenseModePath,
39+
ESLogger logger, Runnable onChange) {
40+
this.resourceWatcherService = resourceWatcherService;
41+
this.licenseModePath = licenseModePath;
42+
this.logger = logger;
43+
this.onChange = onChange;
44+
}
45+
46+
public void init() {
47+
if (initialized.compareAndSet(false, true)) {
48+
final FileWatcher watcher = new FileWatcher(licenseModePath);
49+
watcher.addListener(this);
50+
try {
51+
resourceWatcherService.add(watcher, ResourceWatcherService.Frequency.HIGH);
52+
if (Files.exists(licenseModePath)) {
53+
onChange(licenseModePath);
54+
}
55+
} catch (IOException e) {
56+
logger.error("couldn't initialize watching license mode file", e);
57+
}
58+
}
59+
}
60+
61+
/**
62+
* Returns the current operation mode based on license mode file.
63+
* Defaults to {@link OperationMode#PLATINUM}
64+
*/
65+
public OperationMode getCurrentOperationMode() {
66+
return currentOperationMode;
67+
}
68+
69+
@Override
70+
public void onFileInit(Path file) {
71+
onChange(file);
72+
}
73+
74+
@Override
75+
public void onFileCreated(Path file) {
76+
onChange(file);
77+
}
78+
79+
@Override
80+
public void onFileDeleted(Path file) {
81+
onChange(file);
82+
}
83+
84+
@Override
85+
public void onFileChanged(Path file) {
86+
onChange(file);
87+
}
88+
89+
private synchronized void onChange(Path file) {
90+
if (file.equals(licenseModePath)) {
91+
currentOperationMode = defaultOperationMode;
92+
if (Files.exists(licenseModePath)
93+
&& Files.isReadable(licenseModePath)) {
94+
final byte[] content;
95+
try {
96+
content = Files.readAllBytes(licenseModePath);
97+
} catch (IOException e) {
98+
logger.error("couldn't read operation mode from [{}]", e, licenseModePath.toAbsolutePath().toString());
99+
return;
100+
}
101+
String operationMode = new String(content, StandardCharsets.UTF_8);
102+
try {
103+
currentOperationMode = OperationMode.resolve(operationMode);
104+
} catch (IllegalArgumentException e) {
105+
logger.error("invalid operation mode in [{}]", e, licenseModePath.toAbsolutePath().toString());
106+
return;
107+
}
108+
}
109+
onChange.run();
110+
}
111+
}
112+
}
113+
Lines changed: 73 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,73 @@
1+
/*
2+
* Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
3+
* or more contributor license agreements. Licensed under the Elastic License;
4+
* you may not use this file except in compliance with the Elastic License.
5+
*/
6+
package org.elasticsearch.license.core;
7+
8+
import org.elasticsearch.test.ESTestCase;
9+
import org.elasticsearch.watcher.FileWatcher;
10+
import org.elasticsearch.watcher.ResourceWatcherService;
11+
import org.junit.Before;
12+
13+
import java.nio.file.Path;
14+
15+
import static org.elasticsearch.license.core.OperationModeFileWatcherTests.writeMode;
16+
import static org.hamcrest.Matchers.equalTo;
17+
import static org.mockito.Matchers.any;
18+
import static org.mockito.Matchers.eq;
19+
import static org.mockito.Mockito.mock;
20+
import static org.mockito.Mockito.times;
21+
import static org.mockito.Mockito.verify;
22+
import static org.mockito.Mockito.verifyZeroInteractions;
23+
24+
public class LicenseOperationModeUpdateTests extends ESTestCase {
25+
26+
private OperationModeFileWatcher operationModeFileWatcher;
27+
private Path licenseModeFile;
28+
private ResourceWatcherService resourceWatcherService;
29+
30+
@Before
31+
public void init() throws Exception {
32+
licenseModeFile = createTempFile();
33+
resourceWatcherService = mock(ResourceWatcherService.class);
34+
operationModeFileWatcher = new OperationModeFileWatcher(resourceWatcherService, licenseModeFile, logger, () -> {});
35+
}
36+
37+
public void testLicenseOperationModeUpdate() throws Exception {
38+
String type = randomFrom("trial", "basic", "standard", "gold", "platinum");
39+
License license = License.builder()
40+
.uid("id")
41+
.expiryDate(0)
42+
.issueDate(0)
43+
.issuedTo("elasticsearch")
44+
.issuer("issuer")
45+
.type(type)
46+
.maxNodes(1)
47+
.build();
48+
49+
assertThat(license.operationMode(), equalTo(License.OperationMode.resolve(type)));
50+
writeMode("gold", licenseModeFile);
51+
license.setOperationModeFileWatcher(operationModeFileWatcher);
52+
verifyZeroInteractions(resourceWatcherService);
53+
assertThat(license.operationMode(), equalTo(License.OperationMode.resolve(type)));
54+
}
55+
56+
public void testCloudInternalLicenseOperationModeUpdate() throws Exception {
57+
License license = License.builder()
58+
.uid("id")
59+
.expiryDate(0)
60+
.issueDate(0)
61+
.issuedTo("elasticsearch")
62+
.issuer("issuer")
63+
.type("cloud_internal")
64+
.maxNodes(1)
65+
.build();
66+
67+
assertThat(license.operationMode(), equalTo(License.OperationMode.PLATINUM));
68+
writeMode("gold", licenseModeFile);
69+
license.setOperationModeFileWatcher(operationModeFileWatcher);
70+
verify(resourceWatcherService, times(1)).add(any(FileWatcher.class), eq(ResourceWatcherService.Frequency.HIGH));
71+
assertThat(license.operationMode(), equalTo(License.OperationMode.GOLD));
72+
}
73+
}
Lines changed: 114 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,114 @@
1+
/*
2+
* Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
3+
* or more contributor license agreements. Licensed under the Elastic License;
4+
* you may not use this file except in compliance with the Elastic License.
5+
*/
6+
package org.elasticsearch.license.core;
7+
8+
import org.elasticsearch.common.settings.Settings;
9+
import org.elasticsearch.test.ESTestCase;
10+
import org.elasticsearch.threadpool.TestThreadPool;
11+
import org.elasticsearch.watcher.ResourceWatcherService;
12+
import org.junit.After;
13+
import org.junit.Before;
14+
15+
import java.io.IOException;
16+
import java.nio.charset.StandardCharsets;
17+
import java.nio.file.Files;
18+
import java.nio.file.Path;
19+
import java.nio.file.StandardOpenOption;
20+
import java.util.concurrent.atomic.AtomicInteger;
21+
22+
import static org.hamcrest.Matchers.equalTo;
23+
24+
public class OperationModeFileWatcherTests extends ESTestCase {
25+
private ResourceWatcherService watcherService;
26+
private TestThreadPool threadPool;
27+
private Path licenseModePath;
28+
private OperationModeFileWatcher operationModeFileWatcher;
29+
private AtomicInteger onChangeCounter;
30+
31+
@Before
32+
public void setup() throws Exception {
33+
threadPool = new TestThreadPool("license mode file watcher tests");
34+
Settings settings = Settings.builder()
35+
.put("resource.reload.interval.high", "10ms")
36+
.build();
37+
watcherService = new ResourceWatcherService(settings,
38+
threadPool);
39+
watcherService.start();
40+
licenseModePath = createTempFile();
41+
onChangeCounter = new AtomicInteger();
42+
operationModeFileWatcher = new OperationModeFileWatcher(watcherService, licenseModePath, logger,
43+
() -> onChangeCounter.incrementAndGet());
44+
}
45+
46+
@After
47+
public void shutdown() throws InterruptedException {
48+
terminate(threadPool);
49+
watcherService.stop();
50+
}
51+
52+
public void testInit() throws Exception {
53+
writeMode("gold");
54+
assertThat(operationModeFileWatcher.getCurrentOperationMode(), equalTo(License.OperationMode.PLATINUM));
55+
operationModeFileWatcher.init();
56+
assertThat(onChangeCounter.get(), equalTo(2));
57+
assertThat(operationModeFileWatcher.getCurrentOperationMode(), equalTo(License.OperationMode.GOLD));
58+
}
59+
60+
public void testUpdateModeFromFile() throws Exception {
61+
Files.delete(licenseModePath);
62+
operationModeFileWatcher.init();
63+
assertThat(operationModeFileWatcher.getCurrentOperationMode(), equalTo(License.OperationMode.PLATINUM));
64+
writeMode("gold");
65+
assertBusy(() -> assertThat(operationModeFileWatcher.getCurrentOperationMode(), equalTo(License.OperationMode.GOLD)));
66+
writeMode("basic");
67+
assertBusy(() -> assertThat(operationModeFileWatcher.getCurrentOperationMode(), equalTo(License.OperationMode.BASIC)));
68+
assertThat(onChangeCounter.get(), equalTo(2));
69+
}
70+
71+
public void testDeleteModeFromFile() throws Exception {
72+
Files.delete(licenseModePath);
73+
operationModeFileWatcher.init();
74+
writeMode("gold");
75+
assertBusy(() -> assertThat(operationModeFileWatcher.getCurrentOperationMode(), equalTo(License.OperationMode.GOLD)));
76+
Files.delete(licenseModePath);
77+
assertBusy(() -> assertThat(operationModeFileWatcher.getCurrentOperationMode(), equalTo(License.OperationMode.PLATINUM)));
78+
assertThat(onChangeCounter.get(), equalTo(2));
79+
}
80+
81+
public void testInvalidModeFromFile() throws Exception {
82+
writeMode("invalid");
83+
operationModeFileWatcher.init();
84+
assertThat(operationModeFileWatcher.getCurrentOperationMode(), equalTo(License.OperationMode.PLATINUM));
85+
operationModeFileWatcher.onFileChanged(licenseModePath);
86+
assertThat(operationModeFileWatcher.getCurrentOperationMode(), equalTo(License.OperationMode.PLATINUM));
87+
}
88+
89+
public void testLicenseModeFileIsDirectory() throws Exception {
90+
licenseModePath = createTempDir();
91+
operationModeFileWatcher.init();
92+
assertThat(operationModeFileWatcher.getCurrentOperationMode(), equalTo(License.OperationMode.PLATINUM));
93+
operationModeFileWatcher.onFileChanged(licenseModePath);
94+
assertThat(operationModeFileWatcher.getCurrentOperationMode(), equalTo(License.OperationMode.PLATINUM));
95+
}
96+
97+
public void testLicenseModeFileCreatedAfterInit() throws Exception {
98+
Files.delete(licenseModePath);
99+
operationModeFileWatcher.init();
100+
assertThat(operationModeFileWatcher.getCurrentOperationMode(), equalTo(License.OperationMode.PLATINUM));
101+
Path tempFile = createTempFile();
102+
writeMode("gold", tempFile);
103+
licenseModePath = tempFile;
104+
assertBusy(() -> assertThat(operationModeFileWatcher.getCurrentOperationMode(), equalTo(License.OperationMode.GOLD)));
105+
}
106+
107+
private void writeMode(String mode) throws IOException {
108+
writeMode(mode, licenseModePath);
109+
}
110+
111+
static void writeMode(String mode, Path file) throws IOException {
112+
Files.write(file, mode.getBytes(StandardCharsets.UTF_8), StandardOpenOption.CREATE);
113+
}
114+
}

elasticsearch/license/build.gradle

Lines changed: 0 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,5 @@ subprojects {
33
project.forbiddenPatterns {
44
exclude '**/*.key'
55
}
6-
// someone figure out what the x-plugins logic should be
7-
project.licenseHeaders.enabled = false
86
}
97
}

elasticsearch/qa/audit-tests/build.gradle

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -14,7 +14,7 @@ project.sourceSets.test.output.dir(outputDir, builtBy: copyXPackPluginProps)
1414

1515
integTest {
1616
cluster {
17-
plugin 'x-pack', project(':x-plugins:elasticsearch:x-pack')
17+
plugin ':x-plugins:elasticsearch:x-pack'
1818
setting 'xpack.security.audit.enabled', 'true'
1919
setting 'xpack.security.audit.outputs', 'index'
2020
setting 'logger.level', 'DEBUG'

0 commit comments

Comments
 (0)