Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
@@ -0,0 +1,40 @@
/*
* Licensed to Elasticsearch under one or more contributor
* license agreements. See the NOTICE file distributed with
* this work for additional information regarding copyright
* ownership. Elasticsearch licenses this file to you under
* the Apache License, Version 2.0 (the "License"); you may
* not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing,
* software distributed under the License is distributed on an
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
* KIND, either express or implied. See the License for the
* specific language governing permissions and limitations
* under the License.
*/

package org.elasticsearch.common;

import java.util.function.Supplier;

public class MemoizedSupplier<T> implements Supplier<T> {
private Supplier<T> supplier;
private T value;

public MemoizedSupplier(Supplier<T> supplier) {
this.supplier = supplier;
}

@Override
public T get() {
if (supplier != null) {
value = supplier.get();
supplier = null;
}
return value;
}
}
22 changes: 18 additions & 4 deletions x-pack/plugin/core/build.gradle
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@ import java.nio.file.Paths
apply plugin: 'elasticsearch.esplugin'
apply plugin: 'elasticsearch.publish'
apply plugin: 'elasticsearch.internal-cluster-test'
apply plugin: 'elasticsearch.yaml-rest-test'

archivesBaseName = 'x-pack-core'

Expand Down Expand Up @@ -57,6 +58,8 @@ dependencies {
transitive = false
}

yamlRestTestImplementation project(':x-pack:plugin:core')

}

