Commit 3a5d06d3 authored by Jelte Jansen's avatar Jelte Jansen
Browse files

[2541] add createZone() to DataSourceClient interface

Note: currently called createZone so as not to interfere with an existing addZone() call in MemoryClient.
Instead of pure virtual I made it have a default 'NotImplemented' method.
Also, the DatabaseClient holds the transaction (not the SQLite3Accessor); we could do it on that level but we already have a transaction API on a higher level and it makes a little bit more sense to me to do it there
parent e5ae1675
...@@ -378,6 +378,20 @@ public: ...@@ -378,6 +378,20 @@ public:
isc_throw(isc::NotImplemented, isc_throw(isc::NotImplemented,
"Data source doesn't support getZoneCount"); "Data source doesn't support getZoneCount");
} }
// It first checks if the specified name of the zone exists. If it
// exists it returns false; otherwise it adds information of the
// new zone in backend-dependent manner and returns true.
// The DB-based version of this method would perform the check and add in
// a single transaction.
//
// Throws on any unexpected failure.
// Default implementation throws isc::NotImplemented
virtual bool createZone(const dns::Name&) {
isc_throw(isc::NotImplemented,
"Data source doesn't support addZone");
};
}; };
} }
} }
......
...@@ -43,6 +43,30 @@ using boost::scoped_ptr; ...@@ -43,6 +43,30 @@ using boost::scoped_ptr;
namespace isc { namespace isc {
namespace datasrc { namespace datasrc {
// RAII-style transaction holder; roll back the transaction unless explicitely committed
namespace {
class TransactionHolder {
public:
TransactionHolder(DatabaseAccessor& accessor) : accessor_(accessor),
committed_(false)
{
accessor_.startTransaction();
}
~TransactionHolder() {
if (!committed_) {
accessor_.rollback();
}
}
void commit() {
accessor_.commit();
committed_ = true;
}
private:
DatabaseAccessor& accessor_;
bool committed_;
};
} // end unnamed namespace
DatabaseClient::DatabaseClient(RRClass rrclass, DatabaseClient::DatabaseClient(RRClass rrclass,
boost::shared_ptr<DatabaseAccessor> boost::shared_ptr<DatabaseAccessor>
...@@ -80,6 +104,18 @@ DatabaseClient::findZone(const Name& name) const { ...@@ -80,6 +104,18 @@ DatabaseClient::findZone(const Name& name) const {
return (FindResult(result::NOTFOUND, ZoneFinderPtr())); return (FindResult(result::NOTFOUND, ZoneFinderPtr()));
} }
bool
DatabaseClient::createZone(const Name& name) {
TransactionHolder transaction(*accessor_);
std::pair<bool, int> zone(accessor_->getZone(name.toText()));
if (zone.first) {
return (false);
}
accessor_->addZone(name.toText());
transaction.commit();
return (true);
}
DatabaseClient::Finder::Finder(boost::shared_ptr<DatabaseAccessor> accessor, DatabaseClient::Finder::Finder(boost::shared_ptr<DatabaseAccessor> accessor,
int zone_id, const isc::dns::Name& origin) : int zone_id, const isc::dns::Name& origin) :
accessor_(accessor), accessor_(accessor),
......
...@@ -1393,6 +1393,8 @@ public: ...@@ -1393,6 +1393,8 @@ public:
/// should use it as a ZoneFinder only. /// should use it as a ZoneFinder only.
virtual FindResult findZone(const isc::dns::Name& name) const; virtual FindResult findZone(const isc::dns::Name& name) const;
virtual bool createZone(const isc::dns::Name&);
/// \brief Get the zone iterator /// \brief Get the zone iterator
/// ///
/// The iterator allows going through the whole zone content. If the /// The iterator allows going through the whole zone content. If the
......
...@@ -60,4 +60,8 @@ TEST_F(ClientTest, defaultGetZoneCount) { ...@@ -60,4 +60,8 @@ TEST_F(ClientTest, defaultGetZoneCount) {
EXPECT_THROW(client_.getZoneCount(), isc::NotImplemented); EXPECT_THROW(client_.getZoneCount(), isc::NotImplemented);
} }
TEST_F(ClientTest, defaultCreateZone) {
EXPECT_THROW(client_.createZone(Name("example.com.")), isc::NotImplemented);
}
} }
...@@ -334,6 +334,7 @@ public: ...@@ -334,6 +334,7 @@ public:
isc_throw(isc::NotImplemented, isc_throw(isc::NotImplemented,
"This test database knows nothing about NSEC3 nor order"); "This test database knows nothing about NSEC3 nor order");
} }
private: private:
const std::string database_name_; const std::string database_name_;
...@@ -4097,4 +4098,26 @@ TYPED_TEST(DatabaseClientTest, findNSEC3) { ...@@ -4097,4 +4098,26 @@ TYPED_TEST(DatabaseClientTest, findNSEC3) {
performNSEC3Test(*finder, true); performNSEC3Test(*finder, true);
} }
TYPED_TEST(DatabaseClientTest, createZone) {
const Name new_name("example.com");
const DataSourceClient::FindResult
zone(this->client_->findZone(new_name));
ASSERT_EQ(result::NOTFOUND, zone.code);
// The mock implementation does not do createZone,
// in which case it should throw NotImplemented (from
// the base class)
if(this->is_mock_) {
ASSERT_THROW(this->client_->createZone(new_name), isc::NotImplemented);
} else {
// But in the real case, it should work and return true
ASSERT_TRUE(this->client_->createZone(new_name));
const DataSourceClient::FindResult
zone2(this->client_->findZone(new_name));
ASSERT_EQ(result::SUCCESS, zone2.code);
// And the second call should return false since
// it already exists
ASSERT_FALSE(this->client_->createZone(new_name));
}
}
} }
Markdown is supported
0% or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment