From 34288a8a81ef4c04ddc9e1117aa2bebeaed837cc Mon Sep 17 00:00:00 2001 From: sarahcaseybot Date: Wed, 4 Jan 2023 13:25:25 -0500 Subject: [PATCH] Add default tokens to TLD using nomulus tool (#1888) * Add defualt tokens to TLD using nomulus tool * add test --- .../tools/CreateOrUpdateTldCommand.java | 19 +++ .../tools/DeleteAllocationTokensCommand.java | 5 +- .../tools/UpdateAllocationTokensCommand.java | 2 +- ...UpdateOrDeleteAllocationTokensCommand.java | 20 +-- .../registry/tools/CreateTldCommandTest.java | 65 ++++++++++ .../registry/tools/UpdateTldCommandTest.java | 115 ++++++++++++++++++ 6 files changed, 214 insertions(+), 12 deletions(-) diff --git a/core/src/main/java/google/registry/tools/CreateOrUpdateTldCommand.java b/core/src/main/java/google/registry/tools/CreateOrUpdateTldCommand.java index b94e8a506..de802ae61 100644 --- a/core/src/main/java/google/registry/tools/CreateOrUpdateTldCommand.java +++ b/core/src/main/java/google/registry/tools/CreateOrUpdateTldCommand.java @@ -15,7 +15,9 @@ package google.registry.tools; import static com.google.common.base.Preconditions.checkArgument; +import static google.registry.tools.UpdateOrDeleteAllocationTokensCommand.getTokenKeys; import static google.registry.util.CollectionUtils.findDuplicates; +import static google.registry.util.CollectionUtils.isNullOrEmpty; import static google.registry.util.DomainNameUtils.canonicalizeHostname; import com.beust.jcommander.Parameter; @@ -232,6 +234,16 @@ abstract class CreateOrUpdateTldCommand extends MutatingCommand { ) Integer numDnsPublishShards; + @Nullable + @Parameter( + names = "--default_tokens", + description = + "A comma-separated list of default allocation tokens to be applied to the TLD. The" + + " ordering of this list will determine which token is used in the case where" + + " multiple tokens are valid for a registration. Use an empty string to clear all" + + " present default tokens.") + List defaultTokens; + /** Returns the existing registry (for update) or null (for creates). */ @Nullable abstract Registry getOldRegistry(String tld); @@ -373,6 +385,13 @@ abstract class CreateOrUpdateTldCommand extends MutatingCommand { builder.setAllowedFullyQualifiedHostNames(getAllowedNameservers(oldRegistry)); + if (!isNullOrEmpty(defaultTokens)) { + if (defaultTokens.equals(ImmutableList.of(""))) { + builder.setDefaultPromoTokens(ImmutableList.of()); + } else { + builder.setDefaultPromoTokens(getTokenKeys(defaultTokens, null)); + } + } // Update the Registry object. setCommandSpecificProperties(builder); stageEntityChange(oldRegistry, builder.build()); diff --git a/core/src/main/java/google/registry/tools/DeleteAllocationTokensCommand.java b/core/src/main/java/google/registry/tools/DeleteAllocationTokensCommand.java index 0b7d9a6e7..ce3fde830 100644 --- a/core/src/main/java/google/registry/tools/DeleteAllocationTokensCommand.java +++ b/core/src/main/java/google/registry/tools/DeleteAllocationTokensCommand.java @@ -24,6 +24,7 @@ import static google.registry.persistence.transaction.TransactionManagerFactory. import com.beust.jcommander.Parameter; import com.beust.jcommander.Parameters; import com.google.common.base.Joiner; +import com.google.common.collect.ImmutableList; import com.google.common.collect.ImmutableSet; import google.registry.model.domain.token.AllocationToken; import google.registry.persistence.VKey; @@ -48,11 +49,11 @@ final class DeleteAllocationTokensCommand extends UpdateOrDeleteAllocationTokens private static final int BATCH_SIZE = 20; private static final Joiner JOINER = Joiner.on(", "); - private ImmutableSet> tokensToDelete; + private ImmutableList> tokensToDelete; @Override public void init() { - tokensToDelete = getTokenKeys(); + tokensToDelete = getTokenKeys(tokens, prefix); } @Override diff --git a/core/src/main/java/google/registry/tools/UpdateAllocationTokensCommand.java b/core/src/main/java/google/registry/tools/UpdateAllocationTokensCommand.java index cf2edd39f..f77682a10 100644 --- a/core/src/main/java/google/registry/tools/UpdateAllocationTokensCommand.java +++ b/core/src/main/java/google/registry/tools/UpdateAllocationTokensCommand.java @@ -129,7 +129,7 @@ final class UpdateAllocationTokensCommand extends UpdateOrDeleteAllocationTokens tokensToSave = tm().transact( () -> - tm().loadByKeys(getTokenKeys()).values().stream() + tm().loadByKeys(getTokenKeys(tokens, prefix)).values().stream() .collect(toImmutableMap(Function.identity(), this::updateToken)) .entrySet() .stream() diff --git a/core/src/main/java/google/registry/tools/UpdateOrDeleteAllocationTokensCommand.java b/core/src/main/java/google/registry/tools/UpdateOrDeleteAllocationTokensCommand.java index 58044ace0..51ac05052 100644 --- a/core/src/main/java/google/registry/tools/UpdateOrDeleteAllocationTokensCommand.java +++ b/core/src/main/java/google/registry/tools/UpdateOrDeleteAllocationTokensCommand.java @@ -16,14 +16,15 @@ package google.registry.tools; import static com.google.common.base.Preconditions.checkArgument; import static com.google.common.base.Preconditions.checkState; -import static com.google.common.collect.ImmutableSet.toImmutableSet; +import static com.google.common.collect.ImmutableList.toImmutableList; import static google.registry.persistence.transaction.TransactionManagerFactory.tm; import com.beust.jcommander.Parameter; -import com.google.common.collect.ImmutableSet; +import com.google.common.collect.ImmutableList; import google.registry.model.domain.token.AllocationToken; import google.registry.persistence.VKey; import java.util.List; +import javax.annotation.Nullable; /** Shared base class for commands to update or delete allocation tokens. */ abstract class UpdateOrDeleteAllocationTokensCommand extends ConfirmingCommand { @@ -47,19 +48,20 @@ abstract class UpdateOrDeleteAllocationTokensCommand extends ConfirmingCommand { description = "Do not actually update or delete the tokens; defaults to false") protected boolean dryRun; - protected ImmutableSet> getTokenKeys() { + public static ImmutableList> getTokenKeys( + @Nullable List tokens, @Nullable String prefix) { checkArgument( tokens == null ^ prefix == null, "Must provide one of --tokens or --prefix, not both / neither"); if (tokens != null) { - ImmutableSet> keys = + ImmutableList> keys = tokens.stream() .map(token -> VKey.create(AllocationToken.class, token)) - .collect(toImmutableSet()); - ImmutableSet> nonexistentKeys = + .collect(toImmutableList()); + ImmutableList> nonexistentKeys = tm().transact( - () -> keys.stream().filter(key -> !tm().exists(key)).collect(toImmutableSet())); - checkState(nonexistentKeys.isEmpty(), "Tokens with keys %s did not exist.", nonexistentKeys); + () -> keys.stream().filter(key -> !tm().exists(key)).collect(toImmutableList())); + checkState(nonexistentKeys.isEmpty(), "Tokens with keys %s did not exist", nonexistentKeys); return keys; } else { checkArgument(!prefix.isEmpty(), "Provided prefix should not be blank"); @@ -68,7 +70,7 @@ abstract class UpdateOrDeleteAllocationTokensCommand extends ConfirmingCommand { tm().loadAllOf(AllocationToken.class).stream() .filter(token -> token.getToken().startsWith(prefix)) .map(AllocationToken::createVKey) - .collect(toImmutableSet())); + .collect(toImmutableList())); } } } diff --git a/core/src/test/java/google/registry/tools/CreateTldCommandTest.java b/core/src/test/java/google/registry/tools/CreateTldCommandTest.java index bcbc05fdf..000cd0371 100644 --- a/core/src/test/java/google/registry/tools/CreateTldCommandTest.java +++ b/core/src/test/java/google/registry/tools/CreateTldCommandTest.java @@ -16,11 +16,13 @@ package google.registry.tools; import static com.google.common.truth.Truth.assertThat; import static com.google.common.truth.Truth8.assertThat; +import static google.registry.model.domain.token.AllocationToken.TokenType.DEFAULT_PROMO; import static google.registry.model.tld.Registry.TldState.GENERAL_AVAILABILITY; import static google.registry.model.tld.Registry.TldState.PREDELEGATION; import static google.registry.testing.DatabaseHelper.createTld; import static google.registry.testing.DatabaseHelper.persistPremiumList; import static google.registry.testing.DatabaseHelper.persistReservedList; +import static google.registry.testing.DatabaseHelper.persistResource; import static google.registry.util.DateTimeUtils.START_OF_TIME; import static java.math.BigDecimal.ROUND_UNNECESSARY; import static org.joda.money.CurrencyUnit.JPY; @@ -32,6 +34,7 @@ import static org.junit.jupiter.api.Assertions.assertThrows; import com.beust.jcommander.ParameterException; import com.google.common.collect.ImmutableSet; import com.google.common.collect.Range; +import google.registry.model.domain.token.AllocationToken; import google.registry.model.tld.Registry; import java.math.BigDecimal; import org.joda.money.Money; @@ -568,6 +571,68 @@ class CreateTldCommandTest extends CommandTestCase { .contains("Invalid DNS writer name(s) specified: [Deadbeef, Invalid]"); } + @Test + void testSuccess_defaultToken() throws Exception { + AllocationToken token = + persistResource( + new AllocationToken() + .asBuilder() + .setToken("abc123") + .setTokenType(DEFAULT_PROMO) + .setAllowedTlds(ImmutableSet.of("xn--q9jyb4c")) + .build()); + runCommandForced( + "--default_tokens=abc123", + "--roid_suffix=Q9JYB4C", + "--dns_writers=FooDnsWriter", + "xn--q9jyb4c"); + assertThat(Registry.get("xn--q9jyb4c").getDefaultPromoTokens()) + .containsExactly(token.createVKey()); + } + + @Test + void testSuccess_multipleDefaultTokens() throws Exception { + AllocationToken token = + persistResource( + new AllocationToken() + .asBuilder() + .setToken("abc123") + .setTokenType(DEFAULT_PROMO) + .setAllowedTlds(ImmutableSet.of("xn--q9jyb4c")) + .build()); + AllocationToken token2 = + persistResource( + new AllocationToken() + .asBuilder() + .setToken("token") + .setTokenType(DEFAULT_PROMO) + .setAllowedTlds(ImmutableSet.of("xn--q9jyb4c")) + .build()); + runCommandForced( + "--default_tokens=abc123,token", + "--roid_suffix=Q9JYB4C", + "--dns_writers=FooDnsWriter", + "xn--q9jyb4c"); + assertThat(Registry.get("xn--q9jyb4c").getDefaultPromoTokens()) + .containsExactly(token.createVKey(), token2.createVKey()); + } + + @Test + void testFailure_specifiedDefaultToken_doesntExist() { + IllegalStateException thrown = + assertThrows( + IllegalStateException.class, + () -> + runCommandForced( + "xn--q9jyb4c", + "--default_tokens=InvalidToken", + "--roid_suffix=Q9JYB4C", + "--dns_writers=FooDnsWriter")); + assertThat(thrown) + .hasMessageThat() + .contains("Tokens with keys [VKey(sql:InvalidToken)] did not exist"); + } + private void runSuccessfulReservedListsTest(String reservedLists) throws Exception { runCommandForced( "--reserved_lists", diff --git a/core/src/test/java/google/registry/tools/UpdateTldCommandTest.java b/core/src/test/java/google/registry/tools/UpdateTldCommandTest.java index 45523d5f9..c3d9338c6 100644 --- a/core/src/test/java/google/registry/tools/UpdateTldCommandTest.java +++ b/core/src/test/java/google/registry/tools/UpdateTldCommandTest.java @@ -16,6 +16,7 @@ package google.registry.tools; import static com.google.common.truth.Truth.assertThat; import static com.google.common.truth.Truth8.assertThat; +import static google.registry.model.domain.token.AllocationToken.TokenType.DEFAULT_PROMO; import static google.registry.model.tld.Registry.TldState.GENERAL_AVAILABILITY; import static google.registry.model.tld.Registry.TldState.PREDELEGATION; import static google.registry.model.tld.Registry.TldState.QUIET_PERIOD; @@ -33,8 +34,10 @@ import static org.joda.time.Duration.standardMinutes; import static org.junit.jupiter.api.Assertions.assertThrows; import com.beust.jcommander.ParameterException; +import com.google.common.collect.ImmutableList; import com.google.common.collect.ImmutableSet; import com.google.common.collect.ImmutableSortedMap; +import google.registry.model.domain.token.AllocationToken; import google.registry.model.tld.Registry; import java.util.Optional; import org.joda.money.Money; @@ -174,6 +177,118 @@ class UpdateTldCommandTest extends CommandTestCase { .containsExactly("FooDnsWriter", "VoidDnsWriter"); } + @Test + void testSuccess_defaultToken() throws Exception { + AllocationToken token = + persistResource( + new AllocationToken() + .asBuilder() + .setToken("abc123") + .setTokenType(DEFAULT_PROMO) + .setAllowedTlds(ImmutableSet.of("xn--q9jyb4c")) + .build()); + assertThat(Registry.get("xn--q9jyb4c").getDefaultPromoTokens()).isEmpty(); + runCommandForced("--default_tokens=abc123", "xn--q9jyb4c"); + assertThat(Registry.get("xn--q9jyb4c").getDefaultPromoTokens()) + .containsExactly(token.createVKey()); + } + + @Test + void testSuccess_multipleDefaultTokens() throws Exception { + AllocationToken token = + persistResource( + new AllocationToken() + .asBuilder() + .setToken("abc123") + .setTokenType(DEFAULT_PROMO) + .setAllowedTlds(ImmutableSet.of("xn--q9jyb4c")) + .build()); + AllocationToken token2 = + persistResource( + new AllocationToken() + .asBuilder() + .setToken("token") + .setTokenType(DEFAULT_PROMO) + .setAllowedTlds(ImmutableSet.of("xn--q9jyb4c")) + .build()); + assertThat(Registry.get("xn--q9jyb4c").getDefaultPromoTokens()).isEmpty(); + runCommandForced("--default_tokens=abc123,token", "xn--q9jyb4c"); + assertThat(Registry.get("xn--q9jyb4c").getDefaultPromoTokens()) + .containsExactly(token.createVKey(), token2.createVKey()); + } + + @Test + void testSuccess_emptyTokenList() throws Exception { + AllocationToken token = + persistResource( + new AllocationToken() + .asBuilder() + .setToken("abc123") + .setTokenType(DEFAULT_PROMO) + .setAllowedTlds(ImmutableSet.of("xn--q9jyb4c")) + .build()); + assertThat(Registry.get("xn--q9jyb4c").getDefaultPromoTokens()).isEmpty(); + persistResource( + Registry.get("xn--q9jyb4c") + .asBuilder() + .setDefaultPromoTokens(ImmutableList.of(token.createVKey())) + .build()); + assertThat(Registry.get("xn--q9jyb4c").getDefaultPromoTokens()) + .containsExactly(token.createVKey()); + runCommandForced("--default_tokens=", "xn--q9jyb4c"); + assertThat(Registry.get("xn--q9jyb4c").getDefaultPromoTokens()).isEmpty(); + } + + @Test + void testSuccess_replaceExistingDefaultTokensListOrder() throws Exception { + AllocationToken token = + persistResource( + new AllocationToken() + .asBuilder() + .setToken("abc123") + .setTokenType(DEFAULT_PROMO) + .setAllowedTlds(ImmutableSet.of("xn--q9jyb4c")) + .build()); + AllocationToken token2 = + persistResource( + new AllocationToken() + .asBuilder() + .setToken("token") + .setTokenType(DEFAULT_PROMO) + .setAllowedTlds(ImmutableSet.of("xn--q9jyb4c")) + .build()); + AllocationToken token3 = + persistResource( + new AllocationToken() + .asBuilder() + .setToken("othertoken") + .setTokenType(DEFAULT_PROMO) + .setAllowedTlds(ImmutableSet.of("xn--q9jyb4c")) + .build()); + assertThat(Registry.get("xn--q9jyb4c").getDefaultPromoTokens()).isEmpty(); + persistResource( + Registry.get("xn--q9jyb4c") + .asBuilder() + .setDefaultPromoTokens(ImmutableList.of(token.createVKey(), token2.createVKey())) + .build()); + assertThat(Registry.get("xn--q9jyb4c").getDefaultPromoTokens()) + .containsExactly(token.createVKey(), token2.createVKey()); + runCommandForced("--default_tokens=token,othertoken", "xn--q9jyb4c"); + assertThat(Registry.get("xn--q9jyb4c").getDefaultPromoTokens()) + .containsExactly(token2.createVKey(), token3.createVKey()); + } + + @Test + void testFailure_specifiedDefaultToken_doesntExist() { + IllegalStateException thrown = + assertThrows( + IllegalStateException.class, + () -> runCommandForced("xn--q9jyb4c", "--default_tokens=InvalidToken")); + assertThat(thrown) + .hasMessageThat() + .contains("Tokens with keys [VKey(sql:InvalidToken)] did not exist"); + } + @Test void testSuccess_escrow() throws Exception { runCommandForced("--escrow=true", "xn--q9jyb4c");