Deployment and lifecycle management for Nix
0
fork

Configure Feed

Select the types of activity you want to include in your feed.

feat: serve cache config to client, use substituters when realizing

+101 -6
+3 -3
client/src/main.rs
··· 234 234 .bootstrap_token_file(cli.bootstrap_token_file) 235 235 .bootstrap_token(); 236 236 237 - let sower = Sower::new(&config)?; 237 + let sower = Sower::new(&config).await?; 238 238 239 239 match &cli.action { 240 240 Actions::Daemon {} => { ··· 258 258 } 259 259 260 260 SeedCommands::Download {} => { 261 - seed.realize().expect("failed to realize"); 261 + seed.realize(sower.cache_args()).expect("failed to realize"); 262 262 } 263 263 } 264 264 } ··· 284 284 Some(desired) => { 285 285 info!("Activating seed {:?}", &desired); 286 286 desired 287 - .realize() 287 + .realize(sower.cache_args()) 288 288 .expect("failed to realize") 289 289 .activate(mode) 290 290 .expect("failed to activate");
+84 -3
client/src/sower.rs
··· 22 22 } 23 23 24 24 impl Seed { 25 - pub fn realize(&self) -> Result<&Self> { 26 - match run_command("nix-store", vec!["--realize", &self.out_path.clone()]) { 25 + pub fn realize(&self, args: Vec<String>) -> Result<&Self> { 26 + let out_path = self.out_path.clone(); 27 + let realize_args = ["--realize", &out_path]; 28 + let all_args = [ 29 + &args.iter().map(|s| s.as_str()).collect::<Vec<&str>>()[..], 30 + &realize_args[..], 31 + ] 32 + .concat(); 33 + 34 + match run_command("nix-store", all_args) { 27 35 true => Ok(self), 28 36 false => Err(anyhow!("{}", &self.out_path)), 29 37 } ··· 142 150 None, 143 151 } 144 152 153 + #[derive(Clone, Debug, Deserialize, Serialize)] 154 + pub struct NixCache { 155 + pub url: String, 156 + pub public_key: String, 157 + } 158 + 145 159 #[derive(Clone, Debug, Deserialize)] 146 160 pub struct Sower { 147 161 pub url: String, 148 162 pub api_url: String, 149 163 pub channels_url: String, 164 + pub config: SowerConfig, 165 + } 166 + 167 + #[derive(Clone, Debug, Deserialize, Serialize)] 168 + pub struct SowerConfig { 169 + pub nix_caches: Vec<NixCache>, 150 170 } 151 171 152 172 impl Sower { 153 - pub fn new(config: &Config) -> Result<Sower> { 173 + pub async fn new(config: &Config) -> Result<Sower> { 154 174 let url = config.url.clone().expect("URL is required"); 155 175 let api_url = format!("{}/api", url); 156 176 let channels_url = format!("{}/client/websocket", url.replace("http", "ws")); 157 177 178 + let config = Self::fetch_config(api_url.clone()).await; 179 + 180 + debug!("{:?}", &config); 181 + 158 182 Ok(Self { 159 183 url, 160 184 api_url, 161 185 channels_url, 186 + config: config.expect("failed to load configuration"), 162 187 }) 163 188 } 164 189 190 + pub fn cache_args(&self) -> Vec<String> { 191 + if !self.config.nix_caches.is_empty() { 192 + let public_keys = self 193 + .config 194 + .nix_caches 195 + .clone() 196 + .into_iter() 197 + .map(|nc| nc.public_key) 198 + .collect::<Vec<String>>() 199 + .join(","); 200 + 201 + let substituters = self 202 + .config 203 + .nix_caches 204 + .clone() 205 + .into_iter() 206 + .map(|nc| nc.url) 207 + .collect::<Vec<String>>() 208 + .join(","); 209 + 210 + debug!("{:?}", public_keys); 211 + 212 + vec![ 213 + "--extra-substituters".to_string(), 214 + substituters, 215 + "--extra-trusted-public-keys".to_string(), 216 + public_keys, 217 + ] 218 + } else { 219 + vec![] 220 + } 221 + } 222 + 165 223 pub async fn find_seed(&self, name: String, seed_type: SeedType) -> Option<Seed> { 166 224 let client = reqwest::Client::new(); 167 225 ··· 177 235 Ok(seed) => Some(seed), 178 236 Err(err) => { 179 237 error!("failed to get seed: {}", err); 238 + None 239 + } 240 + } 241 + } 242 + Err(err) => { 243 + error!("failed to get seed: {}", err); 244 + None 245 + } 246 + } 247 + } 248 + 249 + async fn fetch_config(api_url: String) -> Option<SowerConfig> { 250 + let client = reqwest::Client::new(); 251 + 252 + match client.get(format!("{}/config", api_url)).send().await { 253 + Ok(result) => { 254 + debug!("{:?}", result); 255 + match result.json::<SowerConfig>().await { 256 + Ok(config) => Some(config), 257 + Err(err) => { 258 + error!("failed to get sower configuration: {}", err); 180 259 None 181 260 } 182 261 } ··· 370 449 } 371 450 372 451 fn run_command(command: &str, args: Vec<&str>) -> bool { 452 + debug!("Running command: {} {}", &command, &args.join(" ")); 453 + 373 454 let status = &mut Command::new(command) 374 455 .args(args) 375 456 .status()
+12
lib/sower_web/controllers/app_controller.ex
··· 24 24 end 25 25 end 26 26 end 27 + 28 + # TODO: this should really be somewhere related to an api 29 + def config(conn, _params) do 30 + with {:ok, nix_caches} <- Application.fetch_env(:sower, :nix_caches) do 31 + # convert back to list of maps 32 + nix_caches = nix_caches |> Enum.map(&(&1 |> Enum.into(%{}))) 33 + 34 + conn 35 + |> put_root_layout(false) 36 + |> json(%{nix_caches: nix_caches}) 37 + end 38 + end 27 39 end
+2
lib/sower_web/router.ex
··· 46 46 47 47 scope "/api" do 48 48 pipe_through :api 49 + get "/config", SowerWeb.AppController, :config 50 + 49 51 get "/seeds", SowerWeb.SeedController, :list 50 52 get "/seeds/latest", SowerWeb.SeedController, :find_latest 51 53 post "/seeds", SowerWeb.SeedController, :new