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
Expand Up @@ -22,18 +22,21 @@
import eu.cloudnetservice.driver.document.property.DocProperty;
import io.leangen.geantyref.TypeFactory;
import java.util.Map;
import lombok.EqualsAndHashCode;
import java.util.Objects;
import lombok.NonNull;
import org.jetbrains.annotations.Nullable;

/**
* An inclusion which can be added to a service and will download a file from the specified url to the given
* destination. This can be anything for example a plugin or configuration file.
*
* @since 4.0
*/
@EqualsAndHashCode
public final class ServiceRemoteInclusion implements DefaultedDocPropertyHolder, Cloneable {

public static final String NO_CACHE_STRATEGY = "none";
public static final String KEEP_UNTIL_RESTART_STRATEGY = "until-node-restart";

/**
* A property which can be added to a service inclusion to set the http headers to send when making the download http
* request. All key-value pairs of the given document will be set as headers in the request.
Expand All @@ -44,19 +47,28 @@ public final class ServiceRemoteInclusion implements DefaultedDocPropertyHolder,

private final String url;
private final String destination;
private final String cacheStrategy;
private final Document properties;

/**
* Constructs a new service remote inclusion instance.
*
* @param url the url to download the associated file from.
* @param destination the destination inside the service directory to copy the downloaded file to.
* @param properties the properties of the remote inclusion, these can for example contain the http headers to send.
* @param url the url to download the associated file from.
* @param destination the destination inside the service directory to copy the downloaded file to.
* @param cacheStrategy the cache strategy to use when downloading files from the remote.
* @param properties the properties of the remote inclusion, these can for example contain the http headers to
* send.
* @throws NullPointerException if one of the given parameters is null.
*/
private ServiceRemoteInclusion(@NonNull String url, @NonNull String destination, @NonNull Document properties) {
private ServiceRemoteInclusion(
@NonNull String url,
@NonNull String destination,
@NonNull String cacheStrategy,
@NonNull Document properties
) {
this.url = url;
this.destination = destination;
this.cacheStrategy = cacheStrategy;
this.properties = properties;
}

Expand Down Expand Up @@ -84,6 +96,7 @@ private ServiceRemoteInclusion(@NonNull String url, @NonNull String destination,
return builder()
.url(inclusion.url())
.destination(inclusion.destination())
.cacheStrategy(inclusion.cacheStrategy())
.properties(inclusion.propertyHolder());
}

Expand All @@ -106,6 +119,15 @@ private ServiceRemoteInclusion(@NonNull String url, @NonNull String destination,
return this.destination;
}

/**
* The caching strategy that is used when downloading the inclusion.
*
* @return the caching strategy.
*/
public @NonNull String cacheStrategy() {
return this.cacheStrategy;
}

/**
* {@inheritDoc}
*/
Expand Down Expand Up @@ -134,6 +156,25 @@ private ServiceRemoteInclusion(@NonNull String url, @NonNull String destination,
}
}

/**
* {@inheritDoc}
*/
@Override
public boolean equals(@Nullable Object other) {
if (this == other) {
return true;
}
if (!(other instanceof ServiceRemoteInclusion that)) {
return false;
}
return Objects.equals(this.url, that.url()) && Objects.equals(this.destination, that.destination());
}

@Override
public int hashCode() {
return Objects.hash(this.url, this.destination);
}

/**
* A builder for a service remote inclusion.
*
Expand All @@ -143,6 +184,7 @@ public static class Builder {

protected String url;
protected String destination;
protected String cacheStrategy = ServiceRemoteInclusion.NO_CACHE_STRATEGY;
protected Document properties = Document.emptyDocument();

/**
Expand Down Expand Up @@ -172,6 +214,21 @@ public static class Builder {
return this;
}

/**
* Sets the cache strategy that is used when downloading the inclusion from the remote.
* <p>
* To disable caching use the {@link ServiceRemoteInclusion#NO_CACHE_STRATEGY}, which is also the default of this
* builder.
*
* @param cacheStrategy the caching strategy to use.
* @return the same instance as used to call the method, for chaining.
* @throws NullPointerException if the given cache strategy is null.
*/
public @NonNull Builder cacheStrategy(@NonNull String cacheStrategy) {
this.cacheStrategy = cacheStrategy;
return this;
}

/**
* Sets the properties of the service remote inclusion. The properties can for example be used to set the http
* headers which should get send when making a request to the given download url.
Expand All @@ -189,13 +246,14 @@ public static class Builder {
* Builds a service remote inclusion instance based on this builder.
*
* @return the service remote inclusion.
* @throws NullPointerException if no url or destination was given.
* @throws NullPointerException if no url, destination or cache strategy is given.
*/
public @NonNull ServiceRemoteInclusion build() {
Preconditions.checkNotNull(this.url, "no url given");
Preconditions.checkNotNull(this.destination, "no destination given");
Preconditions.checkNotNull(this.cacheStrategy, "no cacheStrategy given");

return new ServiceRemoteInclusion(this.url, this.destination, this.properties);
return new ServiceRemoteInclusion(this.url, this.destination, this.cacheStrategy, this.properties);
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -76,6 +76,22 @@ public GroupsCommand(@NonNull GroupConfigurationProvider groupProvider) {
return this.groupProvider.groupConfigurations().stream().map(Named::name).toList();
}

@Parser(name = "inclusionCacheStrategy", suggestions = "inclusionCacheStrategy")
public @NonNull String inclusionCacheStrategyParser(@NonNull CommandContext<?> $, @NonNull Queue<String> input) {
var strategy = input.remove();
if (strategy.equals(ServiceRemoteInclusion.NO_CACHE_STRATEGY) ||
strategy.equals(ServiceRemoteInclusion.KEEP_UNTIL_RESTART_STRATEGY)) {
return strategy;
}

throw new ArgumentNotAvailableException(I18n.trans("command-tasks-inclusion-cache-strategy-not-found", strategy));
}

@Suggestions("inclusionCacheStrategy")
public @NonNull List<String> inclusionCacheStrategySuggester(@NonNull CommandContext<?> $, @NonNull String input) {
return List.of(ServiceRemoteInclusion.NO_CACHE_STRATEGY, ServiceRemoteInclusion.KEEP_UNTIL_RESTART_STRATEGY);
}

@CommandMethod("groups delete <name>")
public void deleteGroup(@NonNull CommandSource source, @NonNull @Argument("name") GroupConfiguration configuration) {
this.groupProvider.removeGroupConfiguration(configuration);
Expand Down Expand Up @@ -186,14 +202,22 @@ public void addTemplate(
group.name()));
}

@CommandMethod("groups group <name> add inclusion <url> <path>")
@CommandMethod("groups group <name> add inclusion <url> <path> [cacheStrategy]")
public void addInclusion(
@NonNull CommandSource source,
@NonNull @Argument("name") GroupConfiguration group,
@NonNull @Argument("url") String url,
@NonNull @Argument("path") String path
@NonNull @Argument("path") String path,
@NonNull @Argument(
value = "cacheStrategy",
parserName = "inclusionCacheStrategy",
defaultValue = ServiceRemoteInclusion.NO_CACHE_STRATEGY) String cacheStrategy
) {
var inclusion = ServiceRemoteInclusion.builder().url(url).destination(path).build();
var inclusion = ServiceRemoteInclusion.builder()
.url(url)
.destination(path)
.cacheStrategy(cacheStrategy)
.build();
this.updateGroup(group, builder -> builder.modifyInclusions(inclusions -> inclusions.add(inclusion)));
source.sendMessage(I18n.trans("command-groups-add-collection-property",
"inclusion",
Expand Down Expand Up @@ -344,6 +368,17 @@ public void clearProcessParameters(
group.name()));
}

@CommandMethod("groups group <name> clear inclusions")
public void clearInclusions(
@NonNull CommandSource source,
@NonNull @Argument("name") GroupConfiguration group
) {
this.updateGroup(group, builder -> builder.modifyInclusions(Collection::clear));
source.sendMessage(I18n.trans("command-groups-clear-property",
"inclusions",
group.name()));
}

private void updateGroup(@NonNull GroupConfiguration group, Consumer<GroupConfiguration.Builder> modifier) {
modifier
.andThen(builder -> this.groupProvider.addGroupConfiguration(builder.build()))
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -133,7 +133,7 @@ public static void applyServiceConfigurationDisplay(
messages.add("Includes:");

for (var inclusion : configurationBase.inclusions()) {
messages.add("- " + inclusion.url() + " => " + inclusion.destination());
messages.add("- " + inclusion.url() + ':' + inclusion.cacheStrategy() + " => " + inclusion.destination());
}

messages.add(" ");
Expand Down Expand Up @@ -634,18 +634,22 @@ public void addTemplate(
template);
}

@CommandMethod("tasks task <name> add inclusion <url> <path>")
@CommandMethod("tasks task <name> add inclusion <url> <path> [cacheStrategy]")
public void addInclusion(
@NonNull CommandSource source,
@NonNull @Argument("name") Collection<ServiceTask> tasks,
@NonNull @Argument("url") String url,
@NonNull @Argument("path") String path
@NonNull @Argument("path") String path,
@NonNull @Argument(
value = "cacheStrategy",
parserName = "inclusionCacheStrategy",
defaultValue = ServiceRemoteInclusion.NO_CACHE_STRATEGY) String cacheStrategy
) {
var inclusion = ServiceRemoteInclusion.builder().url(url).destination(path).build();
var inclusion = ServiceRemoteInclusion.builder().url(url).destination(path).cacheStrategy(cacheStrategy).build();
this.applyChange(
source,
tasks,
(builder, $) -> builder.modifyInclusions(col -> col.add(inclusion)),
(builder, _) -> builder.modifyInclusions(col -> col.add(inclusion)),
"command-tasks-add-collection-property",
"inclusion",
inclusion);
Expand Down Expand Up @@ -829,6 +833,20 @@ public void clearProcessParameter(
null);
}

@CommandMethod("tasks task <name> clear inclusions")
public void clearInclusions(
@NonNull CommandSource source,
@NonNull @Argument("name") Collection<ServiceTask> tasks
) {
this.applyChange(
source,
tasks,
(builder, $) -> builder.modifyInclusions(Collection::clear),
"command-tasks-clear-property",
"inclusions",
null);
}

@CommandMethod("tasks task <name> unset javaCommand")
public void unsetJavaCommand(
@NonNull CommandSource source,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -19,33 +19,28 @@
import eu.cloudnetservice.driver.event.Cancelable;
import eu.cloudnetservice.driver.service.ServiceRemoteInclusion;
import eu.cloudnetservice.node.service.CloudService;
import kong.unirest.core.GetRequest;
import lombok.NonNull;

public final class CloudServicePreLoadInclusionEvent extends CloudServiceEvent implements Cancelable {

private final GetRequest request;
private final ServiceRemoteInclusion serviceRemoteInclusion;

private volatile boolean cancelled;
private boolean cancelled;
private ServiceRemoteInclusion serviceRemoteInclusion;

public CloudServicePreLoadInclusionEvent(
@NonNull CloudService cloudService,
@NonNull ServiceRemoteInclusion serviceRemoteInclusion,
@NonNull GetRequest request
@NonNull ServiceRemoteInclusion serviceRemoteInclusion
) {
super(cloudService);

this.serviceRemoteInclusion = serviceRemoteInclusion;
this.request = request;
}

public @NonNull ServiceRemoteInclusion inclusion() {
return this.serviceRemoteInclusion;
}

public @NonNull GetRequest request() {
return this.request;
public void inclusion(@NonNull ServiceRemoteInclusion inclusion) {
this.serviceRemoteInclusion = inclusion;
}

public boolean cancelled() {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -30,6 +30,7 @@
import eu.cloudnetservice.driver.network.rpc.handler.RPCHandlerRegistry;
import eu.cloudnetservice.driver.provider.GroupConfigurationProvider;
import eu.cloudnetservice.driver.service.GroupConfiguration;
import eu.cloudnetservice.driver.service.ServiceRemoteInclusion;
import eu.cloudnetservice.node.cluster.sync.DataSyncHandler;
import eu.cloudnetservice.node.cluster.sync.DataSyncRegistry;
import eu.cloudnetservice.node.event.group.LocalGroupConfigurationAddEvent;
Expand All @@ -45,6 +46,7 @@
import java.util.Collection;
import java.util.Collections;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;
import lombok.NonNull;
Expand All @@ -60,6 +62,7 @@ public class NodeGroupConfigurationProvider implements GroupConfigurationProvide
private static final Path GROUP_DIRECTORY_PATH = Path.of(
System.getProperty("cloudnet.config.groups.directory.path", "local/groups"));

private static final Type LIST_DOCUMENT_TYPE = TypeFactory.parameterizedClass(List.class, Document.class);
private static final Type TYPE = TypeFactory.parameterizedClass(Collection.class, GroupConfiguration.class);

private final EventManager eventManager;
Expand Down Expand Up @@ -228,6 +231,18 @@ protected void loadGroupConfigurations() {
document.append("environmentVariables", new HashMap<>());
}

// inclusions now feature a mandatory cache strategy field.
// Convert old inclusions that do not have a non-null value already set.
List<Document> remoteInclusions = document.readObject("includes", LIST_DOCUMENT_TYPE);
var migratedInclusions = remoteInclusions.stream().map(remoteInclusion -> {
if (remoteInclusion.containsNonNull("cacheStrategy")) {
return remoteInclusion;
}

return remoteInclusion.mutableCopy().append("cacheStrategy", ServiceRemoteInclusion.NO_CACHE_STRATEGY);
}).toList();
document.append("includes", migratedInclusions);

// load the group
var group = document.toInstanceOf(GroupConfiguration.class);

Expand Down
Loading