Skip to content

Where people and agents ship software together. The open-source git platform for the whole job: issues, agents, checks and deploys to the edge.

g1t.sh

Commit

A deploy build reads g1t's reason when a step is refused: ureq makes any status over 399 an error, so the registry step's 409 not_enabled (g1t.page runs no containers yet) failed the build as "could not report registry to g1t: status code 409" before the build could fall back to the project's site, or say Docker deployments are not switched on. Tests answer a step with a 409 and a 502.

syntaqxcommitted Parent44d681bBrowse files
1 file+62−40/1 viewed
+62−4
109109 impl Reporter {
110110 fn send(&self, step: &str, mut body: Value) -> Result<Value> {
111111 body["token"] = self.token.clone().into();
112− let response = ureq::post(&format!("{}/{step}", self.base))
113− .send_json(body)
114− .with_context(|| format!("could not report `{step}` to g1t"))?;
115− Ok(response.into_json().unwrap_or(Value::Null))
112+ match ureq::post(&format!("{}/{step}", self.base)).send_json(body) {
113+ Ok(response) => Ok(response.into_json().unwrap_or(Value::Null)),
114+ // A refusal (409 `not_enabled`, say) still says why in its
115+ // body, which the step reads: ureq makes any status over 399
116+ // an error.
117+ Err(ureq::Error::Status(status, response)) => match response.into_json::<Value>() {
118+ Ok(answer) if answer.get("ok").is_some() => Ok(answer),
119+ _ => bail!("could not report `{step}` to g1t: status code {status}"),
120+ },
121+ Err(error) => Err(error).with_context(|| format!("could not report `{step}` to g1t")),
122+ }
116123 }
117124 }
118125
15351542 mod tests {
15361543 use super::*;
15371544
1545+ /// A server that answers one request with `status` and `body`.
1546+ fn answering(status: &str, body: &'static str) -> String {
1547+ use std::io::{Read, Write};
1548+ let listener = std::net::TcpListener::bind("127.0.0.1:0").unwrap();
1549+ let base = format!("http://{}", listener.local_addr().unwrap());
1550+ let status = status.to_owned();
1551+ std::thread::spawn(move || {
1552+ let (mut stream, _) = listener.accept().unwrap();
1553+ // The whole request first (its headers, then its body by
1554+ // length), or the client may still be writing when the answer
1555+ // closes the connection.
1556+ let mut seen = Vec::new();
1557+ let mut buf = [0; 4096];
1558+ loop {
1559+ let n = stream.read(&mut buf).unwrap_or(0);
1560+ if n == 0 {
1561+ break;
1562+ }
1563+ seen.extend_from_slice(&buf[..n]);
1564+ let text = String::from_utf8_lossy(&seen);
1565+ if let Some(end) = text.find("\r\n\r\n") {
1566+ let length = text[..end]
1567+ .lines()
1568+ .find_map(|l| l.to_ascii_lowercase().strip_prefix("content-length:").map(|v| v.trim().parse::<usize>().unwrap_or(0)))
1569+ .unwrap_or(0);
1570+ if seen.len() >= end + 4 + length {
1571+ break;
1572+ }
1573+ }
1574+ }
1575+ let reply = format!("HTTP/1.1 {status}\r\ncontent-type: application/json\r\ncontent-length: {}\r\nconnection: close\r\n\r\n{body}", body.len());
1576+ stream.write_all(reply.as_bytes()).unwrap();
1577+ });
1578+ base
1579+ }
1580+
1581+ #[test]
1582+ fn a_refusal_reaches_the_step_with_its_reason() {
1583+ let base = answering("409 Conflict", r#"{"ok":false,"error":{"code":"not_enabled","message":"Docker deployments are not switched on for g1t.page yet."}}"#);
1584+ let answer = Reporter { base, token: "t".into() }.send("registry", json!({})).unwrap();
1585+ assert_eq!(answer["ok"], false);
1586+ assert_eq!(answer["error"]["code"], "not_enabled");
1587+ }
1588+
1589+ #[test]
1590+ fn a_failure_without_an_answer_says_its_status() {
1591+ let base = answering("502 Bad Gateway", "upstream down");
1592+ let error = Reporter { base, token: "t".into() }.send("registry", json!({})).unwrap_err();
1593+ assert!(format!("{error:#}").contains("status code 502"), "{error:#}");
1594+ }
1595+
15381596 #[test]
15391597 fn the_upload_session_is_read_in_either_spelling() {
15401598 for answer in [

Comments

Sign in to comment.