mirror of
https://github.com/kanidm/kanidm.git
synced 2025-02-24 13:07:00 +01:00
165 lines
5 KiB
Rust
165 lines
5 KiB
Rust
use std::fs::File;
|
|
use std::path::Path;
|
|
use std::time::Duration;
|
|
use uuid::Uuid;
|
|
|
|
use std::collections::{HashMap, HashSet};
|
|
|
|
use crate::data::*;
|
|
|
|
const N_USERS: usize = 3000;
|
|
const N_GROUPS: usize = 1500;
|
|
const N_MEMBERSHIPS: usize = 10;
|
|
const N_NEST: usize = 4;
|
|
|
|
pub(crate) fn doit(output: &Path) {
|
|
info!(
|
|
"Performing data generation into {}",
|
|
output.to_str().unwrap(),
|
|
);
|
|
|
|
let mut rng = rand::thread_rng();
|
|
|
|
if N_MEMBERSHIPS >= N_GROUPS {
|
|
error!("Too many memberships per group. Memberships must be less that n-groups");
|
|
return;
|
|
}
|
|
|
|
// Open before we start so we have it ready to go.
|
|
let out_file = match File::create(output) {
|
|
Ok(f) => f,
|
|
Err(e) => {
|
|
error!("Failed to open {} - {:?}", output.to_str().unwrap(), e);
|
|
return;
|
|
}
|
|
};
|
|
|
|
// Number of users
|
|
let accounts: Vec<_> = (0..N_USERS)
|
|
.map(|i| Account {
|
|
name: format!("testuser{}", i),
|
|
display_name: format!("Test User {}", i),
|
|
password: readable_password_from_random(),
|
|
uuid: Uuid::new_v4(),
|
|
})
|
|
.collect();
|
|
|
|
// Number of groups.
|
|
let mut groups: Vec<_> = (0..N_GROUPS)
|
|
.map(|i| Group {
|
|
name: format!("testgroup{}", i),
|
|
uuid: Uuid::new_v4(),
|
|
members: Vec::new(),
|
|
})
|
|
.collect();
|
|
|
|
// Should groups be randomly nested?
|
|
// The way this is done is we split the array based on nest level. If it's 1, we split
|
|
// in 2, 2 we split in 3 and so on.
|
|
if N_NEST > 0 {
|
|
debug!("Nesting Groups");
|
|
|
|
let chunk_size = N_GROUPS / (N_NEST + 1);
|
|
if chunk_size == 0 {
|
|
error!("Unable to chunk groups, need (N_GROUPS / (N_NEST + 1)) > 0");
|
|
return;
|
|
}
|
|
|
|
let mut chunk_iter = groups.chunks_mut(chunk_size);
|
|
// Can't fail due to above checks.
|
|
let mut p_chunk = chunk_iter.next().unwrap();
|
|
// while let Some(w_chunk) = chunk_iter.next() {
|
|
for w_chunk in chunk_iter {
|
|
// add items from work chunk to parent chunk
|
|
p_chunk
|
|
.iter_mut()
|
|
.zip(w_chunk.iter())
|
|
.for_each(|(p, w): (&mut _, &_)| p.members.push(w.uuid));
|
|
|
|
// swap w_chunk to p_chunk
|
|
p_chunk = w_chunk;
|
|
}
|
|
}
|
|
|
|
// Number of memberships per user.
|
|
// We use rand for this to sample random numbers of
|
|
for acc in accounts.iter() {
|
|
// Sample randomly.
|
|
for idx in rand::seq::index::sample(&mut rng, N_GROUPS, N_MEMBERSHIPS).iter() {
|
|
groups[idx].members.push(acc.uuid);
|
|
}
|
|
}
|
|
|
|
// Build from the generated data above.
|
|
let all_entities: HashMap<Uuid, Entity> = accounts
|
|
.into_iter()
|
|
.map(|acc| (acc.uuid, Entity::Account(acc)))
|
|
.chain(groups.into_iter().map(|grp| (grp.uuid, Entity::Group(grp))))
|
|
.collect();
|
|
|
|
// Define the entries that should exist "at the start of the test". For now, we just
|
|
// create everything. Maybe when we start to add mod tests we need to retain a pool
|
|
// of things to retain here for those ops.
|
|
let precreate: HashSet<_> = all_entities.keys().copied().collect();
|
|
|
|
// The set of accounts in all_entities.
|
|
let accounts: HashSet<Uuid> = all_entities
|
|
.iter()
|
|
.filter_map(|(uuid, ent)| match ent {
|
|
Entity::Account(_) => Some(*uuid),
|
|
_ => None,
|
|
})
|
|
.collect();
|
|
|
|
// This defines a map of "entity" to "what can it manipulate". This
|
|
// is used to create access controls in some cases for mod tests.
|
|
//
|
|
// For example, if we have user with uuid X and it changes Group with
|
|
// uuid Y, then we need to ensure that X has group-mod permissions over
|
|
// Y in some capacity.
|
|
let access: HashMap<Uuid, Vec<EntityType>> = HashMap::new();
|
|
|
|
// The set of operations to simulate. We pre-calc these so tests can randomly
|
|
// sample and perform the searches as needed.
|
|
|
|
// We don't have original times, so we can fudge these.
|
|
let orig_etime = Duration::from_secs(1);
|
|
let rtime = Duration::from_secs(1);
|
|
// Needed for random sampling.
|
|
let all_ids: Vec<_> = all_entities.keys().copied().collect();
|
|
let all_ids_len = all_ids.len();
|
|
|
|
let connections: Vec<_> = (0..all_ids_len)
|
|
.map(|id| {
|
|
// Could be rand?
|
|
let n_search = 1;
|
|
|
|
let mut search_ids = Vec::new();
|
|
for idx in rand::seq::index::sample(&mut rng, all_ids_len, n_search).iter() {
|
|
search_ids.push(all_ids[idx]);
|
|
}
|
|
//
|
|
Conn {
|
|
id: id as i32,
|
|
ops: vec![Op {
|
|
orig_etime,
|
|
rtime,
|
|
op_type: OpType::Search(search_ids),
|
|
}],
|
|
}
|
|
})
|
|
.collect();
|
|
|
|
let td = TestData {
|
|
all_entities,
|
|
access,
|
|
accounts,
|
|
precreate,
|
|
connections,
|
|
};
|
|
|
|
if let Err(e) = serde_json::to_writer_pretty(out_file, &td) {
|
|
error!("Writing to file -> {:?}", e);
|
|
};
|
|
}
|