diff options
author | Trumeet <yuuta@yuuta.moe> | 2021-04-02 13:33:39 -0700 |
---|---|---|
committer | Trumeet <yuuta@yuuta.moe> | 2021-04-02 13:33:39 -0700 |
commit | e5e38927755c355d6cb318cb9d0ecef0efc6491e (patch) | |
tree | 1f9b7281b1aa4222c3708425e54a5ba9c59028cd | |
parent | 85859d8f23f78985755026f07a7066245d1551c4 (diff) | |
download | dn42peering-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
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 |