1
0
mirror of https://github.com/google/nomulus synced 2026-05-20 06:41:51 +00:00

Compare commits

...

6 Commits

Author SHA1 Message Date
Lai Jiang
009fda67b7 Do not retry transactions inside Beam (#2318) 2024-02-05 18:40:56 +00:00
sarahcaseybot
e492936cec Add check for build_environment flag in updateReservedListCommand and updatePremiumListCommand (#2317)
* Add check for build_environment flag in updateReservedListCommand

* Do the same for premium list
2024-02-02 16:43:45 -05:00
Weimin Yu
d1d59c1afd Add a reminder for BEAM at Java version declaration (#2316) 2024-02-02 12:05:10 -05:00
Weimin Yu
7b786eaf1f Update dataflow java runtime to 17 (#2315) 2024-02-01 15:37:21 -05:00
Pavlo Tkach
45c5d12743 Add angular signals and computed to the console (#2308) 2024-02-01 14:15:05 -05:00
sarahcaseybot
73ab95bd9d Add Cloud Build sync job for reserved and premium lists (#2302)
* Change tld-update to db-object-updater

* rename sync_tlds.sh to sync_db_objects.sh

* Change to configured command name

* Change environment to sandbox explicitly for testing on alpha

* Add remaining object steps and change cloudbuild-tld-sync to cloudbuild-sync-db-objects

* Add build_environment flag

* Change order of command and directory

* Uncomment out reserved list part
2024-01-31 14:50:54 -05:00
28 changed files with 2553 additions and 15384 deletions

View File

@@ -382,6 +382,9 @@ subprojects {
apply from: "${rootDir.path}/java_common.gradle"
// When changing Java version here, be sure to update BEAM Java runtime:
// in core/build.gradle, search for `flex-template-base-image` and update
// the parameter value.
sourceCompatibility = JavaVersion.VERSION_17
targetCompatibility = JavaVersion.VERSION_17

File diff suppressed because it is too large Load Diff

View File

@@ -12,7 +12,7 @@
// See the License for the specific language governing permissions and
// limitations under the License.
import { AfterViewInit, Component, ViewChild } from '@angular/core';
import { AfterViewInit, Component, ViewChild, effect } from '@angular/core';
import { RegistrarService } from './registrar/registrar.service';
import { UserDataService } from './shared/services/userData.service';
import { GlobalLoaderService } from './shared/services/globalLoader.service';
@@ -36,11 +36,13 @@ export class AppComponent implements AfterViewInit {
protected globalLoader: GlobalLoaderService,
protected router: Router
) {
registrarService.activeRegistrarIdChange.subscribe(() => {
this.renderRouter = false;
setTimeout(() => {
this.renderRouter = true;
}, 400);
effect(() => {
if (registrarService.registrarId()) {
this.renderRouter = false;
setTimeout(() => {
this.renderRouter = true;
}, 400);
}
});
}

View File

@@ -52,7 +52,7 @@ export class DomainListService {
) {
return this.backendService
.getDomains(
this.registrarService.activeRegistrarId,
this.registrarService.registrarId(),
this.checkpointTime,
pageNumber,
resultsPerPage,

View File

@@ -23,8 +23,10 @@ export class BillingWidgetComponent {
constructor(public registrarService: RegistrarService) {}
public get driveFolderUrl(): string {
if (this.registrarService?.registrar.driveFolderId) {
return `https://drive.google.com/drive/folders/${this.registrarService?.registrar.driveFolderId}`;
if (this.registrarService.registrar()?.driveFolderId) {
return `https://drive.google.com/drive/folders/${
this.registrarService.registrar()?.driveFolderId
}`;
}
return '';
}

View File

@@ -12,9 +12,8 @@
// See the License for the specific language governing permissions and
// limitations under the License.
import { Component } from '@angular/core';
import { Component, effect } from '@angular/core';
import { ActivatedRoute, Router } from '@angular/router';
import { Subscription } from 'rxjs';
import { RegistrarService } from './registrar.service';
@@ -24,22 +23,15 @@ import { RegistrarService } from './registrar.service';
styleUrls: ['./emptyRegistrar.component.scss'],
})
export class EmptyRegistrar {
private registrarIdChangeSubscription?: Subscription;
constructor(
private route: ActivatedRoute,
protected registrarService: RegistrarService,
private router: Router
) {
this.registrarIdChangeSubscription =
registrarService.activeRegistrarIdChange.subscribe((newRegistrarId) => {
if (newRegistrarId) {
this.router.navigate([this.route.snapshot.paramMap.get('nextUrl')]);
}
});
}
ngOnDestroy() {
this.registrarIdChangeSubscription?.unsubscribe();
effect(() => {
if (registrarService.registrarId()) {
this.router.navigate([this.route.snapshot.paramMap.get('nextUrl')]);
}
});
}
}

View File

@@ -34,7 +34,7 @@ export class RegistrarGuard {
_: ActivatedRouteSnapshot,
state: RouterStateSnapshot
): Promise<boolean> | boolean {
if (this.registrarService.activeRegistrarId) {
if (this.registrarService.registrarId()) {
return true;
}
return this.router.navigate([

View File

@@ -12,8 +12,8 @@
// See the License for the specific language governing permissions and
// limitations under the License.
import { Injectable } from '@angular/core';
import { Observable, Subject, tap } from 'rxjs';
import { Injectable, computed, signal } from '@angular/core';
import { Observable, tap } from 'rxjs';
import { BackendService } from '../shared/services/backend.service';
import {
@@ -52,9 +52,11 @@ export interface Registrar {
providedIn: 'root',
})
export class RegistrarService implements GlobalLoader {
activeRegistrarId: string = '';
registrars: Registrar[] = [];
activeRegistrarIdChange: Subject<string> = new Subject<string>();
registrarId = signal<string>('');
registrars = signal<Registrar[]>([]);
registrar = computed<Registrar | undefined>(() =>
this.registrars().find((r) => r.registrarId === this.registrarId())
);
constructor(
private backend: BackendService,
@@ -67,22 +69,15 @@ export class RegistrarService implements GlobalLoader {
this.globalLoader.startGlobalLoader(this);
}
public get registrar(): Registrar {
return this.registrars.filter(
(r) => r.registrarId === this.activeRegistrarId
)[0];
}
public updateSelectedRegistrar(registrarId: string) {
this.activeRegistrarId = registrarId;
this.activeRegistrarIdChange.next(registrarId);
this.registrarId.set(registrarId);
}
public loadRegistrars(): Observable<Registrar[]> {
return this.backend.getRegistrars().pipe(
tap((registrars) => {
if (registrars) {
this.registrars = registrars;
this.registrars.set(registrars);
}
})
);

View File

@@ -5,20 +5,20 @@
routerLinkActive="active"
*ngIf="isMobile; else desktop"
>
{{ registrarService.activeRegistrarId || "Select registrar" }}
{{ registrarService.registrarId() || "Select registrar" }}
<mat-icon>open_in_new</mat-icon>
</button>
<ng-template #desktop>
<mat-form-field class="mat-form-field-density-5" appearance="fill">
<mat-label>Registrar</mat-label>
<mat-select
[ngModel]="registrarService.activeRegistrarId"
[ngModel]="registrarService.registrarId()"
(selectionChange)="
registrarService.updateSelectedRegistrar($event.value)
"
>
<mat-option
*ngFor="let registrar of registrarService.registrars"
*ngFor="let registrar of registrarService.registrars()"
[value]="registrar.registrarId"
>
{{ registrar.registrarId }}

View File

@@ -87,7 +87,7 @@ export class RegistrarComponent {
constructor(protected registrarService: RegistrarService) {
this.dataSource = new MatTableDataSource<Registrar>(
registrarService.registrars
registrarService.registrars()
);
}

View File

@@ -1,41 +1,41 @@
<div *ngIf="loading" class="contact__loading">
@if (loading) {
<div class="contact__loading">
<mat-progress-bar mode="indeterminate"></mat-progress-bar>
</div>
<div *ngIf="!loading">
<div
class="contact__empty-contacts"
*ngIf="contactService.contacts.length === 0"
>
} @else {
<div>
@if (contactService.contacts().length === 0) {
<div class="contact__empty-contacts">
<mat-icon class="contact__empty-contacts-icon secondary-text"
>apps_outage</mat-icon
>
<h1>No contacts found</h1>
</div>
<div *ngFor="let group of groupedData">
<div class="contact__cards-wrapper" *ngIf="group.contacts.length">
<h3>{{ group.label }}s</h3>
<mat-divider></mat-divider>
<div class="contact__cards">
<mat-card class="contact__card" *ngFor="let contact of group.contacts">
<mat-card-title>{{ contact.name }}</mat-card-title>
<p *ngIf="contact.phoneNumber">{{ contact.phoneNumber }}</p>
<p *ngIf="contact.emailAddress">{{ contact.emailAddress }}</p>
<mat-card-actions class="contact__card-actions">
<button
mat-button
color="primary"
(click)="openDetails($event, contact)"
>
<mat-icon>edit</mat-icon>Edit
</button>
<button mat-button color="accent" (click)="deleteContact(contact)">
<mat-icon>delete</mat-icon>Delete
</button>
</mat-card-actions>
</mat-card>
</div>
} @else { @for (group of groupedContacts(); track group.emailAddress) {
<div class="contact__cards-wrapper">
<h3>{{ group.label }}s</h3>
<mat-divider></mat-divider>
<div class="contact__cards">
<mat-card class="contact__card" *ngFor="let contact of group.contacts">
<mat-card-title>{{ contact.name }}</mat-card-title>
<p *ngIf="contact.phoneNumber">{{ contact.phoneNumber }}</p>
<p *ngIf="contact.emailAddress">{{ contact.emailAddress }}</p>
<mat-card-actions class="contact__card-actions">
<button
mat-button
color="primary"
(click)="openDetails($event, contact)"
>
<mat-icon>edit</mat-icon>Edit
</button>
<button mat-button color="accent" (click)="deleteContact(contact)">
<mat-icon>delete</mat-icon>Delete
</button>
</mat-card-actions>
</mat-card>
</div>
</div>
} }
<div class="contact__actions">
<button mat-raised-button color="primary" (click)="openCreateNew($event)">
<mat-icon>add</mat-icon>Create a Contact
@@ -45,3 +45,4 @@
#contactDetailsWrapper
></app-dialog-bottom-sheet-wrapper>
</div>
}

View File

@@ -12,7 +12,7 @@
// See the License for the specific language governing permissions and
// limitations under the License.
import { Component, ViewChild } from '@angular/core';
import { Component, ViewChild, computed } from '@angular/core';
import { Contact, ContactService } from './contact.service';
import { HttpErrorResponse } from '@angular/common/http';
import { MatSnackBar } from '@angular/material/snack-bar';
@@ -70,9 +70,9 @@ export class ContactDetailsDialogComponent implements DialogBottomSheetContent {
init(params: ContactDetailsParams) {
this.params = params;
this.contactIndex = this.contactService.contacts.findIndex(
(c) => c === params.data.contact
);
this.contactIndex = this.contactService
.contacts()
.findIndex((c) => c === params.data.contact);
this.contact = JSON.parse(JSON.stringify(params.data.contact));
}
@@ -115,6 +115,16 @@ export class ContactDetailsDialogComponent implements DialogBottomSheetContent {
})
export default class ContactComponent {
public static PATH = 'contact';
public groupedContacts = computed(() => {
return this.contactService.contacts().reduce((acc, contact) => {
contact.types.forEach((contactType) => {
acc
.find((group: GroupedContacts) => group.value === contactType)
?.contacts.push(contact);
});
return acc;
}, JSON.parse(JSON.stringify(contactTypes)));
});
@ViewChild('contactDetailsWrapper')
detailsComponentWrapper!: DialogBottomSheetWrapper;
@@ -131,17 +141,6 @@ export default class ContactComponent {
});
}
public get groupedData() {
return this.contactService.contacts?.reduce((acc, contact) => {
contact.types.forEach((type) => {
acc
.find((group: GroupedContacts) => group.value === type)
?.contacts.push(contact);
});
return acc;
}, JSON.parse(JSON.stringify(contactTypes)));
}
deleteContact(contact: Contact) {
if (confirm(`Please confirm contact ${contact.name} delete`)) {
this.contactService.deleteContact(contact).subscribe({

View File

@@ -12,7 +12,7 @@
// See the License for the specific language governing permissions and
// limitations under the License.
import { Injectable } from '@angular/core';
import { Injectable, signal } from '@angular/core';
import { Observable, tap } from 'rxjs';
import { RegistrarService } from 'src/app/registrar/registrar.service';
import { BackendService } from 'src/app/shared/services/backend.service';
@@ -33,7 +33,7 @@ export interface Contact {
providedIn: 'root',
})
export class ContactService {
contacts: Contact[] = [];
contacts = signal<Contact[]>([]);
constructor(
private backend: BackendService,
@@ -42,39 +42,37 @@ export class ContactService {
// TODO: Come up with a better handling for registrarId
fetchContacts(): Observable<Contact[]> {
return this.backend
.getContacts(this.registrarService.activeRegistrarId)
.pipe(
tap((contacts = []) => {
this.contacts = contacts;
})
);
return this.backend.getContacts(this.registrarService.registrarId()).pipe(
tap((contacts = []) => {
this.contacts.set(contacts);
})
);
}
saveContacts(contacts: Contact[]): Observable<Contact[]> {
return this.backend
.postContacts(this.registrarService.activeRegistrarId, contacts)
.postContacts(this.registrarService.registrarId(), contacts)
.pipe(
tap((_) => {
this.contacts = contacts;
this.contacts.set(contacts);
})
);
}
updateContact(index: number, contact: Contact) {
const newContacts = this.contacts.map((c, i) =>
const newContacts = this.contacts().map((c, i) =>
i === index ? contact : c
);
return this.saveContacts(newContacts);
}
addContact(contact: Contact) {
const newContacts = this.contacts.concat([contact]);
const newContacts = this.contacts().concat([contact]);
return this.saveContacts(newContacts);
}
deleteContact(contact: Contact) {
const newContacts = this.contacts.filter((c) => c !== contact);
const newContacts = this.contacts().filter((c) => c !== contact);
return this.saveContacts(newContacts);
}
}

View File

@@ -40,7 +40,7 @@ export default class SecurityComponent {
private _snackBar: MatSnackBar,
public registrarService: RegistrarService
) {
this.dataSource = apiToUiConverter(this.registrarService.registrar);
this.dataSource = apiToUiConverter(this.registrarService.registrar());
}
enableEdit() {
@@ -76,6 +76,6 @@ export default class SecurityComponent {
}
resetDataSource() {
this.dataSource = apiToUiConverter(this.registrarService.registrar);
this.dataSource = apiToUiConverter(this.registrarService.registrar());
}
}

View File

@@ -64,7 +64,7 @@ export class SecurityService {
saveChanges(newSecuritySettings: SecuritySettings) {
return this.backend
.postSecuritySettings(
this.registrarService.activeRegistrarId,
this.registrarService.registrarId(),
uiToApiConverter(newSecuritySettings)
)
.pipe(

View File

@@ -776,7 +776,7 @@ if (environment == 'alpha') {
gs://${gcpProject}-deploy/live/beam/${metaDataBaseName} \
--image-gcr-path ${imageName}:live \
--sdk-language JAVA \
--flex-template-base-image JAVA11 \
--flex-template-base-image JAVA17 \
--metadata-file ${projectDir}/src/main/resources/${metaData} \
--jar ${uberJarName} \
--env FLEX_TEMPLATE_JAVA_MAIN_CLASS=${mainClass} \

View File

@@ -209,10 +209,18 @@ public final class RegistryJpaIO {
@ProcessElement
public void processElement(OutputReceiver<T> outputReceiver) {
tm().transact(
// Note the use of no-retry transaction here. The results from the query are streamed to the
// output receiver inside the transaction, which cannot be rolled back in case of a retry,
// which in turn results in duplicate elements. If we try to pass the results to the output
// receiver outside the transaction, they have to be materialized into a list containing all
// the elements (without resorting to manual pagination) and greatly decrease the
// parallelism of the pipeline.
tm().transactNoRetry(
() -> {
query.stream().map(resultMapper::apply).forEach(outputReceiver::output);
});
return null;
},
null);
}
}
}

View File

@@ -203,6 +203,7 @@ public class JpaTransactionManagerImpl implements JpaTransactionManager {
return transact(work, null);
}
@Override
public <T> T transactNoRetry(
Callable<T> work, @Nullable TransactionIsolationLevel isolationLevel) {
if (inTransaction()) {

View File

@@ -63,6 +63,18 @@ public interface TransactionManager {
*/
<T> T transact(Callable<T> work, TransactionIsolationLevel isolationLevel);
/**
* Executes the work in a transaction at the given {@link TransactionIsolationLevel} and returns
* the result, without retrying upon retryable exceptions.
*
* <p>This method should only be used when the transaction contains side effects that are not
* rolled back by the transaction manager, for example in {@link
* google.registry.beam.common.RegistryJpaIO} where the results from a query are streamed to the
* next transformation inside a transaction, as the result stream has to materialize to a list
* outside a transaction and doing so would greatly affect the parallelism of the pipeline.
*/
<T> T transactNoRetry(Callable<T> work, TransactionIsolationLevel isolationLevel);
/**
* Executes the work in a (potentially wrapped) transaction and returns the result.
*

View File

@@ -48,11 +48,9 @@ class UpdatePremiumListCommand extends CreateOrUpdatePremiumListCommand {
@Override
protected String prompt() throws Exception {
// TODO(sarahbot): uncomment once go/r3pr/2292 is deployed
// checkArgument(
// !RegistryToolEnvironment.get().equals(RegistryToolEnvironment.PRODUCTION) || buildEnv,
// "The --build_environment flag must be used when running update_premium_list in
// production");
checkArgument(
!RegistryToolEnvironment.get().equals(RegistryToolEnvironment.PRODUCTION) || buildEnv,
"The --build_environment flag must be used when running update_premium_list in production");
name = Strings.isNullOrEmpty(name) ? convertFilePathToName(inputFile) : name;
PremiumList existingList =
PremiumListDao.getLatestRevision(name)

View File

@@ -14,6 +14,7 @@
package google.registry.tools;
import static com.google.common.base.Preconditions.checkArgument;
import static google.registry.util.DiffUtils.prettyPrintEntityDeepDiff;
import static google.registry.util.ListNamingUtils.convertFilePathToName;
import static java.nio.charset.StandardCharsets.UTF_8;
@@ -47,11 +48,10 @@ final class UpdateReservedListCommand extends CreateOrUpdateReservedListCommand
@Override
protected String prompt() throws Exception {
// TODO(sarahbot): uncomment once go/r3pr/2292 is deployed
// checkArgument(
// !RegistryToolEnvironment.get().equals(RegistryToolEnvironment.PRODUCTION) || buildEnv,
// "The --build_environment flag must be used when running update_reserved_list in"
// + " production");
checkArgument(
!RegistryToolEnvironment.get().equals(RegistryToolEnvironment.PRODUCTION) || buildEnv,
"The --build_environment flag must be used when running update_reserved_list in"
+ " production");
name = Strings.isNullOrEmpty(name) ? convertFilePathToName(input) : name;
ReservedList existingReservedList =
ReservedList.get(name)

View File

@@ -206,39 +206,38 @@ class UpdatePremiumListCommandTest<C extends UpdatePremiumListCommand>
.containsExactly(PremiumEntry.create(0L, new BigDecimal("9090.00"), "doge"));
}
// TODO(sarahbot): uncomment once go/r3pr/2292 is deployed
// @Test
// void testFailure_runCommandOnProduction_noFlag() throws Exception {
// File tmpFile = tmpDir.resolve(String.format("%s.txt", TLD_TEST)).toFile();
// String newPremiumListData = "eth,USD 9999";
// Files.asCharSink(tmpFile, UTF_8).write(newPremiumListData);
// IllegalArgumentException thrown =
// assertThrows(
// IllegalArgumentException.class,
// () ->
// runCommandInEnvironment(
// RegistryToolEnvironment.PRODUCTION,
// "--name=" + TLD_TEST,
// "--input=" + Paths.get(tmpFile.getPath())));
// assertThat(thrown.getMessage())
// .isEqualTo(
// "The --build_environment flag must be used when running update_premium_list in"
// + " production");
// }
//
// @Test
// void testSuccess_runCommandOnProduction_buildEnvFlag() throws Exception {
// File tmpFile = tmpDir.resolve(String.format("%s.txt", TLD_TEST)).toFile();
// String newPremiumListData = "eth,USD 9999";
// Files.asCharSink(tmpFile, UTF_8).write(newPremiumListData);
// runCommandInEnvironment(
// RegistryToolEnvironment.PRODUCTION,
// "--name=" + TLD_TEST,
// "--input=" + Paths.get(tmpFile.getPath()),
// "--build_environment",
// "-f");
// assertThat(PremiumListDao.loadAllPremiumEntries(TLD_TEST))
// .comparingElementsUsing(immutableObjectCorrespondence("revisionId"))
// .containsExactly(PremiumEntry.create(0L, new BigDecimal("9999.00"), "eth"));
// }
@Test
void testFailure_runCommandOnProduction_noFlag() throws Exception {
File tmpFile = tmpDir.resolve(String.format("%s.txt", TLD_TEST)).toFile();
String newPremiumListData = "eth,USD 9999";
Files.asCharSink(tmpFile, UTF_8).write(newPremiumListData);
IllegalArgumentException thrown =
assertThrows(
IllegalArgumentException.class,
() ->
runCommandInEnvironment(
RegistryToolEnvironment.PRODUCTION,
"--name=" + TLD_TEST,
"--input=" + Paths.get(tmpFile.getPath())));
assertThat(thrown.getMessage())
.isEqualTo(
"The --build_environment flag must be used when running update_premium_list in"
+ " production");
}
@Test
void testSuccess_runCommandOnProduction_buildEnvFlag() throws Exception {
File tmpFile = tmpDir.resolve(String.format("%s.txt", TLD_TEST)).toFile();
String newPremiumListData = "eth,USD 9999";
Files.asCharSink(tmpFile, UTF_8).write(newPremiumListData);
runCommandInEnvironment(
RegistryToolEnvironment.PRODUCTION,
"--name=" + TLD_TEST,
"--input=" + Paths.get(tmpFile.getPath()),
"--build_environment",
"-f");
assertThat(PremiumListDao.loadAllPremiumEntries(TLD_TEST))
.comparingElementsUsing(immutableObjectCorrespondence("revisionId"))
.containsExactly(PremiumEntry.create(0L, new BigDecimal("9999.00"), "eth"));
}
}

View File

@@ -153,40 +153,39 @@ class UpdateReservedListCommandTest
assertThat(reservedList.getReservationInList("helicopter")).hasValue(FULLY_BLOCKED);
}
// TODO(sarahbot): uncomment once go/r3pr/2292 is deployed
// @Test
// void testFailure_runCommandOnProduction_noFlag() throws Exception {
// IllegalArgumentException thrown =
// assertThrows(
// IllegalArgumentException.class,
// () ->
// runCommandInEnvironment(
// RegistryToolEnvironment.PRODUCTION,
// "--name=xn--q9jyb4c_common-reserved",
// "--input=" + reservedTermsPath));
// assertThat(thrown.getMessage())
// .isEqualTo(
// "The --build_environment flag must be used when running update_reserved_list in"
// + " production");
// }
//
// @Test
// void testSuccess_runCommandOnProduction_buildEnvFlag() throws Exception {
// runCommandInEnvironment(
// RegistryToolEnvironment.PRODUCTION,
// "--name=xn--q9jyb4c_common-reserved",
// "--input=" + reservedTermsPath,
// "--build_environment",
// "-f");
// assertThat(ReservedList.get("xn--q9jyb4c_common-reserved")).isPresent();
// ReservedList reservedList = ReservedList.get("xn--q9jyb4c_common-reserved").get();
// assertThat(reservedList.getReservedListEntries()).hasSize(2);
// assertThat(reservedList.getReservationInList("baddies")).hasValue(FULLY_BLOCKED);
// assertThat(reservedList.getReservationInList("ford")).hasValue(FULLY_BLOCKED);
// assertThat(reservedList.getReservationInList("helicopter")).isEmpty();
// assertInStdout("Update reserved list for xn--q9jyb4c_common-reserved?");
// assertInStdout("helicopter: helicopter,FULLY_BLOCKED -> null");
// assertInStdout("baddies: null -> baddies,FULLY_BLOCKED");
// assertInStdout("ford: null -> ford,FULLY_BLOCKED # random comment");
// }
@Test
void testFailure_runCommandOnProduction_noFlag() throws Exception {
IllegalArgumentException thrown =
assertThrows(
IllegalArgumentException.class,
() ->
runCommandInEnvironment(
RegistryToolEnvironment.PRODUCTION,
"--name=xn--q9jyb4c_common-reserved",
"--input=" + reservedTermsPath));
assertThat(thrown.getMessage())
.isEqualTo(
"The --build_environment flag must be used when running update_reserved_list in"
+ " production");
}
@Test
void testSuccess_runCommandOnProduction_buildEnvFlag() throws Exception {
runCommandInEnvironment(
RegistryToolEnvironment.PRODUCTION,
"--name=xn--q9jyb4c_common-reserved",
"--input=" + reservedTermsPath,
"--build_environment",
"-f");
assertThat(ReservedList.get("xn--q9jyb4c_common-reserved")).isPresent();
ReservedList reservedList = ReservedList.get("xn--q9jyb4c_common-reserved").get();
assertThat(reservedList.getReservedListEntries()).hasSize(2);
assertThat(reservedList.getReservationInList("baddies")).hasValue(FULLY_BLOCKED);
assertThat(reservedList.getReservationInList("ford")).hasValue(FULLY_BLOCKED);
assertThat(reservedList.getReservationInList("helicopter")).isEmpty();
assertInStdout("Update reserved list for xn--q9jyb4c_common-reserved?");
assertInStdout("helicopter: helicopter,FULLY_BLOCKED -> null");
assertInStdout("baddies: null -> baddies,FULLY_BLOCKED");
assertInStdout("ford: null -> ford,FULLY_BLOCKED # random comment");
}
}

View File

@@ -91,7 +91,7 @@ steps:
--format="get(digest)" --filter="tags = ${TAG_NAME}")
sed -i s/'prober_cert_updater:latest'/prober_cert_updater@$digest/g \
release/cloudbuild-renew-prober-certs-*.yaml
# Build the tld_updater image and upload it to GCR. This image extends
# Build the db_object_updater image and upload it to GCR. This image extends
# from the `builder` and the nomulus.jar built earlier.
- name: 'gcr.io/cloud-builders/docker'
entrypoint: /bin/bash
@@ -101,14 +101,14 @@ steps:
set -e
# The nomulus jar is not under the working dir. Must be copied over.
cp ../../output/nomulus.jar .
docker build -t gcr.io/${PROJECT_ID}/tld_updater:${TAG_NAME} \
docker build -t gcr.io/${PROJECT_ID}/db_object_updater:${TAG_NAME} \
--build-arg TAG_NAME=${TAG_NAME} --build-arg PROJECT_ID=${PROJECT_ID} .
docker tag gcr.io/${PROJECT_ID}/tld_updater:${TAG_NAME} \
gcr.io/${PROJECT_ID}/tld_updater:latest
docker push gcr.io/${PROJECT_ID}/tld_updater:latest
docker push gcr.io/${PROJECT_ID}/tld_updater:${TAG_NAME}
dir: 'release/tld-updater/'
# Update the tld_updater image digest in relevant GCB files.
docker tag gcr.io/${PROJECT_ID}/db_object_updater:${TAG_NAME} \
gcr.io/${PROJECT_ID}/db_object_updater:latest
docker push gcr.io/${PROJECT_ID}/db_object_updater:latest
docker push gcr.io/${PROJECT_ID}/db_object_updater:${TAG_NAME}
dir: 'release/db-object-updater/'
# Update the db_object_updater image digest in relevant GCB files.
- name: 'gcr.io/${PROJECT_ID}/builder:latest'
entrypoint: /bin/bash
args:
@@ -116,10 +116,10 @@ steps:
- |
set -e
digest=$(gcloud container images list-tags \
gcr.io/${PROJECT_ID}/tld_updater \
gcr.io/${PROJECT_ID}/db_object_updater \
--format="get(digest)" --filter="tags = ${TAG_NAME}")
sed -i s/'tld_updater:latest'/tld_updater@$digest/g \
release/cloudbuild-tld-sync-*.yaml
sed -i s/'db_object_updater:latest'/db_object_updater@$digest/g \
release/cloudbuild-sync-db-objects-*.yaml
# Build and stage Dataflow Flex templates.
- name: 'gcr.io/${PROJECT_ID}/builder:latest'
entrypoint: /bin/bash
@@ -190,7 +190,7 @@ artifacts:
- 'release/cloudbuild-renew-prober-certs-*.yaml'
- 'release/cloudbuild-schema-deploy-*.yaml'
- 'release/cloudbuild-schema-verify-*.yaml'
- 'release/cloudbuild-tld-sync-*.yaml'
- 'release/cloudbuild-sync-db-objects-*.yaml'
timeout: 7200s
options:

View File

@@ -139,9 +139,9 @@ steps:
gcloud container images list-tags \
gcr.io/${PROJECT_ID}/prober_cert_updater \
--format='get(digest)' --filter='tags = ${TAG_NAME}')
tld_updater_digest=$( \
db_object_updater_digest=$( \
gcloud container images list-tags \
gcr.io/${PROJECT_ID}/tld_updater \
gcr.io/${PROJECT_ID}/db_object_updater \
--format='get(digest)' --filter='tags = ${TAG_NAME}')
sed -i s/builder:latest/builder@$builder_digest/g \
release/cloudbuild-schema-deploy.yaml
@@ -150,7 +150,7 @@ steps:
sed -i s/builder:latest/builder@$builder_digest/g \
release/cloudbuild-renew-prober-certs.yaml
sed -i s/builder:latest/builder@$builder_digest/g \
release/cloudbuild-tld-sync.yaml
release/cloudbuild-sync-db-objects.yaml
sed -i s/schema_deployer:latest/schema_deployer@$schema_deployer_digest/g \
release/cloudbuild-schema-deploy.yaml
sed -i s/schema_verifier:latest/schema_verifier@$schema_verifier_digest/g \
@@ -163,8 +163,8 @@ steps:
> release/cloudbuild-schema-verify-${environment}.yaml
sed s/'$${_ENV}'/${environment}/g release/cloudbuild-renew-prober-certs.yaml \
> release/cloudbuild-renew-prober-certs-${environment}.yaml
sed s/'$${_ENV}'/${environment}/g release/cloudbuild-tld-sync.yaml \
> release/cloudbuild-tld-sync-${environment}.yaml
sed s/'$${_ENV}'/${environment}/g release/cloudbuild-sync-db-objects.yaml \
> release/cloudbuild-sync-db-objects-${environment}.yaml
done
# Upload the gradle binary to GCS if it does not exist and point URL in gradle wrapper to it.
- name: 'gcr.io/cloud-builders/gsutil'

View File

@@ -1,7 +1,8 @@
# This will sync the Tld configurations in the internal repo with the Tld objects in the database.
# This will sync the configuration files in the internal repo with their
# corresponding objects in the database.
#
# To manually trigger a build on GCB, run:
# gcloud builds submit --config cloudbuild-tld-sync.yaml --substitutions \
# gcloud builds submit --config cloudbuild-sync-db-objects.yaml --substitutions \
# _INTERNAL_REPO_URL=[URL] ..
#
# To trigger a build automatically, follow the instructions below and add a trigger:
@@ -34,11 +35,26 @@ steps:
--secret nomulus-tool-cloudbuild-credential \
> nomulus_tool_credential.json
# Configure the TLDs using the stored configuration files in the internal repo
- name: 'gcr.io/$PROJECT_ID/tld_updater:latest'
- name: 'gcr.io/$PROJECT_ID/db_object_updater:latest'
args:
- ${_ENV}
- ./nomulus_tool_credential.json
- configure_tld
- nomulus-internal/core/src/main/java/google/registry/config/files/tld/
# Configure the premium lists using the stored configuration files in the internal repo
- name: 'gcr.io/$PROJECT_ID/db_object_updater:latest'
args:
- ${_ENV}
- ./nomulus_tool_credential.json
- update_premium_list
- nomulus-internal/core/src/main/java/google/registry/config/files/premium/
# Configure the reserved lists using the stored configuration files in the internal repo
- name: 'gcr.io/$PROJECT_ID/db_object_updater:latest'
args:
- ${_ENV}
- ./nomulus_tool_credential.json
- update_reserved_list
- nomulus-internal/core/src/main/java/google/registry/config/files/reserved/
timeout: 7200s
options:

View File

@@ -17,6 +17,6 @@ ARG TAG_NAME
FROM gcr.io/${PROJECT_ID}/builder:${TAG_NAME}
COPY nomulus.jar /
COPY sync_tlds.sh /usr/local/bin
COPY sync_db_objects.sh /usr/local/bin
ENTRYPOINT [ "bash", "sync_tlds.sh" ]
ENTRYPOINT [ "bash", "sync_db_objects.sh" ]

View File

@@ -13,23 +13,25 @@
# See the License for the specific language governing permissions and
# limitations under the License.
# Sync the TLD configuration files from the internal repo with the Tld object
# in the database. Loops through the Tld configuration files and runs the configure_tld command
# with the file.
# Sync the configuration files in the internal repo with the objects in the
# database. Loops through the configuration files in the inputted directory and
# runs the passed in nomulus update command with the file.
# - env: The Nomulus environment, production, sandbox, etc.
# - tools_credential: The credential (.json) needed to run the nomulus command.
# - nomulus_command: The nomulus command to run.
# - config_file_directory: The internal directory storing the TLD config files.
set -e
if [ "$#" -ne 3 ]; then
echo "Expecting three parameters in order: env tools_credential config_file_directory"
if [ "$#" -ne 4 ]; then
echo "Expecting four parameters in order: env tools_credential nomulus_command config_file_directory"
exit 1
fi
nomulus_env="${1}"
tools_credential="${2}"
config_file_directory="${3}"
nomulus_command="${3}"
config_file_directory="${4}"
echo ${config_file_directory}
@@ -37,5 +39,5 @@ for FILE in ${config_file_directory}/${nomulus_env}/*; do
echo $FILE
java -jar /nomulus.jar -e "${nomulus_env}" \
--credential "${tools_credential}" \
configure_tld -i $FILE --force --build_environment
"${nomulus_command}" -i $FILE --force --build_environment
done