ext.expansions = [
Expand Down Expand Up @@ -143,7 +146,18 @@ thirdPartyAudit.ignoreMissingClasses(
'javax.servlet.ServletContextListener'
)

// xpack modules are installed in real clusters as the meta plugin, so
// installing them as individual plugins for integ tests doesn't make sense,
// so we disable integ tests
integTest.enabled = false
restResources {
restApi {
includeCore '*'
}
}

testClusters.yamlRestTest {
testDistribution = 'default'
setting 'xpack.security.enabled', 'true'
setting 'xpack.license.self_generated.type', 'trial'
keystore 'bootstrap.password', 'x-pack-test-password'
user username: "x_pack_rest_user", password: "x-pack-test-password"
}

testingConventions.enabled = false
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
/*
* Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
* or more contributor license agreements. Licensed under the Elastic License;
* you may not use this file except in compliance with the Elastic License.
*/

package org.elasticsearch.license;

import org.elasticsearch.action.ActionRequest;
import org.elasticsearch.action.ActionRequestValidationException;
import org.elasticsearch.common.io.stream.StreamInput;

import java.io.IOException;

public class GetFeatureUsageRequest extends ActionRequest {

public GetFeatureUsageRequest() {}

public GetFeatureUsageRequest(StreamInput in) throws IOException {
super(in);
}

@Override
public ActionRequestValidationException validate() {
return null;
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,84 @@
/*
* Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
* or more contributor license agreements. Licensed under the Elastic License;
* you may not use this file except in compliance with the Elastic License.
*/

package org.elasticsearch.license;

import org.elasticsearch.action.ActionResponse;
import org.elasticsearch.common.io.stream.StreamInput;
import org.elasticsearch.common.io.stream.StreamOutput;
import org.elasticsearch.common.io.stream.Writeable;
import org.elasticsearch.common.xcontent.ToXContentObject;
import org.elasticsearch.common.xcontent.XContentBuilder;

import java.io.IOException;
import java.time.Instant;
import java.time.ZoneOffset;
import java.time.ZonedDateTime;
import java.util.Collections;
import java.util.List;

public class GetFeatureUsageResponse extends ActionResponse implements ToXContentObject {

public static class FeatureUsageInfo implements Writeable {
public final String name;
public final ZonedDateTime lastUsedTime;
public final String licenseLevel;

public FeatureUsageInfo(String name, ZonedDateTime lastUsedTime, String licenseLevel) {
this.name = name;
this.lastUsedTime = lastUsedTime;
this.licenseLevel = licenseLevel;
}

public FeatureUsageInfo(StreamInput in) throws IOException {
this.name = in.readString();
this.lastUsedTime = ZonedDateTime.ofInstant(Instant.ofEpochSecond(in.readLong()), ZoneOffset.UTC);
this.licenseLevel = in.readString();
}

@Override
public void writeTo(StreamOutput out) throws IOException {
out.writeString(name);
out.writeLong(lastUsedTime.toEpochSecond());
out.writeString(licenseLevel);
}
}

private List<FeatureUsageInfo> features;

public GetFeatureUsageResponse(List<FeatureUsageInfo> features) {
this.features = Collections.unmodifiableList(features);
}

public GetFeatureUsageResponse(StreamInput in) throws IOException {
this.features = in.readList(FeatureUsageInfo::new);
}

public List<FeatureUsageInfo> getFeatures() {
return features;
}

@Override
public void writeTo(StreamOutput out) throws IOException {
out.writeList(features);
}

@Override
public XContentBuilder toXContent(XContentBuilder builder, Params params) throws IOException {
builder.startObject();
builder.startArray("features");
for (FeatureUsageInfo feature : features) {
builder.startObject();
builder.field("name", feature.name);
builder.field("last_used", feature.lastUsedTime.toString());
builder.field("license_level", feature.licenseLevel);
builder.endObject();
}
builder.endArray();
builder.endObject();
return builder;
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -66,7 +66,8 @@ public Licensing(Settings settings) {
new ActionHandler<>(PostStartTrialAction.INSTANCE, TransportPostStartTrialAction.class),
new ActionHandler<>(GetTrialStatusAction.INSTANCE, TransportGetTrialStatusAction.class),
new ActionHandler<>(PostStartBasicAction.INSTANCE, TransportPostStartBasicAction.class),
new ActionHandler<>(GetBasicStatusAction.INSTANCE, TransportGetBasicStatusAction.class));
new ActionHandler<>(GetBasicStatusAction.INSTANCE, TransportGetBasicStatusAction.class),
new ActionHandler<>(TransportGetFeatureUsageAction.TYPE, TransportGetFeatureUsageAction.class));
}

@Override
Expand All @@ -81,6 +82,7 @@ public List<RestHandler> getRestHandlers(Settings settings, RestController restC
handlers.add(new RestGetBasicStatus());
handlers.add(new RestPostStartTrialLicense());
handlers.add(new RestPostStartBasicLicense());
handlers.add(new RestGetFeatureUsageAction());
return handlers;
}

Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,37 @@
/*
* Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
* or more contributor license agreements. Licensed under the Elastic License;
* you may not use this file except in compliance with the Elastic License.
*/

package org.elasticsearch.license;

import org.elasticsearch.client.node.NodeClient;
import org.elasticsearch.rest.BaseRestHandler;
import org.elasticsearch.rest.RestRequest;
import org.elasticsearch.rest.action.RestToXContentListener;

import java.io.IOException;
import java.util.Collections;
import java.util.List;

import static org.elasticsearch.rest.RestRequest.Method.GET;

public class RestGetFeatureUsageAction extends BaseRestHandler {

@Override
public String getName() {
return "get_feature_usage";
}

@Override
public List<Route> routes() {
return Collections.singletonList(new Route(GET, "/_license/feature_usage"));
}

@Override
protected RestChannelConsumer prepareRequest(RestRequest request, NodeClient client) throws IOException {
return channel -> client.execute(TransportGetFeatureUsageAction.TYPE, new GetFeatureUsageRequest(),
new RestToXContentListener<>(channel));
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,53 @@
/*
* Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
* or more contributor license agreements. Licensed under the Elastic License;
* you may not use this file except in compliance with the Elastic License.
*/

package org.elasticsearch.license;

import org.elasticsearch.action.ActionListener;
import org.elasticsearch.action.ActionType;
import org.elasticsearch.action.support.ActionFilters;
import org.elasticsearch.action.support.HandledTransportAction;
import org.elasticsearch.common.inject.Inject;
import org.elasticsearch.tasks.Task;
import org.elasticsearch.transport.TransportService;

import java.time.Instant;
import java.time.ZoneOffset;
import java.time.ZonedDateTime;
import java.util.ArrayList;
import java.util.List;
import java.util.Locale;
import java.util.Map;

public class TransportGetFeatureUsageAction extends HandledTransportAction<GetFeatureUsageRequest, GetFeatureUsageResponse> {

public static final ActionType<GetFeatureUsageResponse> TYPE =
new ActionType<>("cluster:admin/xpack/license/feature_usage", GetFeatureUsageResponse::new);

private final XPackLicenseState licenseState;

@Inject
public TransportGetFeatureUsageAction(TransportService transportService, ActionFilters actionFilters,
XPackLicenseState licenseState) {
super(TYPE.name(), transportService, actionFilters, GetFeatureUsageRequest::new);
this.licenseState = licenseState;
}


@Override
protected void doExecute(Task task, GetFeatureUsageRequest request, ActionListener<GetFeatureUsageResponse> listener) {
Map<XPackLicenseState.Feature, Long> featureUsage = licenseState.getLastUsed();
List<GetFeatureUsageResponse.FeatureUsageInfo> usageInfos = new ArrayList<>();
for (Map.Entry<XPackLicenseState.Feature, Long> entry : featureUsage.entrySet()) {
XPackLicenseState.Feature feature = entry.getKey();
String name = feature.name().toLowerCase(Locale.ROOT);
ZonedDateTime lastUsedTime = Instant.ofEpochMilli(entry.getValue()).atZone(ZoneOffset.UTC);
String licenseLevel = feature.minimumOperationMode.name().toLowerCase(Locale.ROOT);
usageInfos.add(new GetFeatureUsageResponse.FeatureUsageInfo(name, lastUsedTime, licenseLevel));
}
listener.onResponse(new GetFeatureUsageResponse(usageInfos));
}
}
Loading