Skip to content

Commit 0f1a07b

Browse files
Initial implementation of refs locking (#929)
Change-Id: refs-lock
1 parent e38fbeb commit 0f1a07b

File tree

4 files changed

+331
-33
lines changed

4 files changed

+331
-33
lines changed

josh-proxy/src/bin/josh-proxy.rs

Lines changed: 43 additions & 33 deletions
Original file line numberDiff line numberDiff line change
@@ -271,7 +271,7 @@ async fn repo_update_fn(
271271
async fn do_filter(
272272
repo_path: std::path::PathBuf,
273273
service: Arc<JoshProxyService>,
274-
upstream_repo: String,
274+
meta: josh_proxy::MetaConfig,
275275
temp_ns: Arc<josh_proxy::TmpGitNamespace>,
276276
filter: josh::filter::Filter,
277277
headref: String,
@@ -284,23 +284,23 @@ async fn do_filter(
284284
let _e = s.enter();
285285
tracing::trace!("in do_filter worker");
286286
let filter_spec = josh::filter::spec(filter);
287-
josh::housekeeping::remember_filter(&upstream_repo, &filter_spec);
287+
josh::housekeeping::remember_filter(&meta.config.repo, &filter_spec);
288288

289289
let transaction = josh::cache::Transaction::open(
290290
&repo_path.join("mirror"),
291291
Some(&format!(
292292
"refs/josh/upstream/{}/",
293-
&josh::to_ns(&upstream_repo),
293+
&josh::to_ns(&meta.config.repo),
294294
)),
295295
)?;
296-
let mut refslist = josh::housekeeping::list_refs(transaction.repo(), &upstream_repo)?;
296+
let mut refslist = josh::housekeeping::list_refs(transaction.repo(), &meta.config.repo)?;
297297

298298
let mut headref = headref;
299299

300300
if headref.starts_with("refs/") || headref == "HEAD" {
301301
let name = format!(
302302
"refs/josh/upstream/{}/{}",
303-
&josh::to_ns(&upstream_repo),
303+
&josh::to_ns(&meta.config.repo),
304304
headref.clone()
305305
);
306306
if let Ok(r) = transaction.repo().revparse_single(&name) {
@@ -315,7 +315,7 @@ async fn do_filter(
315315
if headref == "HEAD" {
316316
headref = heads_map
317317
.read()?
318-
.get(&upstream_repo)
318+
.get(&meta.config.repo)
319319
.unwrap_or(&"invalid".to_string())
320320
.clone();
321321
}
@@ -324,7 +324,8 @@ async fn do_filter(
324324
t2.repo()
325325
.odb()?
326326
.add_disk_alternate(&repo_path.join("mirror").join("objects").to_str().unwrap())?;
327-
let mut updated_refs = josh::filter_refs(&t2, filter, &refslist, josh::filter::empty())?;
327+
let updated_refs = josh::filter_refs(&t2, filter, &refslist, josh::filter::empty())?;
328+
let mut updated_refs = josh_proxy::refs_locking(&transaction.repo(), updated_refs, &meta);
328329
josh::housekeeping::namespace_refs(&mut updated_refs, &temp_ns.name());
329330
josh::update_refs(&t2, &mut updated_refs, &temp_ns.reference(&headref));
330331
t2.repo()
@@ -392,20 +393,12 @@ async fn handle_ui_request(
392393
return Ok(response);
393394
}
394395

395-
#[derive(serde::Serialize, serde::Deserialize, Debug, Clone, Default)]
396-
struct RepoConfig {
397-
repo: String,
398-
399-
#[serde(default)]
400-
filter: josh::filter::Filter,
401-
}
402-
403396
async fn query_meta_repo(
404397
serv: Arc<JoshProxyService>,
405398
meta_repo: &str,
406399
upstream_repo: &str,
407400
auth: &josh_proxy::auth::Handle,
408-
) -> josh::JoshResult<RepoConfig> {
401+
) -> josh::JoshResult<josh_proxy::MetaConfig> {
409402
let remote_url = [serv.upstream_url.as_str(), meta_repo].join("");
410403
match fetch_upstream(
411404
serv.clone(),
@@ -442,9 +435,24 @@ async fn query_meta_repo(
442435
return Err(josh::josh_error(&"meta repo entry not found"));
443436
}
444437

445-
let config: RepoConfig = serde_yaml::from_str(&meta_blob)?;
438+
let mut meta: josh_proxy::MetaConfig = Default::default();
439+
440+
meta.config = serde_yaml::from_str(&meta_blob)?;
446441

447-
return Ok(config);
442+
if meta.config.lock_refs {
443+
let meta_blob = josh::filter::tree::get_blob(
444+
transaction.repo(),
445+
&meta_tree,
446+
&std::path::Path::new(&upstream_repo.trim_start_matches("/")).join("refs.yml"),
447+
);
448+
449+
if meta_blob == "" {
450+
return Err(josh::josh_error(&"locked refs not found"));
451+
}
452+
meta.refs_lock = serde_yaml::from_str(&meta_blob)?;
453+
}
454+
455+
return Ok(meta);
448456
}
449457

450458
#[tracing::instrument]
@@ -516,22 +524,24 @@ async fn call_service(
516524
}
517525
};
518526

519-
let mut config = RepoConfig::default();
527+
let mut meta = Default::default();
520528

521529
if let Ok(meta_repo) = std::env::var("JOSH_META_REPO") {
522530
let auth = if let Ok(token) = std::env::var("JOSH_META_AUTH_TOKEN") {
523531
josh_proxy::auth::add_auth(&token)?
524532
} else {
525533
auth.clone()
526534
};
527-
config =
528-
query_meta_repo(serv.clone(), &meta_repo, &parsed_url.upstream_repo, &auth).await?;
535+
meta = query_meta_repo(serv.clone(), &meta_repo, &parsed_url.upstream_repo, &auth).await?;
529536
} else {
530-
config.repo = parsed_url.upstream_repo;
537+
meta.config.repo = parsed_url.upstream_repo;
531538
}
532539

533-
let filter = josh::filter::chain(config.filter, josh::filter::parse(&parsed_url.filter_spec)?);
534-
let remote_url = [serv.upstream_url.as_str(), config.repo.as_str()].join("");
540+
let filter = josh::filter::chain(
541+
meta.config.filter,
542+
josh::filter::parse(&parsed_url.filter_spec)?,
543+
);
544+
let remote_url = [serv.upstream_url.as_str(), meta.config.repo.as_str()].join("");
535545

536546
if parsed_url.pathinfo.starts_with("/info/lfs") {
537547
return Ok(Response::builder()
@@ -564,7 +574,7 @@ async fn call_service(
564574
let block = block.split(";").collect::<Vec<_>>();
565575

566576
for b in block {
567-
if b == config.repo {
577+
if b == meta.config.repo {
568578
return Ok(make_response(
569579
hyper::Body::from(formatdoc!(
570580
r#"
@@ -577,11 +587,11 @@ async fn call_service(
577587
}
578588

579589
if parsed_url.api == "/~/graphql" {
580-
return serve_graphql(serv, req, config.repo.to_owned(), remote_url, auth).await;
590+
return serve_graphql(serv, req, meta.config.repo.to_owned(), remote_url, auth).await;
581591
}
582592

583593
if parsed_url.api == "/~/graphiql" {
584-
let addr = format!("/~/graphql{}", config.repo);
594+
let addr = format!("/~/graphql{}", meta.config.repo);
585595
return Ok(tokio::task::spawn_blocking(move || {
586596
josh_proxy::juniper_hyper::graphiql(&addr, None)
587597
})
@@ -591,7 +601,7 @@ async fn call_service(
591601

592602
match fetch_upstream(
593603
serv.clone(),
594-
config.repo.to_owned(),
604+
meta.config.repo.to_owned(),
595605
&auth,
596606
remote_url.to_owned(),
597607
&headref,
@@ -618,10 +628,10 @@ async fn call_service(
618628
req.uri().query().map(|x| x.to_string()),
619629
parsed_url.pathinfo.is_empty(),
620630
) {
621-
return serve_query(serv, q, config.repo, filter, headref).await;
631+
return serve_query(serv, q, meta.config.repo, filter, headref).await;
622632
}
623633

624-
let temp_ns = prepare_namespace(serv.clone(), &config.repo, filter, &headref)
634+
let temp_ns = prepare_namespace(serv.clone(), &meta, filter, &headref)
625635
.in_current_span()
626636
.await?;
627637

@@ -654,7 +664,7 @@ async fn call_service(
654664
auth,
655665
port: serv.port.clone(),
656666
filter_spec: josh::filter::spec(filter),
657-
base_ns: josh::to_ns(&config.repo),
667+
base_ns: josh::to_ns(&meta.config.repo),
658668
git_ns: temp_ns.name().to_string(),
659669
git_dir: repo_path.clone(),
660670
mirror_git_dir: mirror_repo_path.clone(),
@@ -752,7 +762,7 @@ async fn serve_query(
752762
#[tracing::instrument]
753763
async fn prepare_namespace(
754764
serv: Arc<JoshProxyService>,
755-
upstream_repo: &str,
765+
meta: &josh_proxy::MetaConfig,
756766
filter: josh::filter::Filter,
757767
headref: &str,
758768
) -> josh::JoshResult<std::sync::Arc<josh_proxy::TmpGitNamespace>> {
@@ -766,7 +776,7 @@ async fn prepare_namespace(
766776
do_filter(
767777
serv.repo_path.clone(),
768778
serv.clone(),
769-
upstream_repo.to_owned(),
779+
meta.clone(),
770780
temp_ns.to_owned(),
771781
filter,
772782
headref.to_string(),

josh-proxy/src/lib.rs

Lines changed: 43 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,49 @@ enum PushMode {
1414
Split,
1515
}
1616

17+
#[derive(serde::Serialize, serde::Deserialize, Debug, Clone, Default)]
18+
pub struct Ref {
19+
pub target: josh::Oid,
20+
}
21+
22+
type RefsLock = std::collections::HashMap<String, josh::Oid>;
23+
24+
#[derive(serde::Serialize, serde::Deserialize, Debug, Clone, Default)]
25+
pub struct RepoConfig {
26+
pub repo: String,
27+
28+
#[serde(default)]
29+
pub filter: josh::filter::Filter,
30+
31+
#[serde(default)]
32+
pub lock_refs: bool,
33+
}
34+
35+
#[derive(Debug, Clone, Default)]
36+
pub struct MetaConfig {
37+
pub config: RepoConfig,
38+
pub refs_lock: RefsLock,
39+
}
40+
41+
pub fn refs_locking(
42+
repo: &git2::Repository,
43+
refs: Vec<(String, git2::Oid)>,
44+
meta: &MetaConfig,
45+
) -> Vec<(String, git2::Oid)> {
46+
if !meta.config.lock_refs {
47+
return refs;
48+
}
49+
let mut output = vec![];
50+
51+
for (n, id) in refs.into_iter() {
52+
if let Some(lid) = meta.refs_lock.get(&n) {
53+
output.push((n, (*lid).into()));
54+
}
55+
}
56+
57+
output
58+
}
59+
1760
fn baseref_and_options(refname: &str) -> josh::JoshResult<(String, String, Vec<String>, PushMode)> {
1861
let mut split = refname.splitn(2, '%');
1962
let push_to = split.next().ok_or(josh::josh_error("no next"))?.to_owned();

src/lib.rs

Lines changed: 37 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -63,6 +63,43 @@ impl Change {
6363
}
6464
}
6565

66+
#[derive(
67+
Clone, Hash, PartialEq, Eq, Copy, PartialOrd, Ord, Debug, serde::Serialize, serde::Deserialize,
68+
)]
69+
#[serde(try_from = "String", into = "String")]
70+
pub struct Oid(git2::Oid);
71+
72+
impl Default for Oid {
73+
fn default() -> Self {
74+
Oid(git2::Oid::zero())
75+
}
76+
}
77+
78+
impl std::convert::TryFrom<String> for Oid {
79+
type Error = JoshError;
80+
fn try_from(s: String) -> JoshResult<Oid> {
81+
Ok(Oid(git2::Oid::from_str(&s)?))
82+
}
83+
}
84+
85+
impl Into<String> for Oid {
86+
fn into(self) -> String {
87+
self.0.to_string()
88+
}
89+
}
90+
91+
impl Into<git2::Oid> for Oid {
92+
fn into(self) -> git2::Oid {
93+
self.0
94+
}
95+
}
96+
97+
impl From<git2::Oid> for Oid {
98+
fn from(oid: git2::Oid) -> Self {
99+
Self(oid)
100+
}
101+
}
102+
66103
pub const VERSION: &str =
67104
git_version::git_version!(args = ["--tags", "--always", "--dirty=-modified"]);
68105

0 commit comments

Comments
 (0)