All Downloads are FREE. Search and download functionalities are using the official Maven repository.

io.datarouter.aws.rds.web.AuroraInstancesHandler Maven / Gradle / Ivy

/*
 * Copyright © 2009 HotPads ([email protected])
 *
 * 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 io.datarouter.aws.rds.web;

import static j2html.TagCreator.a;
import static j2html.TagCreator.div;
import static j2html.TagCreator.each;
import static j2html.TagCreator.h2;
import static j2html.TagCreator.i;
import static j2html.TagCreator.table;
import static j2html.TagCreator.tbody;
import static j2html.TagCreator.td;
import static j2html.TagCreator.tr;

import java.util.ArrayList;
import java.util.Collection;
import java.util.List;
import java.util.Map;

import javax.inject.Inject;

import org.apache.http.client.utils.URIBuilder;

import io.datarouter.aws.rds.config.DatarouterAwsPaths;
import io.datarouter.aws.rds.config.DatarouterAwsRdsConfigSettings;
import io.datarouter.aws.rds.service.AuroraDnsService;
import io.datarouter.aws.rds.service.AuroraDnsService.DnsHostEntryDto;
import io.datarouter.aws.rds.service.DatabaseAdministrationConfiguration;
import io.datarouter.aws.rds.service.RdsService;
import io.datarouter.instrumentation.changelog.ChangelogRecorder;
import io.datarouter.instrumentation.changelog.ChangelogRecorder.DatarouterChangelogDtoBuilder;
import io.datarouter.scanner.Scanner;
import io.datarouter.util.Require;
import io.datarouter.web.handler.BaseHandler;
import io.datarouter.web.handler.mav.Mav;
import io.datarouter.web.handler.mav.imp.InContextRedirectMav;
import io.datarouter.web.handler.types.Param;
import io.datarouter.web.html.j2html.J2HtmlTable;
import io.datarouter.web.html.j2html.bootstrap4.Bootstrap4PageFactory;
import j2html.tags.DomContent;
import j2html.tags.specialized.DivTag;
import j2html.tags.specialized.TrTag;

public class AuroraInstancesHandler extends BaseHandler{

	private static final String P_clientName = "clientName";
	private static final String P_clusterName = "clusterName";
	private static final String P_region = "region";


	@Inject
	private AuroraDnsService dnsService;
	@Inject
	private DatarouterAwsPaths paths;
	@Inject
	private RdsService rdsService;
	@Inject
	private DatarouterAwsRdsConfigSettings rdsSettings;
	@Inject
	private Bootstrap4PageFactory pageFactory;
	@Inject
	private DatabaseAdministrationConfiguration config;
	@Inject
	private ChangelogRecorder changelogRecorder;

	@Handler
	public Mav inspectClientUrl(){
		List otherReaderInstances = new ArrayList<>();
		List clientsMissingOtherInstances = new ArrayList<>();

		Map dnsEntriesForClients = dnsService.getDnsEntryForClients();
		for(DnsHostEntryDto dnsEntry : dnsEntriesForClients.values()){
			if(dnsEntry.isWriter()){
				String region = dnsEntry.getRegion();
				DnsHostEntryDto otherEntry = dnsEntriesForClients.get(dnsEntry.getClientName()
						+ AuroraDnsService.OTHER);
				if(otherEntry == null){
					clientsMissingOtherInstances.add(new OtherClientDto(dnsEntry.getClientName(),
							dnsEntry.getClusterName(), region));
				}else{
					otherReaderInstances.add(otherEntry);
				}

			}
		}

		List fragments = new ArrayList<>();
		List dnsEntries = Scanner.of(dnsEntriesForClients.values())
				.exclude(dnsEntry -> dnsEntry.isOther())
				.list();
		fragments.add(makeAuroraClientsTable("Aurora Clients", dnsEntries, false));
		if(otherReaderInstances.size() != 0){
			fragments.add(makeAuroraClientsTable("Aurora Other Instances", otherReaderInstances, true));
		}
		if(clientsMissingOtherInstances.size() != 0){
			fragments.add(makeCreateOtherSection(clientsMissingOtherInstances));
		}
		DivTag content = div(each(fragments.stream()))
				.withClass("container my-4");
		return pageFactory.startBuilder(request)
				.withTitle("Aurora Clients")
				.withContent(content)
				.buildMav();
	}

	@Handler
	public Mav createOtherInstance(@Param(P_clientName) String clientName, @Param(P_clusterName) String clusterName,
			@Param(P_region) String region){
		rdsService.createOtherInstance(rdsSettings.dbPrefix.get() + clusterName, region);
		config.addOtherDatabaseDns(rdsSettings.dbPrefix.get() + clientName, region);
		var dto = new DatarouterChangelogDtoBuilder(
				"AuroraClients",
				clientName,
				"created other instance for " + clusterName,
				getSessionInfo().getNonEmptyUsernameOrElse(""))
				.build();
		changelogRecorder.record(dto);
		return new InContextRedirectMav(request, paths.datarouter.auroraInstances.inspectClientUrl.toSlashedString());
	}

	private DivTag makeAuroraClientsTable(String header, Collection rows,
			boolean showDeleteOption){
		String contextPath = request.getContextPath();
		var h2 = h2(header);
		var table = new J2HtmlTable()
				.withClasses("sortable table table-sm table-striped my-4 border")
				.withHtmlColumn("Client name", row -> {
					if(row.isReaderPointedToWriter() || row.isReaderPointedToWrongReader()){
						return td(row.getClientName()).withClass("table-danger");
					}
					return td(row.getClientName());
				})
				.withColumn("Hostname", DnsHostEntryDto::getHostname)
				.withColumn("Cluster hostname", DnsHostEntryDto::getClusterHostname)
				.withColumn("Cluster name", DnsHostEntryDto::getClusterName)
				.withColumn("Replcation role", DnsHostEntryDto::getReplicationRole)
				.withColumn("Instance hostname", DnsHostEntryDto::getInstanceHostname)
				.withColumn("IP", DnsHostEntryDto::getIp)
				.withHtmlColumn("X", row -> {
					if(showDeleteOption){
						var trashIcon = i().withClass("fas fa-trash");
						return td(a(trashIcon).withHref(getDeleteOtherClientUri(contextPath, row)));
					}
					return td("");

				})
				.withCaption("Total " + rows.size())
				.build(rows);
		return div(h2, table)
				.withClass("container my-4");
	}

	private DivTag makeCreateOtherSection(Collection rows){
		var h2 = h2("Create a read-only Other Instance");
		var table = table(tbody(each(rows, this::makeCreateOtherRow)));
		return div(h2, table);
	}

	private TrTag makeCreateOtherRow(OtherClientDto row){
		String href = new URIBuilder().setPath(servletContext.getContextPath()
				+ paths.datarouter.auroraInstances.createOtherInstance.toSlashedString())
				.addParameter(P_clientName, row.clientName)
				.addParameter(P_clusterName, row.clusterName)
				.addParameter(P_region, row.region)
				.toString();
		return tr(
				td(row.clientName),
				td(a("Create Other Instance").withHref(href))
				.withClass("text-center"));
	}

	@Handler
	public Mav deleteOtherInstance(@Param(P_clientName) String clientName, @Param(P_region) String region){
		Require.isTrue(clientName.endsWith(rdsSettings.dbOtherInstanceSuffix.get()));
		rdsService.deleteOtherInstance(clientName, region);
		config.removeOtherDatabaseDns(clientName);
		var dto = new DatarouterChangelogDtoBuilder(
				"AuroraClients",
				clientName,
				"deleted " + clientName + " instance",
				getSessionInfo().getNonEmptyUsernameOrElse(""))
				.build();
		changelogRecorder.record(dto);
		return new InContextRedirectMav(request, paths.datarouter.auroraInstances.inspectClientUrl.toSlashedString());
	}

	public String getDeleteOtherClientUri(String contextPath, DnsHostEntryDto row){
		String href = new URIBuilder().setPath(contextPath
				+ paths.datarouter.auroraInstances.deleteOtherInstance.toSlashedString())
				.addParameter(P_clientName, rdsSettings.dbPrefix.get() + row.getClientName())
				.addParameter(P_region, row.getRegion())
				.toString();
		return href;
	}

	//add region to this dto
	private static class OtherClientDto{
		private final String clientName;
		private final String clusterName;
		private final String region;

		public OtherClientDto(String clientName, String clusterName, String region){
			this.clientName = clientName;
			this.clusterName = clusterName;
			this.region = region;
		}


	}

}




© 2015 - 2025 Weber Informatics LLC | Privacy Policy