aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorTrumeet <yuuta@yuuta.moe>2021-04-02 13:33:39 -0700
committerTrumeet <yuuta@yuuta.moe>2021-04-02 13:33:39 -0700
commite5e38927755c355d6cb318cb9d0ecef0efc6491e (patch)
tree1f9b7281b1aa4222c3708425e54a5ba9c59028cd
parent85859d8f23f78985755026f07a7066245d1551c4 (diff)
downloaddn42peering-e5e38927755c355d6cb318cb9d0ecef0efc6491e.tar
dn42peering-e5e38927755c355d6cb318cb9d0ecef0efc6491e.tar.gz
dn42peering-e5e38927755c355d6cb318cb9d0ecef0efc6491e.tar.bz2
dn42peering-e5e38927755c355d6cb318cb9d0ecef0efc6491e.zip
feat(central): only allow one peer per ASN on the same node
If migration failed with duplication keys, manually remove duplicated peers and repair. Check duplicated peers: SELECT id, asn, node, COUNT(*) FROM peer GROUP BY asn, node HAVING COUNT(*) > 1
-rw-r--r--central/src/main/java/moe/yuuta/dn42peering/manage/ManageHandler.java11
-rw-r--r--central/src/main/java/moe/yuuta/dn42peering/peer/DuplicatePeerException.java4
-rw-r--r--central/src/main/java/moe/yuuta/dn42peering/peer/PeerServiceImpl.java38
-rw-r--r--central/src/main/resources/db/migration/V5__Peer_Node_ASN_Unique.sql1
4 files changed, 40 insertions, 14 deletions
diff --git a/central/src/main/java/moe/yuuta/dn42peering/manage/ManageHandler.java b/central/src/main/java/moe/yuuta/dn42peering/manage/ManageHandler.java
index 6b22858..4ba720a 100644
--- a/central/src/main/java/moe/yuuta/dn42peering/manage/ManageHandler.java
+++ b/central/src/main/java/moe/yuuta/dn42peering/manage/ManageHandler.java
@@ -22,10 +22,12 @@ import io.vertx.json.schema.SchemaParser;
import io.vertx.json.schema.SchemaRouter;
import io.vertx.json.schema.SchemaRouterOptions;
import io.vertx.json.schema.common.dsl.ObjectSchemaBuilder;
+import io.vertx.serviceproxy.ServiceException;
import moe.yuuta.dn42peering.admin.SudoUtils;
import moe.yuuta.dn42peering.asn.IASNService;
import moe.yuuta.dn42peering.jaba.Pair;
import moe.yuuta.dn42peering.node.INodeService;
+import moe.yuuta.dn42peering.peer.DuplicatePeerException;
import moe.yuuta.dn42peering.peer.IPeerService;
import moe.yuuta.dn42peering.peer.Peer;
import moe.yuuta.dn42peering.portal.FormException;
@@ -146,7 +148,14 @@ public class ManageHandler implements ISubRouter {
if (ar.succeeded()) {
peer.setId((int) (long) ar.result());
f.complete(peer);
- } else f.fail(ar.cause());
+ } else {
+ if(((ServiceException)ar.cause()).getDebugInfo().getString("causeName")
+ .equals(DuplicatePeerException.class.getName())) {
+ f.fail(new FormException(peer, "A peer on your chosen node already exists. You can only create one peer per node."));
+ return;
+ }
+ f.fail(ar.cause());
+ }
})))
.onSuccess(peer -> {
ctx.response()
diff --git a/central/src/main/java/moe/yuuta/dn42peering/peer/DuplicatePeerException.java b/central/src/main/java/moe/yuuta/dn42peering/peer/DuplicatePeerException.java
new file mode 100644
index 0000000..b80332e
--- /dev/null
+++ b/central/src/main/java/moe/yuuta/dn42peering/peer/DuplicatePeerException.java
@@ -0,0 +1,4 @@
+package moe.yuuta.dn42peering.peer;
+
+public class DuplicatePeerException extends Exception {
+}
diff --git a/central/src/main/java/moe/yuuta/dn42peering/peer/PeerServiceImpl.java b/central/src/main/java/moe/yuuta/dn42peering/peer/PeerServiceImpl.java
index 5784da2..9548815 100644
--- a/central/src/main/java/moe/yuuta/dn42peering/peer/PeerServiceImpl.java
+++ b/central/src/main/java/moe/yuuta/dn42peering/peer/PeerServiceImpl.java
@@ -3,6 +3,7 @@ package moe.yuuta.dn42peering.peer;
import io.vertx.core.*;
import io.vertx.core.impl.logging.Logger;
import io.vertx.core.impl.logging.LoggerFactory;
+import io.vertx.mysqlclient.MySQLException;
import io.vertx.sqlclient.Pool;
import io.vertx.sqlclient.Row;
import io.vertx.sqlclient.RowSet;
@@ -87,19 +88,30 @@ class PeerServiceImpl implements IPeerService {
@Override
public IPeerService addNew(@Nonnull Peer peer, @Nonnull Handler<AsyncResult<Long>> handler) {
peer.setId(0);
- Future.<RowSet<Peer>>future(f -> SqlTemplate
- .forQuery(pool, "INSERT INTO peer VALUES (#{id}, #{type}, #{asn}, " +
- "#{ipv4}, #{ipv6}, " +
- "#{wg_endpoint}, #{wg_endpoint_port}, " +
- "#{wg_self_pubkey}, #{wg_self_privkey}, " +
- "#{wg_peer_pubkey}, #{wg_preshared_secret}, " +
- "#{provision_status}, #{mpbgp}, #{node}" +
- ")")
- .mapFrom(PeerParametersMapper.INSTANCE)
- .mapTo(PeerRowMapper.INSTANCE)
- .execute(peer, f))
- .compose(rows -> Future.succeededFuture(rows.property(DatabaseUtils.LAST_INSERTED_ID)))
- .onComplete(handler);
+ Future.<Long>future(f -> {
+ Future.<RowSet<Peer>>future(f1 -> SqlTemplate
+ .forQuery(pool, "INSERT INTO peer VALUES (#{id}, #{type}, #{asn}, " +
+ "#{ipv4}, #{ipv6}, " +
+ "#{wg_endpoint}, #{wg_endpoint_port}, " +
+ "#{wg_self_pubkey}, #{wg_self_privkey}, " +
+ "#{wg_peer_pubkey}, #{wg_preshared_secret}, " +
+ "#{provision_status}, #{mpbgp}, #{node}" +
+ ")")
+ .mapFrom(PeerParametersMapper.INSTANCE)
+ .mapTo(PeerRowMapper.INSTANCE)
+ .execute(peer, f1))
+ .compose(rows -> Future.succeededFuture(rows.property(DatabaseUtils.LAST_INSERTED_ID)))
+ .onFailure(err -> {
+ if (err instanceof MySQLException) {
+ if (((MySQLException) err).getErrorCode() == 1062 /* Duplicate */) {
+ f.fail(new DuplicatePeerException());
+ return;
+ }
+ f.fail(err);
+ }
+ })
+ .onSuccess(f::complete);
+ }).onComplete(handler);
return this;
}
diff --git a/central/src/main/resources/db/migration/V5__Peer_Node_ASN_Unique.sql b/central/src/main/resources/db/migration/V5__Peer_Node_ASN_Unique.sql
new file mode 100644
index 0000000..2678483
--- /dev/null
+++ b/central/src/main/resources/db/migration/V5__Peer_Node_ASN_Unique.sql
@@ -0,0 +1 @@
+ALTER TABLE `peer` ADD UNIQUE `Node_ASN_Unique` (`asn`, `node`); \ No newline at end of file