cli: Improve UX for cloning/fetching private repos
When `--private` is passed to `clone`, we force a fetch from nodes that aren't known seeds. When it isn't specified, we skip nodes that aren't known seeds, even if they are in the preferred seeds list. Previously, we would still fetch from them, which would cause errors.
This commit is contained in:
parent
ef1ed621d7
commit
e9b79fb6d0
|
|
@ -29,7 +29,7 @@ $ rad inspect --identity
|
|||
|
||||
``` ~bob
|
||||
$ rad ls --all --private
|
||||
$ rad clone rad:z2ug5mwNKZB8KGpBDRTrWHAMbvHCu --seed z6MknSLrJoTcukLrE435hVNQT4JUhbvWLX4kUzqkEStBU8Vi --timeout 1
|
||||
$ rad clone rad:z2ug5mwNKZB8KGpBDRTrWHAMbvHCu --private --seed z6MknSLrJoTcukLrE435hVNQT4JUhbvWLX4kUzqkEStBU8Vi --timeout 1
|
||||
✓ Seeding policy updated for rad:z2ug5mwNKZB8KGpBDRTrWHAMbvHCu with scope 'all'
|
||||
✓ Fetching rad:z2ug5mwNKZB8KGpBDRTrWHAMbvHCu from z6MknSL…StBU8Vi..
|
||||
✓ Creating checkout in ./heartwood..
|
||||
|
|
|
|||
|
|
@ -5,7 +5,7 @@ Bob tries to fetch it, and even though he's connected to Alice, it fails.
|
|||
$ rad ls
|
||||
```
|
||||
``` ~bob (fail)
|
||||
$ rad clone rad:z2ug5mwNKZB8KGpBDRTrWHAMbvHCu --seed z6MknSLrJoTcukLrE435hVNQT4JUhbvWLX4kUzqkEStBU8Vi --timeout 1
|
||||
$ rad clone rad:z2ug5mwNKZB8KGpBDRTrWHAMbvHCu --private --seed z6MknSLrJoTcukLrE435hVNQT4JUhbvWLX4kUzqkEStBU8Vi --timeout 1
|
||||
✓ Seeding policy updated for rad:z2ug5mwNKZB8KGpBDRTrWHAMbvHCu with scope 'all'
|
||||
✗ Fetching rad:z2ug5mwNKZB8KGpBDRTrWHAMbvHCu from z6MknSL…StBU8Vi.. error: failed to perform fetch handshake
|
||||
✗ Error: repository rad:z2ug5mwNKZB8KGpBDRTrWHAMbvHCu not found
|
||||
|
|
|
|||
|
|
@ -41,13 +41,14 @@ Usage
|
|||
The `clone` command will use your local node's routing table to find seeds from
|
||||
which it can clone the repository.
|
||||
|
||||
For private repositories, the `--seed` option can be passed to clone directly
|
||||
from a known seed in the privacy set.
|
||||
For private repositories, use the `--private` and `--seed` options, to clone directly
|
||||
from known seeds in the privacy set.
|
||||
|
||||
Options
|
||||
|
||||
--scope <scope> Follow scope: `followed` or `all` (default: all)
|
||||
-s, --seed <nid> Clone from this seed (may be specified multiple times)
|
||||
--private Clone a private repository
|
||||
--timeout <secs> Timeout for fetching repository (default: 9)
|
||||
--help Print help
|
||||
|
||||
|
|
@ -64,8 +65,6 @@ pub struct Options {
|
|||
scope: Scope,
|
||||
/// Sync settings.
|
||||
sync: SyncSettings,
|
||||
/// Fetch timeout.
|
||||
timeout: time::Duration,
|
||||
}
|
||||
|
||||
impl Args for Options {
|
||||
|
|
@ -76,7 +75,6 @@ impl Args for Options {
|
|||
let mut id: Option<RepoId> = None;
|
||||
let mut scope = Scope::All;
|
||||
let mut sync = SyncSettings::default();
|
||||
let mut timeout = time::Duration::from_secs(9);
|
||||
let mut directory = None;
|
||||
|
||||
while let Some(arg) = parser.next()? {
|
||||
|
|
@ -93,11 +91,14 @@ impl Args for Options {
|
|||
|
||||
scope = term::args::parse_value("scope", value)?;
|
||||
}
|
||||
Long("private") => {
|
||||
sync.force = true;
|
||||
}
|
||||
Long("timeout") => {
|
||||
let value = parser.value()?;
|
||||
let secs = term::args::number(&value)?;
|
||||
|
||||
timeout = time::Duration::from_secs(secs as u64);
|
||||
sync.timeout = time::Duration::from_secs(secs as u64);
|
||||
}
|
||||
Long("no-confirm") => {
|
||||
// We keep this flag here for consistency though it doesn't have any effect,
|
||||
|
|
@ -129,7 +130,6 @@ impl Args for Options {
|
|||
directory,
|
||||
scope,
|
||||
sync,
|
||||
timeout,
|
||||
},
|
||||
vec![],
|
||||
))
|
||||
|
|
@ -151,7 +151,7 @@ pub fn run(options: Options, ctx: impl term::Context) -> anyhow::Result<()> {
|
|||
options.id,
|
||||
options.directory.clone(),
|
||||
options.scope,
|
||||
options.sync.with_profile(&profile).timeout(options.timeout),
|
||||
options.sync.with_profile(&profile),
|
||||
&mut node,
|
||||
&signer,
|
||||
&profile.storage,
|
||||
|
|
|
|||
|
|
@ -435,14 +435,13 @@ pub fn fetch(
|
|||
let replicas = settings
|
||||
.replicas
|
||||
.min(seeds.iter().filter(|s| s.nid != local).count());
|
||||
let sessions = node.sessions()?;
|
||||
let mut results = FetchResults::default();
|
||||
let (connected, mut disconnected) = seeds.partition();
|
||||
|
||||
// Fetch from specified seeds, plus our preferred seeds.
|
||||
// Fetch from specified seeds.
|
||||
for nid in &settings.seeds {
|
||||
if !sessions.iter().any(|s| &s.nid == nid) {
|
||||
term::warning(format!("node {nid} is not connected.. skipping"));
|
||||
if !seeds.is_connected(nid) && !settings.force {
|
||||
term::warning(format!("node {nid} is not connected or seeding.. skipping"));
|
||||
continue;
|
||||
}
|
||||
let result = fetch_from(rid, nid, settings.timeout, node)?;
|
||||
|
|
@ -463,6 +462,9 @@ pub fn fetch(
|
|||
let result = fetch_from(rid, &nid, settings.timeout, node)?;
|
||||
results.push(nid, result);
|
||||
}
|
||||
if results.success().count() >= replicas {
|
||||
return Ok(results);
|
||||
}
|
||||
|
||||
// Try to connect to disconnected seeds and fetch from them.
|
||||
while results.success().count() < replicas {
|
||||
|
|
|
|||
|
|
@ -22,6 +22,9 @@ pub struct SyncSettings {
|
|||
pub replicas: usize,
|
||||
/// Sync with the given list of seeds.
|
||||
pub seeds: BTreeSet<NodeId>,
|
||||
/// Sync with the given seeds even if they aren't in our routing table.
|
||||
/// Can be used to fetch private repositories, for example.
|
||||
pub force: bool,
|
||||
/// How long to wait for syncing to complete.
|
||||
pub timeout: time::Duration,
|
||||
}
|
||||
|
|
@ -33,6 +36,7 @@ impl SyncSettings {
|
|||
Self {
|
||||
replicas: seeds.len(),
|
||||
seeds,
|
||||
force: false,
|
||||
timeout: DEFAULT_SYNC_TIMEOUT,
|
||||
}
|
||||
}
|
||||
|
|
@ -51,17 +55,22 @@ impl SyncSettings {
|
|||
self
|
||||
}
|
||||
|
||||
/// Set the 'force' option.
|
||||
pub fn force(mut self, force: bool) -> Self {
|
||||
self.force = force;
|
||||
self
|
||||
}
|
||||
|
||||
/// Use profile to populate sync settings, by adding preferred seeds if no seeds are specified,
|
||||
/// and removing the local node from the set.
|
||||
pub fn with_profile(mut self, profile: &Profile) -> Self {
|
||||
// If no seeds were specified, add up to `replica` seeds from the preferred seeds.
|
||||
// If no seeds were specified, add the preferred seeds.
|
||||
if self.seeds.is_empty() {
|
||||
self.seeds = profile
|
||||
.config
|
||||
.preferred_seeds
|
||||
.iter()
|
||||
.map(|p| p.id)
|
||||
.take(self.replicas)
|
||||
.collect();
|
||||
}
|
||||
// Remove our local node from the seed set just in case it was added by mistake.
|
||||
|
|
@ -75,6 +84,7 @@ impl Default for SyncSettings {
|
|||
Self {
|
||||
replicas: 3,
|
||||
seeds: BTreeSet::new(),
|
||||
force: false,
|
||||
timeout: DEFAULT_SYNC_TIMEOUT,
|
||||
}
|
||||
}
|
||||
|
|
|
|||
Loading…
Reference in New Issue