Skip to content

Commit

Permalink
Distinguish LicensedFeature by family field (elastic#116809)
Browse files Browse the repository at this point in the history
This PR fixes unintentional licensed feature overlaps for features with
the same name but different family fields.
  • Loading branch information
n1v0lg committed Nov 22, 2024
1 parent 193417f commit c8e42d8
Show file tree
Hide file tree
Showing 3 changed files with 52 additions and 2 deletions.
5 changes: 5 additions & 0 deletions docs/changelog/116809.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
pr: 116809
summary: "Distinguish `LicensedFeature` by family field"
area: License
type: bug
issues: []
Original file line number Diff line number Diff line change
Expand Up @@ -136,11 +136,11 @@ public boolean equals(Object o) {
if (this == o) return true;
if (o == null || getClass() != o.getClass()) return false;
LicensedFeature that = (LicensedFeature) o;
return Objects.equals(name, that.name);
return Objects.equals(name, that.name) && Objects.equals(family, that.family);
}

@Override
public int hashCode() {
return Objects.hash(name);
return Objects.hash(name, family);
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@

import java.util.Arrays;
import java.util.Map;
import java.util.Set;
import java.util.concurrent.atomic.AtomicInteger;
import java.util.function.Predicate;
import java.util.stream.Collectors;
Expand Down Expand Up @@ -228,6 +229,50 @@ public void testLastUsedMomentaryFeature() {
assertThat(lastUsed.get(usage), equalTo(200L));
}

public void testLastUsedMomentaryFeatureWithSameNameDifferentFamily() {
LicensedFeature.Momentary featureFamilyA = LicensedFeature.momentary("familyA", "goldFeature", GOLD);
LicensedFeature.Momentary featureFamilyB = LicensedFeature.momentary("familyB", "goldFeature", GOLD);

AtomicInteger currentTime = new AtomicInteger(100); // non zero start time
XPackLicenseState licenseState = new XPackLicenseState(currentTime::get);

featureFamilyA.check(licenseState);
featureFamilyB.check(licenseState);

Map<XPackLicenseState.FeatureUsage, Long> lastUsed = licenseState.getLastUsed();
assertThat("feature.check tracks usage separately by family", lastUsed, aMapWithSize(2));
Set<FeatureInfoWithTimestamp> actualFeatures = lastUsed.entrySet()
.stream()
.map(it -> new FeatureInfoWithTimestamp(it.getKey().feature().getFamily(), it.getKey().feature().getName(), it.getValue()))
.collect(Collectors.toSet());
assertThat(
actualFeatures,
containsInAnyOrder(
new FeatureInfoWithTimestamp("familyA", "goldFeature", 100L),
new FeatureInfoWithTimestamp("familyB", "goldFeature", 100L)
)
);

currentTime.set(200);
featureFamilyB.check(licenseState);

lastUsed = licenseState.getLastUsed();
assertThat("feature.check tracks usage separately by family", lastUsed, aMapWithSize(2));
actualFeatures = lastUsed.entrySet()
.stream()
.map(it -> new FeatureInfoWithTimestamp(it.getKey().feature().getFamily(), it.getKey().feature().getName(), it.getValue()))
.collect(Collectors.toSet());
assertThat(
actualFeatures,
containsInAnyOrder(
new FeatureInfoWithTimestamp("familyA", "goldFeature", 100L),
new FeatureInfoWithTimestamp("familyB", "goldFeature", 200L)
)
);
}

private record FeatureInfoWithTimestamp(String family, String featureName, Long timestamp) {}

public void testLastUsedPersistentFeature() {
LicensedFeature.Persistent goldFeature = LicensedFeature.persistent("family", "goldFeature", GOLD);
AtomicInteger currentTime = new AtomicInteger(100); // non zero start time
Expand Down

0 comments on commit c8e42d8

Please sign in to comment.