From aac38dc8286810a78a83fe75d5a1824fccf865a2 Mon Sep 17 00:00:00 2001 From: Stanislau Hlebik Date: Wed, 31 Oct 2018 06:31:50 -0700 Subject: [PATCH] mononoke: add separate rc and prod config Summary: Let's have separate config bookmarks for release candidate and prod. That will let us customize shadow tier behaviour. This diff also adds checking of config repo consistency. It requires that RC bookmark is a descendant of a PROD bookmark. This topology makes it easy to see what are the changes between PROD and RC, and verification prevents divergence of configs i.e. sutiations when somebody updated a prod config but forget to rebase rc config. Reviewed By: HarveyHunt Differential Revision: D12857131 fbshipit-source-id: b60d8f7af16e3d530e5edeb22145ec0bd473ffe4 --- cmds/admin/config_repo.rs | 68 ++++++++++++++++++++++++++++++++------- 1 file changed, 56 insertions(+), 12 deletions(-) diff --git a/cmds/admin/config_repo.rs b/cmds/admin/config_repo.rs index 4bd05a0ff0..da1db5b8a2 100644 --- a/cmds/admin/config_repo.rs +++ b/cmds/admin/config_repo.rs @@ -48,6 +48,8 @@ enum ErrorKind { #[fail(display = "Aborting: command '{}' killed by a signal", _0)] KilledBySignal(&'static str), #[fail(display = "Aborting: command '{}' exited with exit status {}", _0, _1)] NonZeroExit(&'static str, i32), + #[fail(display = "Aborting: RC bookmark is not a descendant of PROD bookmark. Please move RC or PROD bookmarks")] + InvalidConfigRepoBookmarks(), } pub fn prepare_command<'a, 'b>(app: App<'a, 'b>) -> App<'a, 'b> { @@ -229,7 +231,11 @@ fn handle_fbpkg<'a>(args: &ArgMatches<'a>, logger: Logger) -> BoxFuture<(), Erro let import_dir = tmpdir.path().to_owned().join(IMPORT_DFLT_DIR); try_boxfuture!(fs::create_dir(&import_dir)); - force_prod_bookmark(src.clone()) + set_local_bookmark(src.clone(), "PROD") + .and_then({ + cloned!(src); + move |()| set_local_bookmark(src.clone(), "RC") + }) .and_then({ cloned!(src, import_dir); move |()| import(logger, src.clone(), import_dir) @@ -326,22 +332,30 @@ fn import(logger: Logger, src: PathBuf, dest: PathBuf) -> BoxFuture<(), Error> { RepositoryId::new(0), ))); - Blobimport { - logger, - blobrepo, - revlogrepo_path: src.join(".hg"), - changeset: None, - skip: None, - commits_limit: None, - no_bookmark: false, - }.import() + check_hg_config_repo(src.clone()) + .and_then(move |()| { + Blobimport { + logger, + blobrepo, + revlogrepo_path: src.join(".hg"), + changeset: None, + skip: None, + commits_limit: None, + no_bookmark: false, + }.import() + }) + .boxify() } -fn force_prod_bookmark(src: PathBuf) -> BoxFuture<(), Error> { +// Blobimport can't read remote bookmarks, so this function sets local bookmark +// with the same name +fn set_local_bookmark(src: PathBuf, bookmark: &str) -> BoxFuture<(), Error> { Command::new("hg") .arg("bookmark") .arg("--force") - .arg("PROD") + .arg(bookmark) + .arg("-r") + .arg(format!("remote/{}", bookmark)) .current_dir(&src) .status_async() .into_future() @@ -350,3 +364,33 @@ fn force_prod_bookmark(src: PathBuf) -> BoxFuture<(), Error> { .and_then(|status| check_status(status, "hg bookmark --force PROD")) .boxify() } + +// Verifies the consistency of the config repo +fn check_hg_config_repo(hg_repo_path: PathBuf) -> BoxFuture<(), Error> { + // Config repo should have two bookmarks - PROD and RC. + // PROD is for production jobs, RC for shadow jobs. + // RC should be an descendant of a PROD i.e. configs of a shadow jobs are + // configs of a production job plus some changes on top + // PROD and RC can point to the same commit + Command::new("hg") + .arg("log") + .arg("-r") + .arg("PROD::RC") + .arg("-T") + .arg("'{node}\n'") + .current_dir(&hg_repo_path) + .output_async() + .from_err() + .and_then(|output| { + let stdout = output.stdout.clone(); + check_status(output.status, "hg log -r 'PROD::RC' -T '{node}\\n'").and_then(move |()| { + if stdout.is_empty() { + Err(ErrorKind::InvalidConfigRepoBookmarks().into()) + } else { + Ok(()) + } + }) + }) + .map(|_| ()) + .boxify() +}