int GetCommand::execute (Cmdline const & cl) { if (cl.arguments.size () != 1) throw invalid_argument ("Need one argument"); KeySet conf; kdb::Key root = cl.createKey (0); kdb::KDB kdb (root); std::string n; if (cl.all) { n = root.getName (); root.setName ("/"); } kdb.get (conf, root); if (cl.all) { root.setName (n); } // do a lookup without tracer to warm up default cache conf.lookup (root); root.setCallback (warnOnMeta); if (cl.verbose) { cout << "got " << conf.size () << " keys" << std::endl; root.setCallback (printTrace); } Key k = conf.lookup (root); int ret = 0; if (k) { if (cl.verbose) { cout << "The resulting keyname is " << k.getName () << std::endl; } cout << k.getString (); } else { cerr << "Did not find key"; ret = 1; } if (!cl.noNewline) { cout << endl; } printWarnings (cerr, root); printError (cerr, root); return ret; }
/** * @brief give info about current mounted backends * * @param mountConf a keyset that contains everything below * Backends::mountpointsPath * * @return an vector of information about mounted backends */ Backends::BackendInfoVector Backends::getBackendInfo (KeySet mountConf) { std::vector<BackendInfo> ret; Key rootKey (Backends::mountpointsPath, KEY_END); Key cur; mountConf.rewind (); while ((cur = mountConf.next ())) { if (cur.isDirectBelow (rootKey)) { BackendInfo bi; Key path = mountConf.lookup (cur.getName () + "/config/path"); if (path) { bi.path = path.getString (); } Key mp = mountConf.lookup (cur.getName () + "/mountpoint"); if (mp) { bi.mountpoint = mp.getString (); } bi.name = cur.getBaseName (); ret.push_back (bi); } } return ret; }
TypeChecker(KeySet config) { enforce = config.lookup("/enforce"); Key k = config.lookup("/require_version"); if (k && k.getString() != "2") throw "Required Version does not match 2"; types.insert (pair<string, Type*>("short", new MType<kdb::short_t>())); types.insert (pair<string, Type*>("unsigned_short", new MType<kdb::unsigned_short_t>())); types.insert (pair<string, Type*>("long", new MType<kdb::long_t>())); types.insert (pair<string, Type*>("unsigned_long", new MType<kdb::unsigned_long_t>())); types.insert (pair<string, Type*>("long_long", new MType<kdb::long_long_t>())); types.insert (pair<string, Type*>("unsigned_long_long", new MType<kdb::unsigned_long_long_t>())); types.insert (pair<string, Type*>("float", new TType<kdb::float_t>())); types.insert (pair<string, Type*>("double", new TType<kdb::double_t>())); types.insert (pair<string, Type*>("long_double", new TType<kdb::long_double_t>())); types.insert (pair<string, Type*>("char", new TType<kdb::char_t>())); types.insert (pair<string, Type*>("boolean", new TType<kdb::boolean_t>())); types.insert (pair<string, Type*>("octet", new TType<kdb::octet_t>())); // non-standard types (deprecated, just for // compatibility): types.insert (pair<string, Type*>("any", new AnyType())); types.insert (pair<string, Type*>("empty", new EmptyType())); types.insert (pair<string, Type*>("FSType", new FSType())); types.insert (pair<string, Type*>("string", new StringType())); }
TEST (test_contextual_policy, setPolicy) { using namespace kdb; KeySet ks; Context c; // clang-format off ContextualValue<int, SetPolicyIs<MySetPolicy<int>>> cv (ks, c, Key("/test", KEY_CASCADING_NAME, KEY_VALUE, "/test", KEY_META, "default", "88", KEY_END)); // clang-format on EXPECT_EQ (cv, 88); EXPECT_EQ (cv, 88); EXPECT_TRUE (ks.lookup ("/test")) << "did not find /test"; EXPECT_FALSE (ks.lookup ("dir/test")) << "found dir/test wrongly"; cv = 40; EXPECT_EQ (cv, 40); cv.syncKeySet (); EXPECT_EQ (cv, 40); // TODO: setPolicy not working correctly EXPECT_TRUE (ks.lookup ("/test")) << "did not find /test"; EXPECT_TRUE (ks.lookup ("dir/test")) << "could not find dir/test"; }
std::vector<PluginSpec> PluginVariantDatabase::getPluginVariantsFromSysconf (PluginSpec const & whichplugin, KeySet const & sysconf, KeySet const & genconfToIgnore) const { std::vector<PluginSpec> result; KeySet ksSysconf (sysconf); // first find possible variants Key kVariantBase ("system/elektra/plugins", KEY_END); kVariantBase.addBaseName (whichplugin.getName ()); kVariantBase.addBaseName ("variants"); KeySet ksPluginVariantSysconf (ksSysconf.cut (kVariantBase)); KeySet ksToIterate (ksPluginVariantSysconf); for (auto kCurrent : ksToIterate) { Key kCurrentTest (kVariantBase); kCurrentTest.addBaseName (kCurrent.getBaseName ()); if (kCurrentTest == kCurrent) { PluginSpec variant (whichplugin); KeySet ksVariantConfToAdd; // new base for plugin conf Key kVariantPluginConf ("system/", KEY_END); // add system conf for plugin variant Key kVariantSysconf (this->buildVariantSysconfKey (whichplugin, kCurrent.getBaseName (), "config")); this->addKeysBelowKeyToConf (kVariantSysconf, ksPluginVariantSysconf, kVariantPluginConf, ksVariantConfToAdd); // check if the variant was disabled : system/elektra/plugins/simpleini/variants/space/disable Key kDisable = sysconf.lookup (this->buildVariantSysconfKey (whichplugin, kCurrent.getBaseName (), "disable")); if (kDisable && kDisable.getString () == "1") { continue; // skip this variant } // check if the variant is in the genconfToIgnore list Key kGenconfVariant (kVariantPluginConf); kGenconfVariant.addBaseName (kCurrent.getBaseName ()); Key kIgnore = genconfToIgnore.lookup (kGenconfVariant); if (kIgnore) { continue; // this variant was added by genconf already } if (ksVariantConfToAdd.size () == 0) { continue; // no config means no variant } variant.appendConfig (ksVariantConfToAdd); result.push_back (variant); } } return result; }
std::vector<PluginSpec> PluginVariantDatabase::getPluginVariantsFromGenconf (PluginSpec const & whichplugin, KeySet const & genconf, KeySet const & sysconf) const { std::vector<PluginSpec> result; KeySet ksToIterate (genconf); for (auto kCurrent : ksToIterate) { Key kCurrentTest (kCurrent.getNamespace () + "/", KEY_END); kCurrentTest.addBaseName (kCurrent.getBaseName ()); // e.g. system/space if (kCurrentTest == kCurrent) { PluginSpec variant (whichplugin); KeySet ksVariantConfToAdd; // new base for plugin conf Key kVariantPluginConf ("system/", KEY_END); // take variant config from genconf and transform it to proper plugin conf, // e.g. system/space/config/format -> system/format Key kVariantConf (kCurrentTest); kVariantConf.addBaseName ("config"); // e.g. system/space/config this->addKeysBelowKeyToConf (kVariantConf, genconf, kVariantPluginConf, ksVariantConfToAdd); // TODO plugin infos // check if the variant was disabled : system/elektra/plugins/simpleini/variants/space/disable Key kDisable = sysconf.lookup (this->buildVariantSysconfKey (whichplugin, kCurrent.getBaseName (), "disable")); if (kDisable && kDisable.getString () == "1") { continue; // skip this variant } // check if an override is available : system/elektra/plugins/simpleini/variants/space/override Key kOverride = sysconf.lookup (this->buildVariantSysconfKey (whichplugin, kCurrent.getBaseName (), "override")); if (kOverride && kOverride.getString () == "1") { // first delete config from genconf entirely ksVariantConfToAdd.clear (); Key kVariantSysconf (this->buildVariantSysconfKey (whichplugin, kCurrent.getBaseName (), "config")); this->addKeysBelowKeyToConf (kVariantSysconf, sysconf, kVariantPluginConf, ksVariantConfToAdd); } if (ksVariantConfToAdd.size () == 0) { continue; // no config means no variant } variant.appendConfig (ksVariantConfToAdd); result.push_back (variant); } } std::vector<PluginSpec> resFromSysconf (this->getPluginVariantsFromSysconf (whichplugin, sysconf, genconf)); result.insert (result.end (), resFromSysconf.begin (), resFromSysconf.end ()); return result; }
Point operator() (KeySet const & ks, std::string const & name, option_t const options) const { Key x = ks.lookup (name + "/x", options); if (!x) throw KeyNotFoundException (name + "/x not found"); Key y = ks.lookup (name + "/y", options); if (!y) throw KeyNotFoundException (name + "/y not found"); return Point (x.get<int> (), y.get<int> ()); }
SpecBackendBuilder SpecMountpointReader::readMountpointSpecification (KeySet const & cks) { ks = cks; mp = ks.head ().dup (); Key rmp (mp.dup ()); helper::removeNamespace (rmp); bb.setMountpoint (rmp, mountConf); processKey (mp); bb.nodes++; // count mp ks.lookup (mp, KDB_O_POP); ks.rewind (); // we need old fashioned loop, because it can handle ks.cut during iteration for (Key k = ks.next (); k; k = ks.next ()) { // search for mountpoint Key m = k.getMeta<const Key> ("mountpoint"); if (m) { SpecMountpointReader smr (backends, bbi); backends[k] = smr.readMountpointSpecification (ks.cut (k)); continue; } processKey (k); bb.nodes++; } bb.setBackendConfig (backendConfig); bb.useConfigFile (mp.getMeta<std::string> ("mountpoint")); return bb; }
void TestCommand::doBasicTest () { { KDB kdb; Key t = root.dup (); t.addBaseName ("basic"); t.setString ("BasicString"); KeySet basic; basic.append (t); KeySet test; kdb.get (test, root); kdb.set (basic, root); } { KDB kdb; Key t = root.dup (); t.addBaseName ("basic"); t.setString ("BasicString"); KeySet test; kdb.get (test, root); nrTest++; if (!test.lookup (t)) { nrError++; cerr << "Basic test failed" << endl; } } }
int operator() (KeySet const & ks, std::string const & name, option_t const options) const { Key k = ks.lookup (name, options); if (!k) return -5; if (k.getStringSize () <= 1) return -3; return k.get<int> () + 5; }
int MetaRemoveCommand::execute (Cmdline const & cl) { if (cl.arguments.size () != 2) { throw invalid_argument ("Need 2 arguments"); } Key parentKey = cl.createKey (0); string metaname = cl.arguments[1]; KeySet conf; kdb.get (conf, parentKey); printWarnings (cerr, parentKey); Key k = conf.lookup (parentKey); if (!k) { cerr << "Key not found" << endl; return 1; } k.delMeta (metaname); kdb.set (conf, parentKey); return 0; }
void test_kdbGetSet() { cout << "testing kdbSet() and kdbGet()" << endl; { KeySet ks_set (5, *Key ("user/tests/key3", KEY_DIR, KEY_END), *Key ("user/tests/key3/1", KEY_END), *Key ("user/tests/key3/2", KEY_END), *Key ("user/tests/key3/3", KEY_VALUE, "value", KEY_END), KS_END); KeySet ks; KDB kdb; kdb.get (ks, "user/tests/key3"); ks.append(ks_set); kdb.set (ks, "user/tests/key3"); } // check if they were written { KDB kdb; KeySet ks; kdb.get (ks, "user/tests/key3"); exit_if_fail(ks.lookup("user/tests/key3/3"), "could not find previously written key"); succeed_if(ks.lookup("user/tests/key3/3").get<std::string>() == "value", "could not get value"); succeed_if(!ks.lookup("user/tests/key3/3").needSync(), "should not need sync"); } // now remove keys (cleanup) { KeySet ks; KDB kdb; kdb.get (ks, "user/tests/key3"); ks.cut(Key("user/tests/key3", KEY_END)); kdb.set (ks, "user/tests/key3"); } // check if its gone now { KDB kdb; KeySet ks; kdb.get (ks, "user/tests/key3"); succeed_if(!ks.lookup("user/tests/key3/3"), "key was not removed"); } }
TEST_F (AutoMergeStrategyTest, EqualsModifyMerges) { task.theirs.lookup ("user/parentt/config/key1").setString ("modifiedvalue"); Key conflictKey = mergeKeys.lookup (mk1); result.addConflict (conflictKey, CONFLICT_SAME, CONFLICT_MODIFY); conflictKey = result.getConflictSet ().at (0); strategy.resolveConflict (task, conflictKey, result); EXPECT_FALSE (result.hasConflicts ()) << "Invalid conflict detected"; KeySet merged = result.getMergedKeys (); EXPECT_EQ (4, merged.size ()); EXPECT_EQ (mk1, merged.lookup (mk1)); EXPECT_EQ ("modifiedvalue", merged.lookup (mk1).getString ()) << "Key " << merged.lookup (mk1) << "was not modified correctly"; compareAllExceptKey1 (merged); }
void TestCommand::doStringTest () { vector<string> teststrings; teststrings.push_back (""); teststrings.push_back ("value"); teststrings.push_back ("value with spaces"); teststrings.push_back (" a very long value with many spaces and basically very very long, but only text ... "); for (int i = 1; i < 256; ++i) teststrings.back () += " very very long, but only text ... "; for (auto & teststring : teststrings) { { KDB kdb; Key t = root.dup (); t.addBaseName ("string"); t.setString (teststring); KeySet basic; basic.append (t); KeySet test; kdb.get (test, root); kdb.set (basic, root); } { KDB kdb; KeySet test; kdb.get (test, root); Key t = root.dup (); t.addBaseName ("string"); Key res = test.lookup (t); nrTest++; if (!res) { nrError++; cerr << "String test failed (key not found)" << t.getName () << endl; continue; } nrTest++; if (res.getString () != teststring) { nrError++; cerr << "String test failed (value is not equal)" << endl; cerr << "We got: \"" << res.getString () << "\"" << endl; cerr << "We wanted: \"" << teststring << "\"" << endl; } } } }
TEST (test_contextual_nocontext, integer) { using namespace kdb; KeySet ks; NoContext c; const char * name = "/%language%/%country%/%dialect%/test"; ASSERT_TRUE (!ks.lookup (name)); Value<int, ContextPolicyIs<NoContext>> i (ks, c, Key (name, KEY_CASCADING_NAME, KEY_META, "default", s_value, KEY_END)); ASSERT_EQ (i, i_value); ASSERT_TRUE (ks.lookup (name)); i = 5; ASSERT_EQ (i, 5); ASSERT_EQ (i.getSpec ().getName (), name); i.syncKeySet (); ASSERT_EQ (ks.lookup (name).getString (), "5"); i = 10; ASSERT_EQ (i, 10); ASSERT_EQ (ks.lookup (name).getString (), "10"); }
int MetaSetCommand::execute (Cmdline const & cl) { if (cl.arguments.size () < 2 || cl.arguments.size () > 3) { throw invalid_argument ("Need 2 or 3 arguments"); } string metaname = cl.arguments[1]; Key parentKey = cl.createKey (0); string keyname = parentKey.getName (); if (keyname[0] == '/') { // fix name for lookup keyname = "spec" + keyname; if (!cl.quiet) std::cout << "Using keyname " << keyname << std::endl; // fix k for kdb.set later parentKey.setName (keyname); } KeySet conf; kdb.get (conf, parentKey); Key k = conf.lookup (parentKey); if (!k) { k = Key (keyname, KEY_END); // k.setBinary(0, 0); // conceptually maybe better, but would have confusing "binary" metadata conf.append (k); if (cl.verbose) cout << "Creating key " << keyname << endl; } if (!k.isValid ()) { cerr << "Could not create key " << keyname << endl; return 1; } if (cl.arguments.size () == 2) { if (!cl.quiet) cout << "Only two arguments, thus deleting metaname " << metaname << endl; k.delMeta (metaname); } else { std::string metavalue = cl.arguments[2]; if (metaname == "atime" || metaname == "mtime" || metaname == "ctime") { stringstream str (metavalue); time_t t; str >> t; if (!str.good ()) throw "conversion failure"; k.setMeta<time_t> (metaname, t); } else {
int FstabCommand::execute (Cmdline const & cl) { int argc = cl.arguments.size (); if (argc != 5 && argc != 6 && argc != 7) { throw invalid_argument ("number of arguments not correct, need 5, 6 or 7"); } KeySet conf; Key parentKey = cl.createKey (0); kdb.get (conf, parentKey); printWarnings (cerr, parentKey); Key k = conf.lookup (parentKey); if (!k) { k = cl.createKey (0); conf.append (k); } std::string keyname = k.getName (); string dumpfreq = "0"; if (argc >= 6) { dumpfreq = cl.arguments[5].c_str (); } string passno = "0"; if (argc >= 7) { passno = cl.arguments[6].c_str (); } kdb::KeySet config (20, *kdb::Key (keyname + "/ZZZNewFstabName", KEY_END), *kdb::Key (keyname + "/ZZZNewFstabName/device", KEY_VALUE, cl.arguments[1].c_str (), KEY_END), *kdb::Key (keyname + "/ZZZNewFstabName/mpoint", KEY_VALUE, cl.arguments[2].c_str (), KEY_END), *kdb::Key (keyname + "/ZZZNewFstabName/type", KEY_VALUE, cl.arguments[3].c_str (), KEY_END), *kdb::Key (keyname + "/ZZZNewFstabName/options", KEY_VALUE, cl.arguments[4].c_str (), KEY_END), *kdb::Key (keyname + "/ZZZNewFstabName/dumpfreq", KEY_VALUE, dumpfreq.c_str (), KEY_END), *kdb::Key (keyname + "/ZZZNewFstabName/passno", KEY_VALUE, passno.c_str (), KEY_END), KS_END); conf.append (config); if (cl.verbose) { cout << conf; } kdb.set (conf, parentKey); printWarnings (cerr, parentKey); return 0; }
/** * @brief Allows for updating of a database entry. * * Will renew the entry and all its subkeys (configuration). * * @param entry A custom Entry object holding current information. * @return true if the entry was updated, false if not * @throw kdbrest::exception::EntryNotFoundException in case the entry * to update does not exist. */ bool StorageEngine::updateEntry (model::Entry & entry) { using namespace kdb; // register exclusive access boost::unique_lock<boost::shared_mutex> lock (m_mutex_entryCache); bool found = false; std::vector<model::Entry> & entries = this->m_entryCache; unsigned int i = 0; while (i < entries.size ()) { if (entries[i].getName ().compare (entry.getName ()) == 0) { found = true; break; } i++; } if (!found) { throw exception::EntryNotFoundException (); } KDB kdb; KeySet ks; kdb.get (ks, entry.getName ()); Key k = ks.lookup (entry.getName ()); if (!k) { throw kdbrest::exception::EntryNotFoundException (); } ks.cut (entry); ks.append (entry); ks.append (entry.getSubkeys ()); if (kdb.set (ks, entry.getName ()) >= 1) { entries.erase (entries.begin () + i); entries.push_back (entry); return true; } else { return false; } }
TEST_F(ThreeWayMergeTest, SameAddedEqualValueMerges) { ours.append (Key ("user/parento/config/key5", KEY_VALUE, "newvalue", KEY_END)); theirs.append (Key ("user/parentt/config/key5", KEY_VALUE, "newvalue", KEY_END)); MergeResult result = merger.mergeKeySet (base, ours, theirs, mergeParent); EXPECT_FALSE(result.hasConflicts()) << "Invalid conflict detected"; KeySet merged = result.getMergedKeys (); EXPECT_EQ(6, merged.size ()); compareAllKeys (merged); compareKeys (Key ("user/parentm/config/key5", KEY_VALUE, "newvalue", KEY_END), merged.lookup (mk5)); }
/** * @brief Allows for deleting of an user entry. * * Will delete the entry iteself as well as all subkeys (additional user information). * * @param user A custom User object that should be deleted. * @return true if the user was deleted successfully, false otherwise * @throw kdbrest::exception::UserNotFoundException in case the user * to delete does not exist. */ bool StorageEngine::deleteUser (model::User & user) { using namespace kdb; // register exclusive access boost::unique_lock<boost::shared_mutex> lock (m_mutex_userCache); bool found = false; std::vector<model::User> & users = this->m_userCache; unsigned int i = 0; while (i < users.size ()) { if (users[i].getName ().compare (user.getName ()) == 0) { found = true; break; } i++; } if (!found) { throw exception::UserNotFoundException (); } KDB kdb; KeySet ks; kdb.get (ks, user.getName ()); Key k = ks.lookup (user.getName ()); if (!k) { throw kdbrest::exception::UserNotFoundException (); } ks.cut (user); if (kdb.set (ks, user.getName ()) >= 1) { users.erase (users.begin () + i); return true; } else { return false; } }
TEST_F(OneSideStrategyTest, TheirsWinsCorrectly) { base.lookup ("user/parentb/config/key1").setString ("valueb"); ours.lookup ("user/parento/config/key1").setString ("valueo"); theirs.lookup ("user/parentt/config/key1").setString ("valuet"); Key conflictKey = mk1; result.addConflict (conflictKey, MODIFY, MODIFY); conflictKey = result.getConflictSet ().at (0); OneSideStrategy strategy (THEIRS); strategy.resolveConflict (task, conflictKey, result); EXPECT_FALSE(result.hasConflicts()) << "Invalid conflict detected"; KeySet merged = result.getMergedKeys (); cout << merged << endl; EXPECT_EQ(4, merged.size ()); compareKeys (Key ("user/parentm/config/key1", KEY_VALUE, "valuet", KEY_END), merged.lookup (mk1)); }
int main () { KeySet config; KDB kdb; kdb.get (config, "/sw/MyApp"); Key k = config.lookup ("/sw/MyApp/mykey"); if (k) { k.set<int> (k.get<int> () + 1); } else { Key n; n.setName ("user/sw/MyApp/mykey"); n.set<int> (0); config.append (n); } kdb.set (config, "/sw/MyApp"); }
int ValidationCommand::execute(Cmdline const& cl) { size_t argc = cl.arguments.size(); if (argc != 3 && argc != 4) { throw invalid_argument("need 3 or 4 arguments"); } KeySet conf; Key parentKey = cl.createKey(0); string keyname = parentKey.getName(); kdb.get(conf, parentKey); Key k = conf.lookup(keyname); if (!k) { k = Key(keyname, KEY_END); conf.append (k); } if (!k.isValid()) { throw invalid_argument("keyname not valid"); } string value = cl.arguments[1]; string validationregex = cl.arguments[2]; string validationmessage; if (argc == 4) validationmessage = cl.arguments[3]; else validationmessage = "Regular expression " + validationregex + " does not match the supplied value"; k.setString (value); k.setMeta<string> ("validation/regex", validationregex); k.setMeta<string> ("validation/message", validationmessage); kdb.set(conf,parentKey); return 0; }
/** * @brief Can be used to create an entry in the database. * * Will add the entry and all subkeys to the database (configuration). * * @param entry A custom Entry object holding information to store. * @return true if the entry was stored, false if something went wrong * @throws kdbrest::exception::EntryAlreadyExistsException in case an * entry with the given name does already exist in the key database. */ bool StorageEngine::createEntry (model::Entry & entry) { using namespace kdb; // register exclusive access boost::unique_lock<boost::shared_mutex> lock (m_mutex_entryCache); std::vector<model::Entry> & entries = this->m_entryCache; for (auto & elem : entries) { if (elem.getName ().compare (entry.getName ()) == 0) { throw exception::EntryAlreadyExistsException (); } } KDB kdb; KeySet ks; kdb.get (ks, entry.getName ()); Key k = ks.lookup (entry.getName ()); if (k) { throw kdbrest::exception::EntryAlreadyExistsException (); } ks.append (entry); ks.append (entry.getSubkeys ()); if (kdb.set (ks, entry.getName ()) >= 1) { entries.push_back (entry); return true; } else { return false; } }
/** * @brief Can be used to create an user entry in the database. * * Will add the user and all subkeys to the database (additional information). * * @param user A custom User object holding information to store. * @return true if the user was stored, false if something went wrong * @throws kdbrest::exception::UserAlreadyExistsException in case an * user with the given name does already exist in the key database. */ bool StorageEngine::createUser (model::User & user) { using namespace kdb; // register exclusive access boost::unique_lock<boost::shared_mutex> lock (m_mutex_userCache); std::vector<model::User> & users = this->m_userCache; for (auto & elem : users) { if (elem.getName ().compare (user.getName ()) == 0) { throw exception::UserAlreadyExistsException (); } } KDB kdb; KeySet ks; kdb.get (ks, user.getName ()); Key k = ks.lookup (user.getName ()); if (k) { throw exception::UserAlreadyExistsException (); } ks.append (user); ks.append (user.getSubkeys ()); if (kdb.set (ks, user.getName ()) >= 1) { users.push_back (user); return true; } else { return false; } }
TEST_F(MetaMergeStrategyTest, MergesMetaWithInnerStrategy) { base.lookup ("user/parentb/config/key1").setMeta ("testmeta", "valueb"); ours.lookup ("user/parento/config/key1").setMeta ("testmeta", "valueo"); theirs.lookup ("user/parentt/config/key1").setMeta ("testmeta", "valuet"); Key conflictKey = mk1; result.addConflict (conflictKey, CONFLICT_META, CONFLICT_META); conflictKey = result.getConflictSet ().at (0); ThreeWayMerge merger; MergeConflictStrategy *strategy = new OneSideStrategy (OURS); merger.addConflictStrategy (strategy); MetaMergeStrategy metaStrategy (merger); metaStrategy.resolveConflict (task, conflictKey, result); delete (strategy); EXPECT_FALSE(result.hasConflicts()) << "Invalid conflict detected"; KeySet merged = result.getMergedKeys (); cout << merged << endl; EXPECT_EQ(4, merged.size ()); EXPECT_EQ("valueo", merged.lookup (mk1).getMeta<string> ("testmeta")); }
int MetaGetCommand::execute (Cmdline const& cl) { if (cl.arguments.size() != 2) { throw invalid_argument ("Need 2 arguments"); } string keyname = cl.arguments[0]; string metaname = cl.arguments[1]; KeySet conf; Key parentKey(keyname, KEY_END); kdb.get(conf, parentKey); printWarnings(cerr,parentKey); Key k = conf.lookup(keyname); if (!k) { cerr << "Key not found" << endl; return 1; } if (!k.getMeta<const Key>(metaname)) { cerr << "Metakey not found" << endl; return 2; } cout << k.getMeta<string>(metaname); if (!cl.noNewline) { cout << endl; } return 0; }
int MetaSetCommand::execute (Cmdline const& cl) { if (cl.arguments.size() != 3) { throw invalid_argument ("Need 3 arguments"); } string keyname = cl.arguments[0]; string metaname = cl.arguments[1]; KeySet conf; Key parentKey(keyname, KEY_END); kdb.get(conf, parentKey); Key k = conf.lookup(keyname); if (!k) { k = Key(keyname, KEY_END); // k.setBinary(0, 0); // conceptually maybe better, but would have confusing "binary" metadata conf.append(k); if (cl.verbose) cout << "Creating key " << keyname << endl; } if (!k.isValid()) { cout << "Could not create key" << endl; return 1; } std::string metavalue = cl.arguments[2]; if (metaname == "atime" || metaname == "mtime" || metaname == "ctime") { stringstream str (metavalue); time_t t; str >> t; if (!str.good()) throw "conversion failure"; k.setMeta<time_t> (metaname, t); } else {
int main() { KeySet ours; KeySet theirs; KeySet base; // the root of the subtree containing our keys (i.e. our side of the merge) Key oursRoot ("user/ours", KEY_END); // the root of the subtree containing their keys (i.e. their side of the merge) Key theirsRoot ("user/theirs", KEY_END); // the root of the subtree containing the base keys (i.e. the common ancestor of the merge) Key baseRoot ("user/base", KEY_END); // the root of the subtree that will contain the merge result Key resultRoot ("user/result", KEY_END); // Step 1: retrieve clean KeySets containing only those // keys that should be merged. This is a bit trickier than // it seems at first. Have a look at the documentation of kdbGet // for detailed information // things to note: // * use blocks with local KDB instances so we don't have to worry about // writing the keys back // * remove the root key itself from the result KeySet because it usually // contains the mounted filename and cannot be merged anyway // Also have a look at the documentation of kdbSet() // (http://doc.libelektra.org/api/latest/html/group__kdb.html#ga11436b058408f83d303ca5e996832bcf). // The merging framework can also be used to resolve conflicts resulting from // concurrent calls to kdbSet() as described in the example of kdbSet(). { KDB lkdb; lkdb.get (ours, oursRoot); ours = ours.cut (oursRoot); ours.lookup(oursRoot, KDB_O_POP); lkdb.get (theirs, theirsRoot); theirs = theirs.cut (theirsRoot); theirs.lookup(theirsRoot, KDB_O_POP); lkdb.get (base, baseRoot); base = base.cut (baseRoot); base.lookup(baseRoot, KDB_O_POP); } // Step 2: Make sure that no keys reside below the intended merge result root // Usually the merge can be either aborted if this is the case or the existing // keys can be overwritten. KeySet resultKeys; kdb::KDB kdb; kdb.get (resultKeys, resultRoot); KeySet discard = resultKeys.cut (resultRoot); if (discard.size () != 0) { // handle existing keys below the result root return -1; } ThreeWayMerge merger; // Step 3: Decide which resolution strategies to use. The strategies are registered // with the merge and applied in order as soon as a conflict is detected. If a strategy // marks a conflict as resolved, no further strategies are consulted. Therefore the order // in which they are registered is absolutely crucial. With this chaining the strategies // remain simple, but can be combined to powerful resolution strategies. // Have a look at the strategy documentation for further details on what they do and how they work. // The unit tests also provide insight into how the strategies work. // In order to simplify the initialization, predefined merge configurations exist. // in this example we first resolve all the keys that can be automatically // resolved (e.g. only one side was modified). This is exactly the use case of the // AutoMergeConfiguration. AutoMergeConfiguration configuration; configuration.configureMerger(merger); // Step 4: Perform the actual merge MergeResult result = merger.mergeKeySet ( MergeTask (BaseMergeKeys (base, baseRoot), OurMergeKeys (ours, oursRoot), TheirMergeKeys (theirs, theirsRoot), resultRoot)); // Step 5: work with the result. The merger will return an object containing information // about the merge result. if (!result.hasConflicts ()) { // output some statistical information cout << result.getMergedKeys().size() << " keys in the result" << endl; cout << result.getNumberOfEqualKeys() << " keys were equal" << endl; cout << result.getNumberOfResolvedKeys() << " keys were resolved" << endl; // write the result resultKeys.append(result.getMergedKeys()); kdb.set (resultKeys, resultRoot); return 0; } else { KeySet conflicts = result.getConflictSet(); cerr << conflicts.size() << " conflicts were detected that could not be resolved automatically:" << endl; conflicts.rewind(); Key current; while ((current = conflicts.next())) { // For each unresolved conflict there is a conflict key in the merge result. // This conflict key contains meta information about the reason of the conflict. // In particular the metakeys conflict/operation/our and conflict/operation/their contain // the operations done on our version of the key and their version of the key relative to // the base version of the key. string ourConflict = current.getMeta<string> ("conflict/operation/our"); string theirConflict = current.getMeta<string> ("conflict/operation/their"); cerr << current << endl; cerr << "ours: " << ourConflict << ", theirs: " << theirConflict << endl; cerr << endl; } cerr << "Merge unsuccessful." << endl; return -1; } }
void TestCommand::doMetaTest () { vector<string> teststrings; teststrings.push_back (""); teststrings.push_back ("value"); teststrings.push_back ("value with spaces"); teststrings.push_back (" a very long value with many spaces and basically very very long, but only text ... "); for (int i = 1; i < 256; ++i) teststrings.back () += " very very long, but only text ... "; teststrings.push_back ("ascii umlauts !\"§$%&/()=?`\\}][{"); teststrings.push_back ("utf8 umlauts ¸¬½¼³²¹ł€¶øæßð𳽫»¢“”nµ─·"); teststrings.push_back ("all chars:"); for (int i = 1; i < 256; ++i) teststrings.back ().push_back (i); teststrings.push_back ("€"); for (int i = 1; i < 256; ++i) { string s; s.push_back (i); teststrings.push_back (s); } vector<string> testnames; testnames.push_back ("keyname"); testnames.push_back ("deep/below/keyname"); testnames.push_back ("keyname with spaces"); testnames.push_back ("deep/belowkeyname with spaces"); testnames.push_back (" a very long value with many spaces and basically very very long, but only text "); for (int i = 1; i < 256; ++i) testnames.back () += "/ very very long, but only text ... "; testnames.push_back ("ascii umlauts !\"§$%&/()=?`\\}][{"); testnames.push_back ("utf8 umlauts ¸¬½¼³²¹ł€¶øæßð𳽫»¢“”nµ─·"); testnames.push_back ("all chars:"); for (int i = 1; i < 256; ++i) testnames.back ().push_back (i); testnames.push_back ("€"); for (int i = 1; i < 256; ++i) { if (i == 46) continue; // ignore . string s; s.push_back (i); testnames.push_back (s); } for (auto & testname : testnames) for (auto & teststring : teststrings) { { KDB kdb; Key t = root.dup (); t.addBaseName (testname); t.setMeta<string> ("key", teststring); KeySet basic; basic.append (t); KeySet test; kdb.get (test, root); kdb.set (basic, root); } { KDB kdb; KeySet test; kdb.get (test, root); Key t = root.dup (); t.addBaseName (testname); Key res = test.lookup (t); nrTest++; if (!res) { nrError++; cerr << "Meta test failed (key not found)" << t.getName () << endl; continue; } std::string meta = res.getMeta<std::string> ("key"); nrTest++; if (meta != teststring) { nrError++; cerr << "Meta test failed (name is not equal)" << endl; cerr << "We got: \"" << meta << "\"" << endl; cerr << "We wanted: \"" << teststring << "\"" << endl; } } } }