Add databaseTransitionSchedule entity and tool for updating (#926)

* Add databaseTransitionSchedule entitiy

* add UpdateDatabaseTransitionScheduleCommand

* small fixes

* change entity structure to no longer be singleton

* add get command

* fix getCache

* Change id to TransitionId enum

* more fixes

* Cleanup tests

* Add link to javadoc

* Add lastUpdateTime

* fix datatype of getCached
This commit is contained in:
sarahcaseybot
2021-02-08 17:22:00 -05:00
committed by GitHub
parent d73e557acc
commit bdf9124e87
12 changed files with 633 additions and 0 deletions
@@ -0,0 +1,79 @@
// Copyright 2021 The Nomulus Authors. All Rights Reserved.
//
// Licensed 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 google.registry.model.common;
import static com.google.common.truth.Truth.assertThat;
import static google.registry.persistence.transaction.TransactionManagerFactory.ofyTm;
import static google.registry.util.DateTimeUtils.START_OF_TIME;
import static org.junit.jupiter.api.Assertions.assertThrows;
import com.google.common.collect.ImmutableSortedMap;
import google.registry.model.EntityTestCase;
import google.registry.model.common.DatabaseTransitionSchedule.PrimaryDatabase;
import google.registry.model.common.DatabaseTransitionSchedule.PrimaryDatabaseTransition;
import google.registry.model.common.DatabaseTransitionSchedule.TransitionId;
import org.joda.time.Duration;
import org.junit.jupiter.api.Test;
/** Unit tests for {@link DatabaseTransitionSchedule}. */
public class DatabaseTransitionScheduleTest extends EntityTestCase {
@Test
void testSuccess_persistence() {
TimedTransitionProperty<PrimaryDatabase, PrimaryDatabaseTransition> databaseTransitions =
TimedTransitionProperty.fromValueMap(
ImmutableSortedMap.of(START_OF_TIME, PrimaryDatabase.DATASTORE),
PrimaryDatabaseTransition.class);
DatabaseTransitionSchedule schedule =
DatabaseTransitionSchedule.create(
TransitionId.SIGNED_MARK_REVOCATION_LIST, databaseTransitions);
ofyTm().transactNew(() -> ofyTm().put(schedule));
assertThat(
DatabaseTransitionSchedule.get(TransitionId.SIGNED_MARK_REVOCATION_LIST)
.get()
.databaseTransitions)
.isEqualTo(databaseTransitions);
}
@Test
void testFailure_scheduleWithNoStartOfTime() {
assertThrows(
IllegalArgumentException.class,
() ->
DatabaseTransitionSchedule.create(
TransitionId.SIGNED_MARK_REVOCATION_LIST,
TimedTransitionProperty.fromValueMap(
ImmutableSortedMap.of(fakeClock.nowUtc(), PrimaryDatabase.DATASTORE),
PrimaryDatabaseTransition.class)));
}
@Test
void testSuccess_getPrimaryDatabase() {
DatabaseTransitionSchedule schedule =
DatabaseTransitionSchedule.create(
TransitionId.SIGNED_MARK_REVOCATION_LIST,
TimedTransitionProperty.fromValueMap(
ImmutableSortedMap.of(
START_OF_TIME,
PrimaryDatabase.DATASTORE,
fakeClock.nowUtc().plusDays(1),
PrimaryDatabase.CLOUD_SQL),
PrimaryDatabaseTransition.class));
assertThat(ofyTm().transact(schedule::getPrimaryDatabase)).isEqualTo(PrimaryDatabase.DATASTORE);
fakeClock.advanceBy(Duration.standardDays(5));
assertThat(ofyTm().transact(schedule::getPrimaryDatabase)).isEqualTo(PrimaryDatabase.CLOUD_SQL);
}
}
@@ -0,0 +1,127 @@
// Copyright 2021 The Nomulus Authors. All Rights Reserved.
//
// Licensed 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 google.registry.tools;
import static com.google.common.truth.Truth.assertThat;
import static google.registry.persistence.transaction.TransactionManagerFactory.ofyTm;
import static google.registry.util.DateTimeUtils.START_OF_TIME;
import static org.junit.jupiter.api.Assertions.assertThrows;
import com.beust.jcommander.ParameterException;
import com.google.common.collect.ImmutableSortedMap;
import google.registry.model.common.DatabaseTransitionSchedule;
import google.registry.model.common.DatabaseTransitionSchedule.PrimaryDatabase;
import google.registry.model.common.DatabaseTransitionSchedule.PrimaryDatabaseTransition;
import google.registry.model.common.DatabaseTransitionSchedule.TransitionId;
import google.registry.model.common.TimedTransitionProperty;
import google.registry.model.ofy.Ofy;
import google.registry.testing.FakeClock;
import google.registry.testing.InjectExtension;
import org.joda.time.DateTime;
import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.Test;
import org.junit.jupiter.api.extension.RegisterExtension;
/** Unit tests for {@link GetDatabaseTransitionScheduleCommand} */
public class GetDatabaseTransitionScheduleCommandTest
extends CommandTestCase<GetDatabaseTransitionScheduleCommand> {
@RegisterExtension public final InjectExtension inject = new InjectExtension();
@BeforeEach
void beforeEach() {
inject.setStaticField(
Ofy.class, "clock", new FakeClock(DateTime.parse("1984-12-21T06:07:08.789Z")));
}
@Test
void testSuccess() throws Exception {
TimedTransitionProperty<PrimaryDatabase, PrimaryDatabaseTransition> databaseTransitions =
TimedTransitionProperty.fromValueMap(
ImmutableSortedMap.of(START_OF_TIME, PrimaryDatabase.DATASTORE),
PrimaryDatabaseTransition.class);
DatabaseTransitionSchedule schedule =
DatabaseTransitionSchedule.create(
TransitionId.SIGNED_MARK_REVOCATION_LIST, databaseTransitions);
ofyTm().transactNew(() -> ofyTm().put(schedule));
runCommand("SIGNED_MARK_REVOCATION_LIST");
assertStdoutIs(
"SIGNED_MARK_REVOCATION_LIST(last updated at 1984-12-21T06:07:08.789Z):"
+ " {1970-01-01T00:00:00.000Z=DATASTORE}\n");
}
@Test
void testSuccess_multipleArguments() throws Exception {
fakeClock.setTo(DateTime.parse("2020-10-01T00:00:00Z"));
TimedTransitionProperty<PrimaryDatabase, PrimaryDatabaseTransition> databaseTransitions =
TimedTransitionProperty.fromValueMap(
ImmutableSortedMap.of(START_OF_TIME, PrimaryDatabase.DATASTORE),
PrimaryDatabaseTransition.class);
DatabaseTransitionSchedule schedule =
DatabaseTransitionSchedule.create(TransitionId.DOMAIN_LABEL_LISTS, databaseTransitions);
ofyTm().transactNew(() -> ofyTm().put(schedule));
TimedTransitionProperty<PrimaryDatabase, PrimaryDatabaseTransition> databaseTransitions2 =
TimedTransitionProperty.fromValueMap(
ImmutableSortedMap.of(
START_OF_TIME,
PrimaryDatabase.DATASTORE,
fakeClock.nowUtc(),
PrimaryDatabase.CLOUD_SQL),
PrimaryDatabaseTransition.class);
DatabaseTransitionSchedule schedule2 =
DatabaseTransitionSchedule.create(
TransitionId.SIGNED_MARK_REVOCATION_LIST, databaseTransitions2);
ofyTm().transactNew(() -> ofyTm().put(schedule2));
runCommand("DOMAIN_LABEL_LISTS", "SIGNED_MARK_REVOCATION_LIST");
assertStdoutIs(
"DOMAIN_LABEL_LISTS(last updated at 1984-12-21T06:07:08.789Z):"
+ " {1970-01-01T00:00:00.000Z=DATASTORE}\n"
+ "SIGNED_MARK_REVOCATION_LIST(last updated at 1984-12-21T06:07:08.789Z):"
+ " {1970-01-01T00:00:00.000Z=DATASTORE, 2020-10-01T00:00:00.000Z=CLOUD_SQL}\n");
}
@Test
void testFailure_scheduleDoesNotExist() {
IllegalArgumentException thrown =
assertThrows(
IllegalArgumentException.class, () -> runCommand("SIGNED_MARK_REVOCATION_LIST"));
assertThat(thrown)
.hasMessageThat()
.contains("A database transition schedule for SIGNED_MARK_REVOCATION_LIST does not exist");
}
@Test
void testFailure_noIdGiven() {
assertThrows(ParameterException.class, this::runCommand);
}
@Test
void testFailure_oneScheduleDoesNotExist() {
TimedTransitionProperty<PrimaryDatabase, PrimaryDatabaseTransition> databaseTransitions =
TimedTransitionProperty.fromValueMap(
ImmutableSortedMap.of(START_OF_TIME, PrimaryDatabase.DATASTORE),
PrimaryDatabaseTransition.class);
DatabaseTransitionSchedule schedule =
DatabaseTransitionSchedule.create(TransitionId.DOMAIN_LABEL_LISTS, databaseTransitions);
ofyTm().transactNew(() -> ofyTm().put(schedule));
IllegalArgumentException thrown =
assertThrows(
IllegalArgumentException.class,
() -> runCommand("DOMAIN_LABEL_LISTS", "SIGNED_MARK_REVOCATION_LIST"));
assertThat(thrown)
.hasMessageThat()
.contains("A database transition schedule for SIGNED_MARK_REVOCATION_LIST does not exist");
}
}
@@ -0,0 +1,111 @@
// Copyright 2021 The Nomulus Authors. All Rights Reserved.
//
// Licensed 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 google.registry.tools;
import static com.google.common.truth.Truth.assertThat;
import static google.registry.model.common.EntityGroupRoot.getCrossTldKey;
import static google.registry.model.ofy.ObjectifyService.ofy;
import static google.registry.persistence.transaction.TransactionManagerFactory.ofyTm;
import static google.registry.testing.DatabaseHelper.persistResource;
import static google.registry.util.DateTimeUtils.START_OF_TIME;
import com.google.common.collect.ImmutableSortedMap;
import com.googlecode.objectify.Key;
import google.registry.model.common.DatabaseTransitionSchedule;
import google.registry.model.common.DatabaseTransitionSchedule.PrimaryDatabase;
import google.registry.model.common.DatabaseTransitionSchedule.PrimaryDatabaseTransition;
import google.registry.model.common.DatabaseTransitionSchedule.TransitionId;
import google.registry.model.common.TimedTransitionProperty;
import org.joda.time.DateTime;
import org.joda.time.Duration;
import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.Test;
/** Unit tests for {@link SetDatabaseTransitionScheduleCommand}. */
public class SetDatabaseTransitionScheduleCommandTest
extends CommandTestCase<SetDatabaseTransitionScheduleCommand> {
Key<DatabaseTransitionSchedule> key;
@BeforeEach
void setup() {
key = Key.create(getCrossTldKey(), DatabaseTransitionSchedule.class, "test");
fakeClock.setTo(DateTime.parse("2020-12-01T00:00:00Z"));
}
@Test
void testSuccess_currentScheduleIsEmpty() throws Exception {
assertThat(ofy().load().key(key).now()).isNull();
runCommandForced(
"--transition_id=SIGNED_MARK_REVOCATION_LIST",
"--transition_schedule=1970-01-01T00:00:00.000Z=DATASTORE");
assertThat(
ofyTm()
.transact(
() ->
DatabaseTransitionSchedule.get(TransitionId.SIGNED_MARK_REVOCATION_LIST)
.get()
.getPrimaryDatabase()))
.isEqualTo(PrimaryDatabase.DATASTORE);
assertThat(command.prompt()).contains("Create DatabaseTransitionSchedule");
}
@Test
void testSuccess() throws Exception {
ImmutableSortedMap<DateTime, PrimaryDatabase> transitionMap =
ImmutableSortedMap.of(
START_OF_TIME,
PrimaryDatabase.DATASTORE,
fakeClock.nowUtc().minusDays(1),
PrimaryDatabase.CLOUD_SQL);
persistResource(
DatabaseTransitionSchedule.create(
TransitionId.SIGNED_MARK_REVOCATION_LIST,
TimedTransitionProperty.fromValueMap(transitionMap, PrimaryDatabaseTransition.class)));
assertThat(
DatabaseTransitionSchedule.get(TransitionId.SIGNED_MARK_REVOCATION_LIST)
.get()
.getDatabaseTransitions())
.isEqualTo(transitionMap);
runCommandForced(
"--transition_id=SIGNED_MARK_REVOCATION_LIST",
"--transition_schedule=1970-01-01T00:00:00.000Z=DATASTORE,2020-11-30T00:00:00.000Z=CLOUD_SQL,2020-12-06T00:00:00.000Z=DATASTORE");
ImmutableSortedMap<DateTime, PrimaryDatabase> retrievedTransitionMap =
ofyTm()
.transact(
() ->
DatabaseTransitionSchedule.get(TransitionId.SIGNED_MARK_REVOCATION_LIST)
.get()
.getDatabaseTransitions());
assertThat(retrievedTransitionMap)
.containsExactly(
START_OF_TIME,
PrimaryDatabase.DATASTORE,
fakeClock.nowUtc().minusDays(1),
PrimaryDatabase.CLOUD_SQL,
fakeClock.nowUtc().plusDays(5),
PrimaryDatabase.DATASTORE);
fakeClock.advanceBy(Duration.standardDays(5));
assertThat(
ofyTm()
.transact(
() ->
DatabaseTransitionSchedule.get(TransitionId.SIGNED_MARK_REVOCATION_LIST)
.get()
.getPrimaryDatabase()))
.isEqualTo(PrimaryDatabase.DATASTORE);
assertThat(command.prompt()).contains("Update DatabaseTransitionSchedule");
}
}
@@ -2,6 +2,7 @@ AllocationToken
Cancellation
ContactResource
Cursor
DatabaseTransitionSchedule
DomainBase
EntityGroupRoot
EppResourceIndex
@@ -78,6 +78,20 @@ class google.registry.model.common.Cursor {
google.registry.model.UpdateAutoTimestamp lastUpdateTime;
org.joda.time.DateTime cursorTime;
}
class google.registry.model.common.DatabaseTransitionSchedule {
@Id java.lang.String transitionId;
@Parent com.googlecode.objectify.Key<google.registry.model.common.EntityGroupRoot> parent;
google.registry.model.UpdateAutoTimestamp lastUpdateTime;
google.registry.model.common.TimedTransitionProperty<google.registry.model.common.DatabaseTransitionSchedule$PrimaryDatabase, google.registry.model.common.DatabaseTransitionSchedule$PrimaryDatabaseTransition> databaseTransitions;
}
enum google.registry.model.common.DatabaseTransitionSchedule$PrimaryDatabase {
CLOUD_SQL;
DATASTORE;
}
class google.registry.model.common.DatabaseTransitionSchedule$PrimaryDatabaseTransition {
google.registry.model.common.DatabaseTransitionSchedule$PrimaryDatabase primaryDatabase;
org.joda.time.DateTime transitionTime;
}
class google.registry.model.common.EntityGroupRoot {
@Id java.lang.String id;
google.registry.model.UpdateAutoTimestamp updateTimestamp;