Skip to content

Commit

Permalink
feat: find (soon to be) invalid project names
Browse files Browse the repository at this point in the history
  • Loading branch information
chesedo committed Nov 17, 2022
1 parent c3c0ced commit 91df837
Show file tree
Hide file tree
Showing 5 changed files with 147 additions and 0 deletions.
4 changes: 4 additions & 0 deletions admin/src/args.rs
Original file line number Diff line number Diff line change
Expand Up @@ -18,8 +18,12 @@ pub enum Command {
/// Try to revive projects in the crashed state
Revive,

/// Manage custom domains
#[command(subcommand)]
Acme(AcmeCommand),

/// Manage project names
ProjectNames,
}

#[derive(Subcommand, Debug)]
Expand Down
16 changes: 16 additions & 0 deletions admin/src/client.rs
Original file line number Diff line number Diff line change
Expand Up @@ -36,6 +36,10 @@ impl Client {
self.post(&path, Some(credentials)).await
}

pub async fn list_invalid_project_names(&self) -> Result<Vec<(String, Vec<String>)>> {
self.get("/admin/invalid-names").await
}

async fn post<T: Serialize, R: DeserializeOwned>(
&self,
path: &str,
Expand All @@ -59,4 +63,16 @@ impl Client {
.await
.context("failed to extract json body from post response")
}

async fn get<R: DeserializeOwned>(&self, path: &str) -> Result<R> {
reqwest::Client::new()
.get(format!("{}{}", self.api_url, path))
.bearer_auth(&self.api_key)
.send()
.await
.context("failed to make post request")?
.to_json()
.await
.context("failed to post text body from response")
}
}
20 changes: 20 additions & 0 deletions admin/src/main.rs
Original file line number Diff line number Diff line change
Expand Up @@ -46,6 +46,26 @@ async fn main() {
.await
.expect("to get a certificate challenge response")
}
Command::ProjectNames => {
let projects = client
.list_invalid_project_names()
.await
.expect("get invalid project names");

let mut res = String::new();

for (project, issues) in projects {
writeln!(res, "{project}").expect("to write name of project name having issues");

for issue in issues {
writeln!(res, "\t- {issue}").expect("to write issue with project name");
}

writeln!(res).expect("to write a new line");
}

res
}
};

println!("{res}");
Expand Down
83 changes: 83 additions & 0 deletions gateway/src/api/latest.rs
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
use std::collections::HashMap;
use std::sync::Arc;
use std::time::Duration;

Expand Down Expand Up @@ -220,6 +221,87 @@ async fn request_acme_certificate(
Ok("Certificate created".to_string())
}

async fn invalid_project_names(
_: Admin,
Extension(service): Extension<Arc<GatewayService>>,
) -> Result<AxumJson<Vec<(String, Vec<String>)>>, Error> {
let projects: HashMap<String, String, std::collections::hash_map::RandomState> =
HashMap::from_iter(
service
.iter_projects_with_user()
.await?
.map(|(project_name, account_name)| (project_name.0, account_name.0)),
);
let mut result = Vec::with_capacity(projects.len());

for (project_name, account_name) in &projects {
let mut output = Vec::new();
let cleaned_name = project_name.to_lowercase();

// Were there any uppercase characters
if &cleaned_name != project_name {
// Since there were uppercase characters, will the new name clash with any existing projects
if let Some(other_account) = projects.get(&cleaned_name) {
if other_account == account_name {
output.push("changing to lower case will clash with same owner".to_string());
} else {
output.push(format!(
"changing to lower case will clash with another owner: {other_account}"
));
}
}
}

let cleaned_underscore = cleaned_name.replace('_', "-");
// Were there any underscore cleanups
if cleaned_underscore != cleaned_name {
// Since there were underscore cleanups, will the new name clash with any existing projects
if let Some(other_account) = projects.get(&cleaned_underscore) {
if other_account == account_name {
output.push("cleaning underscore will clash with same owner".to_string());
} else {
output.push(format!(
"cleaning underscore will clash with another owner: {other_account}"
));
}
}
}

let cleaned_separator_name = cleaned_underscore.trim_matches('-');
// Were there any dash cleanups
if cleaned_separator_name != cleaned_underscore {
// Since there were dash cleanups, will the new name clash with any existing projects
if let Some(other_account) = projects.get(cleaned_separator_name) {
if other_account == account_name {
output.push("cleaning dashes will clash with same owner".to_string());
} else {
output.push(format!(
"cleaning dashes will clash with another owner: {other_account}"
));
}
}
}

// Are reserved words used
match cleaned_separator_name {
"shuttleapp" | "shuttle" => output.push("is a reserved name".to_string()),
_ => {}
}

// Is it longer than 63 chars
if cleaned_separator_name.len() > 63 {
output.push("final name is too long".to_string());
}

// Only report of problem projects
if !output.is_empty() {
result.push((project_name.to_string(), output));
}
}

Ok(AxumJson(result))
}

pub fn make_api(
service: Arc<GatewayService>,
acme_client: AcmeClient,
Expand All @@ -241,6 +323,7 @@ pub fn make_api(
.route("/admin/revive", post(revive_projects))
.route("/admin/acme/:email", post(create_acme_account))
.route("/admin/acme/request/:project_name/:fqdn", post(request_acme_certificate))
.route("/admin/invalid-names", get(invalid_project_names))
.layer(Extension(service))
.layer(Extension(acme_client))
.layer(Extension(sender))
Expand Down
24 changes: 24 additions & 0 deletions gateway/src/service.rs
Original file line number Diff line number Diff line change
Expand Up @@ -496,6 +496,22 @@ impl GatewayService {
Ok(custom_domain)
}

pub async fn iter_projects_with_user(
&self,
) -> Result<impl Iterator<Item = (ProjectName, AccountName)>, Error> {
let iter = query("SELECT project_name, account_name FROM projects")
.fetch_all(&self.db)
.await?
.into_iter()
.map(|row| {
(
row.try_get("project_name").unwrap(),
row.try_get("account_name").unwrap(),
)
});
Ok(iter)
}

pub fn context(&self) -> GatewayContext {
self.provider.context()
}
Expand Down Expand Up @@ -613,6 +629,14 @@ pub mod tests {
assert!(creating_same_project_name(&project, &matrix));

assert_eq!(svc.find_project(&matrix).await.unwrap(), project);
assert_eq!(
svc.iter_projects_with_user()
.await
.unwrap()
.next()
.expect("to get one project with its user"),
(matrix.clone(), neo.clone())
);

let mut work = svc
.new_task()
Expand Down

0 comments on commit 91df837

Please sign in to comment.