# # # add_file "cmd_policy.cc" # content [e3e698c544f61dc2ab58549e7264c0d896d56f29] # # patch "Makefile.am" # from [fbd8a4f969f93fad2158db91ca84782b0665f7f8] # to [316032f4d02341483858c5941473bdf59405c6ae] # # patch "project.cc" # from [56f1dc381dc621eb41d1d45414f65102667b0742] # to [a87d6e320fea8da287442b9af2d999e34ff1e030] # ============================================================ --- cmd_policy.cc e3e698c544f61dc2ab58549e7264c0d896d56f29 +++ cmd_policy.cc e3e698c544f61dc2ab58549e7264c0d896d56f29 @@ -0,0 +1,180 @@ +// Copyright 2008 Timothy Brownawell +// +// This file is made available under the GNU GPL v2 or later. + +#include "base.hh" + +#include "app_state.hh" +#include "basic_io.hh" +#include "botan/botan.h" +#include "cmd.hh" +#include "dates.hh" +#include "file_io.hh" +#include "revision.hh" +#include "roster.hh" +#include "transforms.hh" + +CMD_GROUP(policy, "policy", "", CMD_REF(__root__), + N_("Commands that deal with policy branches."), + ""); + +namespace basic_io +{ + namespace syms + { + symbol const branch_uid("branch_uid"); + symbol const committer("committer"); + } +} + +namespace { + std::string + generate_uid() + { + // FIXME: I'm sure there's a better way to do this. + std::string when = date_t::now().as_iso_8601_extended(); + char buf[20]; + Botan::Global_RNG::randomize(reinterpret_cast(buf), 20); + return when + "--" + encode_hexenc(std::string(buf, 20)); + } + + void + write_branch_policy(data & dat, + std::string const & branch_uid, + std::set const & committers) + { + basic_io::printer printer; + basic_io::stanza st; + st.push_str_pair(basic_io::syms::branch_uid, branch_uid); + for (std::set::const_iterator i = committers.begin(); + i != committers.end(); ++i) + { + st.push_str_pair(basic_io::syms::committer, (*i)()); + } + printer.print_stanza(st); + dat = data(printer.buf); + } + void + write_branch_policy(data & dat, + std::string const & branch_uid, + rsa_keypair_id const & committer) + { + std::set committers; + committers.insert(committer); + write_branch_policy(dat, branch_uid, committers); + } + + // Generate a revision with a single file, branches/__policy__, + // and put it in a new branch which is referenced by that file. + void + create_policy_branch(database & db, key_store & keys, + lua_hooks & lua, options & opts, + branch_prefix const & policy_name, + std::set const & administrators, + std::string & policy_uid, data & spec) + { + rsa_keypair_id key; + get_user_key(key, keys, db); + + policy_uid = generate_uid(); + transaction_guard guard(db); + + + // spec file + file_id spec_id; + write_branch_policy(spec, policy_uid, administrators); + file_data spec_data(spec); + calculate_ident(spec_data, spec_id); + + cset cs; + cs.dirs_added.insert(file_path_internal("")); + cs.dirs_added.insert(file_path_internal("branches")); + cs.files_added.insert(std::make_pair(file_path_internal("branches/__policy__"), + spec_id)); + roster_t old_roster; + revision_t rev; + make_revision(revision_id(), old_roster, cs, rev); + revision_id rev_id; + calculate_ident(rev, rev_id); + revision_data rdat; + write_revision(rev, rdat); + + + // write to the db + if (!db.file_version_exists(spec_id)) + { + db.put_file(spec_id, spec_data); + } + db.put_revision(rev_id, rdat); + + + // add certs + // Do not use project_t::put_standard_certs here, we don't want the + // branch name to be translated! + date_t date; + if (opts.date_given) + date = opts.date; + else + date = date_t::now(); + + std::string author = opts.author(); + if (author.empty()) + { + if (!lua.hook_get_author(branch_name(policy_name() + ".__policy__"), + key, author)) + author = key(); + } + utf8 changelog(N_("Create new policy branch.")); + + cert_revision_in_branch(rev_id, branch_uid(policy_uid), db, keys); + cert_revision_changelog(rev_id, changelog, db, keys); + cert_revision_date_time(rev_id, date, db, keys); + cert_revision_author(rev_id, author, db, keys); + + + guard.commit(); + P(F("Created new policy branch with id '%s'") % policy_uid); + } +} + +CMD(create_project, "create_project", "", CMD_REF(policy), + N_("NAME"), + N_("Bootstrap creation of a new project."), + N_(""), + options::opts::none) +{ + N(args.size() == 1, + F("Wrong argument count.")); + std::string project_name = idx(args, 0)(); + system_path project_dir = app.opts.conf_dir / "projects"; + system_path project_file = project_dir / path_component(project_name); + + require_path_is_directory(app.opts.conf_dir, + F("Cannot find configuration directory."), + F("Configuration directory is a file.")); + require_path_is_nonexistent(project_file, + F("You already have a project with that name.")); + mkdir_p(project_dir); + + rsa_keypair_id key; + get_user_key(key, app.keys, app.db); + std::set admin_keys; + admin_keys.insert(key); + + std::string policy_uid; + data policy_spec; + create_policy_branch(app.db, app.keys, app.lua, app.opts, + branch_prefix(project_name), admin_keys, + policy_uid, policy_spec); + + write_data(project_file, policy_spec, project_dir); + P(F("Wrote project spec to %s") % project_file); +} + +CMD(create_subpolicy, "create_subpolicy", "", CMD_REF(policy), + N_("NAME"), + N_("Create a policy for a new subtree of an existing project."), + "", + options::opts::none) +{ +} ============================================================ --- Makefile.am fbd8a4f969f93fad2158db91ca84782b0665f7f8 +++ Makefile.am 316032f4d02341483858c5941473bdf59405c6ae @@ -1,10 +1,10 @@ CMD_SOURCES = \ AUTOMAKE_OPTIONS=subdir-objects 1.7.1 ACLOCAL_AMFLAGS = -I m4 CMD_SOURCES = \ cmd.hh cmd_netsync.cc cmd_list.cc cmd_packet.cc cmd_key_cert.cc \ cmd_merging.cc cmd_db.cc cmd_diff_log.cc cmd_ws_commit.cc \ - cmd_othervcs.cc cmd_automate.cc cmd_files.cc + cmd_othervcs.cc cmd_automate.cc cmd_files.cc cmd_policy.cc SANITY_CORE_SOURCES = \ sanity.cc sanity.hh quick_alloc.hh vector.hh base.hh \ ============================================================ --- project.cc 56f1dc381dc621eb41d1d45414f65102667b0742 +++ project.cc a87d6e320fea8da287442b9af2d999e34ff1e030 @@ -30,8 +30,6 @@ namespace basic_io { symbol const branch_uid("branch_uid"); symbol const committer("committer"); - symbol const policy("policy_branch_id"); - symbol const administrator("administrator"); } } @@ -68,14 +66,14 @@ class policy_branch while (pa.symp()) { - if(pa.symp(basic_io::syms::policy)) + if(pa.symp(basic_io::syms::branch_uid)) { pa.sym(); string branch; pa.str(branch); my_branch_cert_value = branch_uid(branch); } - else if (pa.symp(basic_io::syms::administrator)) + else if (pa.symp(basic_io::syms::committer)) { pa.sym(); string key; @@ -251,9 +249,10 @@ namespace } }; - revision_id policy_branch_head(branch_uid const & name, - set const & trusted_signers, - database & db) + bool maybe_get_policy_branch_head(branch_uid const & name, + set const & trusted_signers, + database & db, + revision_id & rid) { L(FL("getting heads of policy branch %s") % name); base64 branch_encoded; @@ -267,11 +266,18 @@ namespace not_in_policy_branch p(db, branch_encoded, trusted_signers); erase_ancestors_and_failures(heads, p, db, NULL); - E(heads.size() == 1, - F("policy branch %s has %d heads, should have 1 head") - % name % heads.size()); - - return *heads.begin(); + if (heads.size() != 1) + { + W(F("Policy branch %s has %d heads, should have 1 head.") + % name % heads.size()); + W(F("Some branches may not be available.")); + return false; + } + else + { + rid = *heads.begin(); + return true; + } } } @@ -281,15 +287,23 @@ shared_ptr policy_branc if (!rev) { revision_id rid; - rid = policy_branch_head(my_branch_cert_value, my_committers, db); - rev.reset(new policy_revision(db, rid, prefix)); + if (maybe_get_policy_branch_head(my_branch_cert_value, + my_committers, db, rid)) + { + rev.reset(new policy_revision(db, rid, prefix)); + } } return rev; } map policy_branch::branches() { shared_ptr policy = get_policy(); - return policy->all_branches(); + if (policy) + { + return policy->all_branches(); + } + else + return map(); } ////////////////////////////////////////////////////////////////////////