Skip to content

Commit

Permalink
Added support for list of ranges
Browse files Browse the repository at this point in the history
Signed-off-by: Abhilasha Seth <[email protected]>
  • Loading branch information
abseth-amzn committed Dec 15, 2023
1 parent c3b2718 commit d785fa7
Show file tree
Hide file tree
Showing 15 changed files with 375 additions and 105 deletions.
1 change: 1 addition & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@ The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/),
- Allow to pass the list settings through environment variables (like [], ["a", "b", "c"], ...) ([#10625](https://github.com/opensearch-project/OpenSearch/pull/10625))
- [Admission Control] Integrate CPU AC with ResourceUsageCollector and add CPU AC stats to nodes/stats ([#10887](https://github.com/opensearch-project/OpenSearch/pull/10887))
- Maintainer approval check ([#11378](https://github.com/opensearch-project/OpenSearch/pull/11378))
- Add support for dependencies in plugin descriptor properties with semver range ([#11441](https://github.com/opensearch-project/OpenSearch/pull/11441))

### Dependencies
- Bump `log4j-core` from 2.18.0 to 2.19.0
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -83,7 +83,7 @@ private void printPlugin(Environment env, Terminal terminal, Path plugin, String
"WARNING: plugin ["
+ info.getName()
+ "] was built for OpenSearch version "
+ info.getOpenSearchVersionRange().toString()
+ info.getOpenSearchVersionRangesString()
+ " and is not compatible with "
+ Version.CURRENT
);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -751,6 +751,8 @@ public Object readGenericValue() throws IOException {
return readCollection(StreamInput::readGenericValue, HashSet::new, Collections.emptySet());
case 26:
return readBigInteger();
case 27:
return readSemverRange();
default:
throw new IOException("Can't read unknown type [" + type + "]");
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -785,6 +785,10 @@ public final void writeOptionalInstant(@Nullable Instant instant) throws IOExcep
o.writeByte((byte) 26);
o.writeString(v.toString());
});
writers.put(SemverRange.class, (o, v) -> {
o.writeByte((byte) 27);
o.writeSemverRange((SemverRange) v);
});
WRITERS = Collections.unmodifiableMap(writers);
}

Expand Down
48 changes: 20 additions & 28 deletions libs/core/src/main/java/org/opensearch/semver/SemverRange.java
Original file line number Diff line number Diff line change
Expand Up @@ -17,11 +17,9 @@
import org.opensearch.semver.expr.Tilde;

import java.io.IOException;
import java.util.Locale;
import java.util.Objects;
import java.util.Optional;

import static java.lang.String.format;
import static java.util.Arrays.stream;

/**
Expand All @@ -43,10 +41,7 @@ public SemverRange(final Version rangeVersion, final RangeOperator rangeOperator
* @return a {@code SemverRange}
*/
public static SemverRange fromString(final String range) {
Optional<RangeOperator> operator = stream(RangeOperator.values()).filter(
rangeOperator -> rangeOperator != RangeOperator.DEFAULT && range.startsWith(rangeOperator.asString())
).findFirst();
RangeOperator rangeOperator = operator.orElse(RangeOperator.DEFAULT);
RangeOperator rangeOperator = RangeOperator.fromRange(range);
String version = range.replaceFirst(rangeOperator.asString(), "");
if (!Version.stringHasLength(version)) {
throw new IllegalArgumentException("Version cannot be empty");
Expand All @@ -73,34 +68,22 @@ public Version getRangeVersion() {
/**
* Check if range is satisfied by given version string.
*
* @param version version to check
* @param versionToEvaluate version to check
* @return {@code true} if range is satisfied by version, {@code false} otherwise
*/
public boolean isSatisfiedBy(final String version) {
return isSatisfiedBy(Version.fromString(version));
public boolean isSatisfiedBy(final String versionToEvaluate) {
return isSatisfiedBy(Version.fromString(versionToEvaluate));
}

/**
* Check if range is satisfied by given version.
*
* @param version version to check
* @param versionToEvaluate version to check
* @return {@code true} if range is satisfied by version, {@code false} otherwise
* @see #isSatisfiedBy(String)
*/
public boolean isSatisfiedBy(final Version version) {
Expression expression = null;
switch (rangeOperator) {
case DEFAULT:
case EQ:
expression = new Equal(rangeVersion);
break;
case TILDE:
expression = new Tilde(rangeVersion);
break;
default:
throw new RuntimeException(format(Locale.ROOT, "Unsupported range operator: %s", rangeOperator));
}
return expression.evaluate(version);
public boolean isSatisfiedBy(final Version versionToEvaluate) {
return this.rangeOperator.expression.evaluate(this.rangeVersion, versionToEvaluate);
}

@Override
Expand Down Expand Up @@ -135,14 +118,16 @@ public XContentBuilder toXContent(final XContentBuilder builder, final Params pa
*/
public enum RangeOperator {

EQ("="),
TILDE("~"),
DEFAULT("");
EQ("=", new Equal()),
TILDE("~", new Tilde()),
DEFAULT("", new Equal());

private final String operator;
private final Expression expression;

RangeOperator(final String operator) {
RangeOperator(final String operator, final Expression expression) {
this.operator = operator;
this.expression = expression;
}

/**
Expand All @@ -153,5 +138,12 @@ public enum RangeOperator {
public String asString() {
return operator;
}

public static RangeOperator fromRange(final String range) {
Optional<RangeOperator> rangeOperator = stream(values()).filter(
operator -> operator != DEFAULT && range.startsWith(operator.asString())
).findFirst();
return rangeOperator.orElse(DEFAULT);
}
}
}
20 changes: 5 additions & 15 deletions libs/core/src/main/java/org/opensearch/semver/expr/Equal.java
Original file line number Diff line number Diff line change
Expand Up @@ -15,25 +15,15 @@
*/
public class Equal implements Expression {

private final Version version;

/**
* Constructs a {@code Equal} expression with the given version.
*
* @param version given version
*/
public Equal(final Version version) {
this.version = version;
}

/**
* Checks if the current version equals the member version.
* Checks if a given version matches a certain range version.
*
* @param version the version to evaluate
* @param rangeVersion the version specified in range
* @param versionToEvaluate the version to evaluate
* @return {@code true} if the versions are equal {@code false} otherwise
*/
@Override
public boolean evaluate(final Version version) {
return version.equals(this.version);
public boolean evaluate(final Version rangeVersion, final Version versionToEvaluate) {
return versionToEvaluate.equals(rangeVersion);
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -18,8 +18,9 @@ public interface Expression {
/**
* Evaluates an expression.
*
* @param version the version to evaluate
* @param rangeVersion the version specified in range
* @param versionToEvaluate the version to evaluate
* @return the result of the expression evaluation
*/
boolean evaluate(final Version version);
boolean evaluate(final Version rangeVersion, final Version versionToEvaluate);
}
21 changes: 5 additions & 16 deletions libs/core/src/main/java/org/opensearch/semver/expr/Tilde.java
Original file line number Diff line number Diff line change
Expand Up @@ -15,28 +15,17 @@
*/
public class Tilde implements Expression {

private final Version rangeVersion;

/**
* Constructs a {@code Tilde} expression with the given range version.
*
* @param rangeVersion rangeVersion
*/
public Tilde(final Version rangeVersion) {
this.rangeVersion = rangeVersion;
}

/**
* Checks if the given input version is compatible with the rangeVersion allowing for patch version variability.
* Checks if the given version is compatible with a range version allowing for patch version variability.
* Allows all versions starting from the rangeVersion upto next minor version (exclusive).
*
* @param version the version to evaluate
* @param rangeVersion the version specified in range
* @param versionToEvaluate the version to evaluate
* @return {@code true} if the versions are compatible {@code false} otherwise
*/
@Override
public boolean evaluate(final Version version) {
public boolean evaluate(final Version rangeVersion, final Version versionToEvaluate) {
Version lower = rangeVersion;
Version upper = Version.fromString(rangeVersion.major + "." + (rangeVersion.minor + 1) + "." + 0);
return version.onOrAfter(lower) && version.before(upper);
return versionToEvaluate.onOrAfter(lower) && versionToEvaluate.before(upper);
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -14,8 +14,9 @@
public class EqualTests extends OpenSearchTestCase {

public void testEquality() {
Equal equalExpr = new Equal(Version.fromString("1.2.3"));
assertTrue(equalExpr.evaluate(Version.fromString("1.2.3")));
assertFalse(equalExpr.evaluate(Version.fromString("1.2.4")));
Equal equalExpr = new Equal();
Version rangeVersion = Version.fromString("1.2.3");
assertTrue(equalExpr.evaluate(rangeVersion, Version.fromString("1.2.3")));
assertFalse(equalExpr.evaluate(rangeVersion, Version.fromString("1.2.4")));
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -14,15 +14,16 @@
public class TildeTests extends OpenSearchTestCase {

public void testPatchVersionVariability() {
Tilde tildeExpr = new Tilde(Version.fromString("1.2.3"));
Tilde tildeExpr = new Tilde();
Version rangeVersion = Version.fromString("1.2.3");

assertTrue(tildeExpr.evaluate(Version.fromString("1.2.3")));
assertTrue(tildeExpr.evaluate(Version.fromString("1.2.4")));
assertTrue(tildeExpr.evaluate(Version.fromString("1.2.9")));
assertTrue(tildeExpr.evaluate(rangeVersion, Version.fromString("1.2.3")));
assertTrue(tildeExpr.evaluate(rangeVersion, Version.fromString("1.2.4")));
assertTrue(tildeExpr.evaluate(rangeVersion, Version.fromString("1.2.9")));

assertFalse(tildeExpr.evaluate(Version.fromString("1.2.0")));
assertFalse(tildeExpr.evaluate(Version.fromString("1.2.2")));
assertFalse(tildeExpr.evaluate(Version.fromString("1.3.0")));
assertFalse(tildeExpr.evaluate(Version.fromString("2.0.0")));
assertFalse(tildeExpr.evaluate(rangeVersion, Version.fromString("1.2.0")));
assertFalse(tildeExpr.evaluate(rangeVersion, Version.fromString("1.2.2")));
assertFalse(tildeExpr.evaluate(rangeVersion, Version.fromString("1.3.0")));
assertFalse(tildeExpr.evaluate(rangeVersion, Version.fromString("2.0.0")));
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,118 @@
/*
* SPDX-License-Identifier: Apache-2.0
*
* The OpenSearch Contributors require contributions made to
* this file be licensed under the Apache-2.0 license or a
* compatible open source license.
*/

package org.opensearch.plugins;

import org.opensearch.Version;
import org.opensearch.common.settings.Settings;
import org.opensearch.env.Environment;
import org.opensearch.test.OpenSearchIntegTestCase;
import org.opensearch.test.VersionUtils;

import java.io.IOException;
import java.io.InputStream;
import java.nio.file.Files;
import java.nio.file.Path;

import static org.hamcrest.Matchers.containsString;

@OpenSearchIntegTestCase.ClusterScope(scope = OpenSearchIntegTestCase.Scope.TEST, numDataNodes = 0)
public class PluginsServiceIT extends OpenSearchIntegTestCase {

public void testNodeBootstrapWithCompatiblePlugin() throws IOException {
// Prepare the plugins directory and then start a node
Path baseDir = createTempDir();
Path pluginDir = baseDir.resolve("plugins/dummy-plugin");
PluginTestUtil.writePluginProperties(
pluginDir,
"description",
"dummy desc",
"name",
"dummyPlugin",
"version",
"1.0",
"opensearch.version",
Version.CURRENT.toString(),
"java.version",
System.getProperty("java.specification.version"),
"classname",
"test.DummyPlugin"
);
try (InputStream jar = PluginsServiceTests.class.getResourceAsStream("dummy-plugin.jar")) {
Files.copy(jar, pluginDir.resolve("dummy-plugin.jar"));
}
internalCluster().startNode(Settings.builder().put(Environment.PATH_HOME_SETTING.getKey(), baseDir));
for (PluginsService pluginsService : internalCluster().getDataNodeInstances(PluginsService.class)) {
// Ensure plugins service was able to load the plugin
assertEquals(1, pluginsService.info().getPluginInfos().stream().filter(info -> info.getName().equals("dummyPlugin")).count());
}
}

public void testNodeBootstrapWithRangeCompatiblePlugin() throws IOException {
// Prepare the plugins directory and then start a node
Path baseDir = createTempDir();
Path pluginDir = baseDir.resolve("plugins/dummy-plugin");
String range1 = "~" + Version.CURRENT;
String range2 = "=" + Version.CURRENT;
String ranges = "\"" + range1 + "," + range2 + "\"";
PluginTestUtil.writePluginProperties(
pluginDir,
"description",
"dummy desc",
"name",
"dummyPlugin",
"version",
"1.0",
"dependencies",
"{opensearch:" + ranges + "}",
"java.version",
System.getProperty("java.specification.version"),
"classname",
"test.DummyPlugin"
);
try (InputStream jar = PluginsServiceTests.class.getResourceAsStream("dummy-plugin.jar")) {
Files.copy(jar, pluginDir.resolve("dummy-plugin.jar"));
}
internalCluster().startNode(Settings.builder().put(Environment.PATH_HOME_SETTING.getKey(), baseDir));
for (PluginsService pluginsService : internalCluster().getDataNodeInstances(PluginsService.class)) {
// Ensure plugins service was able to load the plugin
assertEquals(1, pluginsService.info().getPluginInfos().stream().filter(info -> info.getName().equals("dummyPlugin")).count());
}
}

public void testNodeBootstrapWithInCompatiblePlugin() throws IOException {
// Prepare the plugins directory with an incompatible plugin and attempt to start a node
Path baseDir = createTempDir();
Path pluginDir = baseDir.resolve("plugins/dummy-plugin");
String incompatibleRange = "~"
+ VersionUtils.getVersion(Version.CURRENT.major, Version.CURRENT.minor, (byte) (Version.CURRENT.revision + 1));
PluginTestUtil.writePluginProperties(
pluginDir,
"description",
"dummy desc",
"name",
"dummyPlugin",
"version",
"1.0",
"dependencies",
"{opensearch:" + incompatibleRange + "}",
"java.version",
System.getProperty("java.specification.version"),
"classname",
"test.DummyPlugin"
);
try (InputStream jar = PluginsServiceTests.class.getResourceAsStream("dummy-plugin.jar")) {
Files.copy(jar, pluginDir.resolve("dummy-plugin.jar"));
}
IllegalArgumentException e = assertThrows(
IllegalArgumentException.class,
() -> internalCluster().startNode(Settings.builder().put(Environment.PATH_HOME_SETTING.getKey(), baseDir))
);
assertThat(e.getMessage(), containsString("Plugin [dummyPlugin] was built for OpenSearch version "));
}
}
Loading

0 comments on commit d785fa7

Please sign in to comment.