summaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
Diffstat (limited to 'src')
-rw-r--r--src/main.rs298
1 files changed, 276 insertions, 22 deletions
diff --git a/src/main.rs b/src/main.rs
index 8933c96..7cdcbaa 100644
--- a/src/main.rs
+++ b/src/main.rs
@@ -18,7 +18,9 @@ fn help(cmd: &str) {
Search FILE for docker images and suggest updates
Options:
---auth <REGISTRY> <USER> <PASS> Authenticate against REGISTRY (repeatable)",
+--auth <REGISTRY> <USER> <PASS> Authenticate against REGISTRY (repeatable)
+--config <PATH> Read config from PATH
+--scratch <DIR> Store temporary files in DIR",
cmd
);
}
@@ -50,10 +52,13 @@ impl AuthMethod {
}
}
+fn basic_auth(user: &str, pass: &str) -> String {
+ return BASE64_STANDARD.encode(format!("{}:{}", user, pass));
+}
+
struct AuthInfo {
host: String,
- username: String,
- password: String,
+ auth: String,
}
enum AuthStage {
@@ -106,13 +111,13 @@ impl AuthState{
let auth_header = response.headers().get("www-authenticate").unwrap();
match AuthMethod::from_header(auth_header) {
Some(AuthMethod::Basic) => {
- let basic_auth = format!("Basic {}", BASE64_STANDARD.encode(format!("{}:{}", self.info.username, self.info.password)));
+ let basic_auth = format!("Basic {}", self.info.auth);
self.stage = AuthStage::Authorized(basic_auth);
return Ok(());
},
Some(AuthMethod::Bearer{realm, scope, service}) => {
- let basic_auth = format!("Basic {}", BASE64_STANDARD.encode(format!("{}:{}", self.info.username, self.info.password)));
+ let basic_auth = format!("Basic {}", self.info.auth);
let url = format!("{}?service={}&scope={}", realm, service, scope);
@@ -170,6 +175,12 @@ fn perform_registry_request(registry: &str, url: &str, accept: &'static str, aut
return Err("Authorization failed".to_string());
}
+struct Repository {
+ url: String,
+ key: Option<std::path::PathBuf>,
+ dest: std::path::PathBuf,
+}
+
enum YContext {
InDocument,
InObject,
@@ -511,6 +522,8 @@ fn main() {
let mut auths = vec![];
let mut positional: Vec<&str> = vec!();
let mut overwrite = false;
+ let mut config_path = None;
+ let mut scratch_path = None;
loop {
match it.next().map(|x| x.as_str()) {
@@ -519,8 +532,7 @@ fn main() {
if let Some(registry) = it.next() && let Some(username) = it.next() && let Some(password) = it.next() {
auths.push(AuthInfo {
host: registry.clone(),
- username: username.clone(),
- password: password.clone(),
+ auth: basic_auth(username, password),
});
} else {
println!("Error: --auth requires three parameters");
@@ -528,6 +540,24 @@ fn main() {
std::process::exit(1);
}
},
+ Some("--config") => {
+ if let Some(path) = it.next() {
+ config_path = Some(std::path::PathBuf::from(path));
+ } else {
+ println!("Error: --config requires an parameters");
+ help(cmd);
+ std::process::exit(1);
+ }
+ },
+ Some("--scratch") => {
+ if let Some(path) = it.next() {
+ scratch_path = Some(std::path::PathBuf::from(path));
+ } else {
+ println!("Error: --scratch requires an parameters");
+ help(cmd);
+ std::process::exit(1);
+ }
+ },
Some("-i") | Some("--inplace") => {
overwrite = true;
},
@@ -539,16 +569,20 @@ fn main() {
};
}
-
- if positional.len() != 1 {
- panic!("Bad arguments");
+ let mut infile_paths = vec![];
+ if positional.len() == 1 {
+ infile_paths.push(std::path::PathBuf::from(positional[0]));
}
- let infile_path = std::path::PathBuf::from(positional[0]);
- let mut auth = Auth::new(auths);
- let inpaths = if infile_path.is_dir() {
- let mut unsearched = vec![infile_path.clone()];
- let mut paths = vec![];
+ let mut repos = vec![];
+
+ let workdir = std::env::current_dir().unwrap();
+ if let Some(config_path) = config_path {
+ if !config_path.is_dir() {
+ std::process::exit(1);
+ }
+
+ let mut unsearched = vec![config_path];
while let Some(next) = unsearched.pop() {
for child in next.read_dir().unwrap() {
@@ -561,18 +595,201 @@ fn main() {
continue;
}
- if let Some(ext) = path.extension() {
- if ext == "yaml" {
- paths.push(path);
+ if let Some(name) = path.file_name() {
+ match name.to_str() {
+ Some(".dockerconfigjson") => {
+ let file = std::fs::File::open(&path).unwrap();
+ let mut reader = json_event_parser::ReaderJsonParser::new(file);
+
+ match reader.parse_next() {
+ Ok(json_event_parser::JsonEvent::StartObject) => {},
+ _ => panic!("Invalid config json"),
+ }
+
+ match reader.parse_next() {
+ Ok(json_event_parser::JsonEvent::ObjectKey(k)) if &k == "auths" => {},
+ _ => panic!("Invalid config json"),
+ }
+
+ match reader.parse_next() {
+ Ok(json_event_parser::JsonEvent::StartObject) => {},
+ _ => panic!("Invalid config json"),
+ }
+
+ let mut possible_key = reader.parse_next();
+ while let Ok(json_event_parser::JsonEvent::ObjectKey(k)) = possible_key {
+ let key = k.into_owned();
+
+ match reader.parse_next() {
+ Ok(json_event_parser::JsonEvent::StartObject) => {},
+ _ => panic!("Invalid config json"),
+ }
+
+ match reader.parse_next() {
+ Ok(json_event_parser::JsonEvent::ObjectKey(k)) if &k == "auth" => {},
+ _ => panic!("Invalid config json"),
+ }
+
+ match reader.parse_next() {
+ Ok(json_event_parser::JsonEvent::String(v)) => {
+ auths.push(AuthInfo {
+ host: key,
+ auth: v.into_owned(),
+ });
+ },
+ _ => panic!("Invalid config json"),
+ }
+
+ match reader.parse_next() {
+ Ok(json_event_parser::JsonEvent::EndObject) => {},
+ _ => panic!("Invalid config json"),
+ }
+
+ possible_key = reader.parse_next();
+ }
+
+ match possible_key {
+ Ok(json_event_parser::JsonEvent::EndObject) => {},
+ _ => panic!("Invalid config json"),
+ }
+
+ match reader.parse_next() {
+ Ok(json_event_parser::JsonEvent::EndObject) => {},
+ _ => panic!("Invalid config json"),
+ }
+ }
+ Some("repository") => {
+ let file = std::fs::File::open(&path).unwrap();
+ let mut reader = json_event_parser::ReaderJsonParser::new(file);
+
+ match reader.parse_next() {
+ Ok(json_event_parser::JsonEvent::StartObject) => {},
+ _ => panic!("Invalid config json"),
+ }
+
+ match reader.parse_next() {
+ Ok(json_event_parser::JsonEvent::ObjectKey(k)) if &k == "repositories" => {},
+ _ => panic!("Invalid config json"),
+ }
+
+ match reader.parse_next() {
+ Ok(json_event_parser::JsonEvent::StartObject) => {},
+ _ => panic!("Invalid config json"),
+ }
+
+ let mut possible_key = reader.parse_next();
+ while let Ok(json_event_parser::JsonEvent::ObjectKey(k)) = possible_key {
+ let k = k.into_owned();
+ match reader.parse_next() {
+ Ok(json_event_parser::JsonEvent::StartObject) => {},
+ _ => panic!("Invalid config json"),
+ }
+
+ match reader.parse_next() {
+ Ok(json_event_parser::JsonEvent::ObjectKey(k)) if &k == "key" => {},
+ _ => panic!("Invalid config json"),
+ }
+
+ let key_name = match reader.parse_next() {
+ Ok(json_event_parser::JsonEvent::String(v)) => {
+ v.into_owned()
+ },
+ _ => panic!("Invalid config json"),
+ };
+
+ match reader.parse_next() {
+ Ok(json_event_parser::JsonEvent::ObjectKey(k)) if &k == "dest" => {},
+ _ => panic!("Invalid config json"),
+ }
+
+ let dest = match reader.parse_next() {
+ Ok(json_event_parser::JsonEvent::String(v)) => {
+ v.into_owned()
+ },
+ _ => panic!("Invalid config json"),
+ };
+
+ match reader.parse_next() {
+ Ok(json_event_parser::JsonEvent::EndObject) => {},
+ _ => panic!("Invalid config json"),
+ }
+
+ repos.push(Repository{
+ url: k,
+ key: Some(workdir.join(path.parent().unwrap().join(std::path::PathBuf::from(key_name)))),
+ dest: scratch_path.as_ref().unwrap().join(dest),
+ });
+
+ possible_key = reader.parse_next();
+ }
+
+ match possible_key {
+ Ok(json_event_parser::JsonEvent::EndObject) => {},
+ _ => panic!("Invalid config json"),
+ }
+
+ match reader.parse_next() {
+ Ok(json_event_parser::JsonEvent::EndObject) => {},
+ _ => panic!("Invalid config json"),
+ }
+ }
+ Some(_) | None => {}
}
}
}
}
+ }
+
+ for repo in &repos {
+ let mut gitcmd = std::process::Command::new("git");
+
+ gitcmd
+ .arg("clone")
+ .arg(&repo.url)
+ .arg(&repo.dest);
+
+ if let Some(key) = &repo.key {
+ gitcmd.env("GIT_SSH_COMMAND", format!("ssh -F none -o IdentitiesOnly=yes -i \"{}\"", key.to_str().unwrap()));
+ }
+
+ let exit = gitcmd.status()
+ .expect("Git command failed");
+ if !exit.success() {
+ panic!("Git exited with failure");
+ }
+
+ infile_paths.push(repo.dest.clone());
+ }
+
+ let mut inpaths = vec!();
+ for infile_path in infile_paths {
+ if infile_path.is_dir() {
+ let mut unsearched = vec![infile_path.clone()];
+
+ while let Some(next) = unsearched.pop() {
+ for child in next.read_dir().unwrap() {
+ let child = child.unwrap();
+ let path = child.path();
+
+ let ft = child.file_type().unwrap();
+ if ft.is_dir() {
+ unsearched.push(path);
+ continue;
+ }
- paths
- } else {
- vec![infile_path]
- };
+ if let Some(ext) = path.extension() {
+ if ext == "yaml" {
+ inpaths.push(path);
+ }
+ }
+ }
+ }
+ } else {
+ inpaths.push(infile_path);
+ };
+ }
+
+ let mut auth = Auth::new(auths);
for infile_path in inpaths {
let mut file = std::fs::File::open(&infile_path).unwrap();
@@ -628,4 +845,41 @@ fn main() {
out.commit();
}
+
+ for repo in &repos {
+ let mut gitcmd = std::process::Command::new("git");
+
+ gitcmd
+ .arg("-C")
+ .arg(&repo.dest)
+ .arg("commit")
+ .arg("--all")
+ .arg("--message")
+ .arg("Update versions");
+
+ let exit = gitcmd.status()
+ .expect("Git command failed");
+ if !exit.success() {
+ println!("Commit failed, presumably there were no changes");
+ continue;
+ }
+
+ let mut gitcmd = std::process::Command::new("git");
+ gitcmd
+ .arg("-C")
+ .arg(&repo.dest)
+ .arg("push")
+ .arg("origin")
+ .arg("+HEAD:version-bump");
+
+ if let Some(key) = &repo.key {
+ gitcmd.env("GIT_SSH_COMMAND", format!("ssh -F none -o IdentitiesOnly=yes -i \"{}\"", key.to_str().unwrap()));
+ }
+
+ let exit = gitcmd.status()
+ .expect("Git command failed");
+ if !exit.success() {
+ panic!("Git exited with failure");
+ }
+ }
}