mirror of
https://github.com/google/nomulus
synced 2026-05-20 06:41:51 +00:00
Compare commits
6 Commits
nomulus-20
...
nomulus-20
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
009fda67b7 | ||
|
|
e492936cec | ||
|
|
d1d59c1afd | ||
|
|
7b786eaf1f | ||
|
|
45c5d12743 | ||
|
|
73ab95bd9d |
@@ -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
|
||||
|
||||
|
||||
17478
console-webapp/package-lock.json
generated
17478
console-webapp/package-lock.json
generated
File diff suppressed because it is too large
Load Diff
@@ -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);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
|
||||
@@ -52,7 +52,7 @@ export class DomainListService {
|
||||
) {
|
||||
return this.backendService
|
||||
.getDomains(
|
||||
this.registrarService.activeRegistrarId,
|
||||
this.registrarService.registrarId(),
|
||||
this.checkpointTime,
|
||||
pageNumber,
|
||||
resultsPerPage,
|
||||
|
||||
@@ -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 '';
|
||||
}
|
||||
|
||||
@@ -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')]);
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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([
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
})
|
||||
);
|
||||
|
||||
@@ -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 }}
|
||||
|
||||
@@ -87,7 +87,7 @@ export class RegistrarComponent {
|
||||
|
||||
constructor(protected registrarService: RegistrarService) {
|
||||
this.dataSource = new MatTableDataSource<Registrar>(
|
||||
registrarService.registrars
|
||||
registrarService.registrars()
|
||||
);
|
||||
}
|
||||
|
||||
|
||||
@@ -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>
|
||||
}
|
||||
|
||||
@@ -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({
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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());
|
||||
}
|
||||
}
|
||||
|
||||
@@ -64,7 +64,7 @@ export class SecurityService {
|
||||
saveChanges(newSecuritySettings: SecuritySettings) {
|
||||
return this.backend
|
||||
.postSecuritySettings(
|
||||
this.registrarService.activeRegistrarId,
|
||||
this.registrarService.registrarId(),
|
||||
uiToApiConverter(newSecuritySettings)
|
||||
)
|
||||
.pipe(
|
||||
|
||||
@@ -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} \
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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()) {
|
||||
|
||||
@@ -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.
|
||||
*
|
||||
|
||||
@@ -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)
|
||||
|
||||
@@ -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)
|
||||
|
||||
@@ -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"));
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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");
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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:
|
||||
|
||||
@@ -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'
|
||||
|
||||
@@ -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:
|
||||
@@ -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" ]
|
||||
@@ -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
|
||||
Reference in New Issue
Block a user