1use crate::ec2::{
4 aws::*, deployer_directory, services::*, utils::*, Config, Error, Host, Hosts, InstanceConfig,
5 CREATED_FILE_NAME, LOGS_PORT, MONITORING_NAME, MONITORING_REGION, PROFILES_PORT, TRACES_PORT,
6};
7use futures::future::try_join_all;
8use std::{
9 collections::{BTreeSet, HashMap, HashSet},
10 fs::File,
11 net::IpAddr,
12 path::PathBuf,
13};
14use tokio::process::Command;
15use tracing::info;
16
17#[derive(Clone)]
19pub struct Deployment {
20 pub instance: InstanceConfig,
21 pub id: String,
22 pub ip: String,
23}
24
25pub struct RegionResources {
27 pub vpc_id: String,
28 pub vpc_cidr: String,
29 pub route_table_id: String,
30 pub subnet_id: String,
31 pub binary_sg_id: Option<String>,
32 pub monitoring_sg_id: Option<String>,
33}
34
35pub async fn create(config: &PathBuf) -> Result<(), Error> {
37 let config: Config = {
39 let config_file = File::open(config)?;
40 serde_yaml::from_reader(config_file)?
41 };
42 let tag = &config.tag;
43 info!(tag = tag.as_str(), "loaded configuration");
44
45 let tag_directory = deployer_directory(tag);
47 if tag_directory.exists() {
48 return Err(Error::CreationAttempted);
49 }
50 std::fs::create_dir_all(&tag_directory)?;
51 info!(path = ?tag_directory, "created tag directory");
52
53 let mut instance_names = HashSet::new();
55 for instance in &config.instances {
56 if instance_names.contains(&instance.name) || instance.name == MONITORING_NAME {
57 return Err(Error::InvalidInstanceName(instance.name.clone()));
58 }
59 instance_names.insert(instance.name.clone());
60 }
61
62 let deployer_ip = get_public_ip().await?;
64 info!(ip = deployer_ip.as_str(), "recovered public IP");
65
66 let key_name = format!("deployer-{tag}");
68 let private_key_path = tag_directory.join(format!("id_rsa_{tag}"));
69 let public_key_path = tag_directory.join(format!("id_rsa_{tag}.pub"));
70 let output = Command::new("ssh-keygen")
71 .arg("-t")
72 .arg("rsa")
73 .arg("-b")
74 .arg("4096")
75 .arg("-f")
76 .arg(private_key_path.to_str().unwrap())
77 .arg("-N")
78 .arg("")
79 .output()
80 .await?;
81 if !output.status.success() {
82 return Err(Error::KeygenFailed);
83 }
84 let public_key = std::fs::read_to_string(&public_key_path)?;
85 let private_key = private_key_path.to_str().unwrap();
86
87 let mut regions: BTreeSet<String> = config.instances.iter().map(|i| i.region.clone()).collect();
89 regions.insert(MONITORING_REGION.to_string());
90
91 let mut instance_types_by_region: HashMap<String, HashSet<String>> = HashMap::new();
93 for instance in &config.instances {
94 instance_types_by_region
95 .entry(instance.region.clone())
96 .or_default()
97 .insert(instance.instance_type.clone());
98 }
99 instance_types_by_region
100 .entry(MONITORING_REGION.to_string())
101 .or_default()
102 .insert(config.monitoring.instance_type.clone());
103
104 info!(?regions, "initializing resources");
106 let mut vpc_cidrs = HashMap::new();
107 let mut subnet_cidrs = HashMap::new();
108 let mut ec2_clients = HashMap::new();
109 let mut region_resources = HashMap::new();
110 for (idx, region) in regions.iter().enumerate() {
111 let ec2_client = create_ec2_client(Region::new(region.clone())).await;
113 ec2_clients.insert(region.clone(), ec2_client);
114 info!(region = region.as_str(), "created EC2 client");
115
116 let instance_types: Vec<String> =
118 instance_types_by_region[region].iter().cloned().collect();
119 assert_arm64_support(&ec2_clients[region], &instance_types).await?;
120
121 let az = find_availability_zone(&ec2_clients[region], &instance_types).await?;
123 info!(
124 az = az.as_str(),
125 region = region.as_str(),
126 "selected availability zone"
127 );
128
129 let vpc_cidr = format!("10.{idx}.0.0/16");
131 vpc_cidrs.insert(region.clone(), vpc_cidr.clone());
132 let vpc_id = create_vpc(&ec2_clients[region], &vpc_cidr, tag).await?;
133 info!(
134 vpc = vpc_id.as_str(),
135 region = region.as_str(),
136 "created VPC"
137 );
138 let igw_id = create_and_attach_igw(&ec2_clients[region], &vpc_id, tag).await?;
139 info!(
140 igw = igw_id.as_str(),
141 vpc = vpc_id.as_str(),
142 region = region.as_str(),
143 "created and attached IGW"
144 );
145 let route_table_id =
146 create_route_table(&ec2_clients[region], &vpc_id, &igw_id, tag).await?;
147 info!(
148 route_table = route_table_id.as_str(),
149 vpc = vpc_id.as_str(),
150 region = region.as_str(),
151 "created route table"
152 );
153 let subnet_cidr = format!("10.{idx}.1.0/24");
154 subnet_cidrs.insert(region.clone(), subnet_cidr.clone());
155 let subnet_id = create_subnet(
156 &ec2_clients[region],
157 &vpc_id,
158 &route_table_id,
159 &subnet_cidr,
160 &az,
161 tag,
162 )
163 .await?;
164 info!(
165 subnet = subnet_id.as_str(),
166 vpc = vpc_id.as_str(),
167 region = region.as_str(),
168 "created subnet"
169 );
170
171 let monitoring_sg_id = if *region == MONITORING_REGION {
173 let sg_id =
174 create_security_group_monitoring(&ec2_clients[region], &vpc_id, &deployer_ip, tag)
175 .await?;
176 info!(
177 sg = sg_id.as_str(),
178 vpc = vpc_id.as_str(),
179 region = region.as_str(),
180 "created monitoring security group"
181 );
182 Some(sg_id)
183 } else {
184 None
185 };
186
187 import_key_pair(&ec2_clients[region], &key_name, &public_key).await?;
189 info!(
190 key = key_name.as_str(),
191 region = region.as_str(),
192 "imported key pair"
193 );
194
195 info!(
197 vpc = vpc_id.as_str(),
198 subnet = subnet_id.as_str(),
199 subnet_cidr = subnet_cidr.as_str(),
200 region = region.as_str(),
201 "initialized resources"
202 );
203 region_resources.insert(
204 region.clone(),
205 RegionResources {
206 vpc_id,
207 vpc_cidr: vpc_cidr.clone(),
208 route_table_id,
209 subnet_id,
210 binary_sg_id: None,
211 monitoring_sg_id,
212 },
213 );
214 }
215 info!(?regions, "initialized resources");
216
217 info!("initializing VPC peering connections");
219 let monitoring_region = MONITORING_REGION.to_string();
220 let monitoring_resources = region_resources.get(&monitoring_region).unwrap();
221 let monitoring_vpc_id = &monitoring_resources.vpc_id;
222 let monitoring_cidr = &monitoring_resources.vpc_cidr;
223 let binary_regions: HashSet<String> =
224 config.instances.iter().map(|i| i.region.clone()).collect();
225 for region in ®ions {
226 if region != &monitoring_region && binary_regions.contains(region) {
227 let binary_resources = region_resources.get(region).unwrap();
228 let binary_vpc_id = &binary_resources.vpc_id;
229 let binary_cidr = &binary_resources.vpc_cidr;
230 let peer_id = create_vpc_peering_connection(
231 &ec2_clients[&monitoring_region],
232 monitoring_vpc_id,
233 binary_vpc_id,
234 region,
235 tag,
236 )
237 .await?;
238 info!(
239 peer = peer_id.as_str(),
240 monitoring = monitoring_vpc_id.as_str(),
241 binary = binary_vpc_id.as_str(),
242 region = region.as_str(),
243 "created VPC peering connection"
244 );
245 wait_for_vpc_peering_connection(&ec2_clients[region], &peer_id).await?;
246 info!(
247 peer = peer_id.as_str(),
248 region = region.as_str(),
249 "VPC peering connection is available"
250 );
251 accept_vpc_peering_connection(&ec2_clients[region], &peer_id).await?;
252 info!(
253 peer = peer_id.as_str(),
254 region = region.as_str(),
255 "accepted VPC peering connection"
256 );
257 add_route(
258 &ec2_clients[&monitoring_region],
259 &monitoring_resources.route_table_id,
260 binary_cidr,
261 &peer_id,
262 )
263 .await?;
264 add_route(
265 &ec2_clients[region],
266 &binary_resources.route_table_id,
267 monitoring_cidr,
268 &peer_id,
269 )
270 .await?;
271 info!(
272 peer = peer_id.as_str(),
273 monitoring = monitoring_vpc_id.as_str(),
274 binary = binary_vpc_id.as_str(),
275 region = region.as_str(),
276 "added routes for VPC peering connection"
277 );
278 }
279 }
280 info!("initialized VPC peering connections");
281
282 info!("launching monitoring instance");
284 let monitoring_instance_id;
285 let monitoring_ip;
286 let monitoring_private_ip;
287 let monitoring_sg_id;
288 {
289 let monitoring_ec2_client = &ec2_clients[&monitoring_region];
290 let ami_id = find_latest_ami(monitoring_ec2_client).await?;
291 let monitoring_instance_type = InstanceType::try_parse(&config.monitoring.instance_type)
292 .expect("Invalid instance type");
293 let monitoring_storage_class =
294 VolumeType::try_parse(&config.monitoring.storage_class).expect("Invalid storage class");
295 monitoring_sg_id = monitoring_resources
296 .monitoring_sg_id
297 .as_ref()
298 .unwrap()
299 .clone();
300 monitoring_instance_id = launch_instances(
301 monitoring_ec2_client,
302 &ami_id,
303 monitoring_instance_type,
304 config.monitoring.storage_size,
305 monitoring_storage_class,
306 &key_name,
307 &monitoring_resources.subnet_id,
308 &monitoring_sg_id,
309 1,
310 MONITORING_NAME,
311 tag,
312 )
313 .await?[0]
314 .clone();
315 monitoring_ip =
316 wait_for_instances_running(monitoring_ec2_client, &[monitoring_instance_id.clone()])
317 .await?[0]
318 .clone();
319 monitoring_private_ip =
320 get_private_ip(monitoring_ec2_client, &monitoring_instance_id).await?;
321 }
322 info!(ip = monitoring_ip.as_str(), "launched monitoring instance");
323
324 info!("creating security groups");
326 for (region, resources) in region_resources.iter_mut() {
327 let binary_sg_id = create_security_group_binary(
328 &ec2_clients[region],
329 &resources.vpc_id,
330 &deployer_ip,
331 &monitoring_ip,
332 tag,
333 &config.ports,
334 )
335 .await?;
336 info!(
337 sg = binary_sg_id.as_str(),
338 vpc = resources.vpc_id.as_str(),
339 region = region.as_str(),
340 "created binary security group"
341 );
342 resources.binary_sg_id = Some(binary_sg_id);
343 }
344 info!("created security groups");
345
346 info!("launching binary instances");
348 let mut launch_futures = Vec::new();
349 for instance in &config.instances {
350 let key_name = key_name.clone();
351 let region = instance.region.clone();
352 let resources = region_resources.get(®ion).unwrap();
353 let ec2_client = ec2_clients.get(®ion).unwrap();
354 let ami_id = find_latest_ami(ec2_client).await?;
355 let instance_type =
356 InstanceType::try_parse(&instance.instance_type).expect("Invalid instance type");
357 let storage_class =
358 VolumeType::try_parse(&instance.storage_class).expect("Invalid storage class");
359 let binary_sg_id = resources.binary_sg_id.as_ref().unwrap();
360 let tag = tag.clone();
361 let future = async move {
362 let instance_id = launch_instances(
363 ec2_client,
364 &ami_id,
365 instance_type,
366 instance.storage_size,
367 storage_class,
368 &key_name,
369 &resources.subnet_id,
370 binary_sg_id,
371 1,
372 &instance.name,
373 &tag,
374 )
375 .await?[0]
376 .clone();
377 let ip =
378 wait_for_instances_running(ec2_client, &[instance_id.clone()]).await?[0].clone();
379 info!(
380 ip = ip.as_str(),
381 instance = instance.name.as_str(),
382 "launched instance"
383 );
384 Ok::<Deployment, Error>(Deployment {
385 instance: instance.clone(),
386 id: instance_id,
387 ip,
388 })
389 };
390 launch_futures.push(future);
391 }
392 let deployments: Vec<Deployment> = try_join_all(launch_futures).await?;
393 info!("launched binary instances");
394
395 let prometheus_service_path = tag_directory.join("prometheus.service");
397 std::fs::write(&prometheus_service_path, PROMETHEUS_SERVICE)?;
398 let loki_service_path = tag_directory.join("loki.service");
399 std::fs::write(&loki_service_path, LOKI_SERVICE)?;
400 let pyroscope_service_path = tag_directory.join("pyroscope.service");
401 std::fs::write(&pyroscope_service_path, PYROSCOPE_SERVICE)?;
402 let tempo_service_path = tag_directory.join("tempo.service");
403 std::fs::write(&tempo_service_path, TEMPO_SERVICE)?;
404 let promtail_service_path = tag_directory.join("promtail.service");
405 std::fs::write(&promtail_service_path, PROMTAIL_SERVICE)?;
406 let node_exporter_service_path = tag_directory.join("node_exporter.service");
407 std::fs::write(&node_exporter_service_path, NODE_EXPORTER_SERVICE)?;
408 let pyroscope_agent_service_path = tag_directory.join("pyroscope-agent.service");
409 std::fs::write(&pyroscope_agent_service_path, PYROSCOPE_AGENT_SERVICE)?;
410 let pyroscope_agent_timer_path = tag_directory.join("pyroscope-agent.timer");
411 std::fs::write(&pyroscope_agent_timer_path, PYROSCOPE_AGENT_TIMER)?;
412 let binary_service_path = tag_directory.join("binary.service");
413 std::fs::write(&binary_service_path, BINARY_SERVICE)?;
414 let memleak_agent_service_path = tag_directory.join("memleak-agent.service");
415 std::fs::write(&memleak_agent_service_path, MEMLEAK_AGENT_SERVICE)?;
416 let memleak_agent_script_path = tag_directory.join("memleak-agent.sh");
417 std::fs::write(&memleak_agent_script_path, MEMLEAK_AGENT_SCRIPT)?;
418
419 let logrotate_conf_path = tag_directory.join("logrotate.conf");
421 std::fs::write(&logrotate_conf_path, LOGROTATE_CONF)?;
422
423 let bbr_conf_path = tag_directory.join("99-bbr.conf");
425 std::fs::write(&bbr_conf_path, BBR_CONF)?;
426
427 info!("configuring monitoring instance");
429 wait_for_instances_ready(&ec2_clients[&monitoring_region], &[monitoring_instance_id]).await?;
430 let instances: Vec<(&str, &str, &str)> = deployments
431 .iter()
432 .map(|d| {
433 (
434 d.instance.name.as_str(),
435 d.ip.as_str(),
436 d.instance.region.as_str(),
437 )
438 })
439 .collect();
440 let prom_config = generate_prometheus_config(&instances);
441 let prom_path = tag_directory.join("prometheus.yml");
442 std::fs::write(&prom_path, prom_config)?;
443 let datasources_path = tag_directory.join("datasources.yml");
444 std::fs::write(&datasources_path, DATASOURCES_YML)?;
445 let all_yaml_path = tag_directory.join("all.yml");
446 std::fs::write(&all_yaml_path, ALL_YML)?;
447 let loki_config_path = tag_directory.join("loki.yml");
448 std::fs::write(&loki_config_path, LOKI_CONFIG)?;
449 let pyroscope_config_path = tag_directory.join("pyroscope.yml");
450 std::fs::write(&pyroscope_config_path, PYROSCOPE_CONFIG)?;
451 let tempo_yml_path = tag_directory.join("tempo.yml");
452 std::fs::write(&tempo_yml_path, TEMPO_CONFIG)?;
453 rsync_file(
454 private_key,
455 prom_path.to_str().unwrap(),
456 &monitoring_ip,
457 "/home/ubuntu/prometheus.yml",
458 )
459 .await?;
460 rsync_file(
461 private_key,
462 datasources_path.to_str().unwrap(),
463 &monitoring_ip,
464 "/home/ubuntu/datasources.yml",
465 )
466 .await?;
467 rsync_file(
468 private_key,
469 all_yaml_path.to_str().unwrap(),
470 &monitoring_ip,
471 "/home/ubuntu/all.yml",
472 )
473 .await?;
474 rsync_file(
475 private_key,
476 &config.monitoring.dashboard,
477 &monitoring_ip,
478 "/home/ubuntu/dashboard.json",
479 )
480 .await?;
481 rsync_file(
482 private_key,
483 prometheus_service_path.to_str().unwrap(),
484 &monitoring_ip,
485 "/home/ubuntu/prometheus.service",
486 )
487 .await?;
488 rsync_file(
489 private_key,
490 loki_config_path.to_str().unwrap(),
491 &monitoring_ip,
492 "/home/ubuntu/loki.yml",
493 )
494 .await?;
495 rsync_file(
496 private_key,
497 loki_service_path.to_str().unwrap(),
498 &monitoring_ip,
499 "/home/ubuntu/loki.service",
500 )
501 .await?;
502 rsync_file(
503 private_key,
504 node_exporter_service_path.to_str().unwrap(),
505 &monitoring_ip,
506 "/home/ubuntu/node_exporter.service",
507 )
508 .await?;
509 rsync_file(
510 private_key,
511 pyroscope_config_path.to_str().unwrap(),
512 &monitoring_ip,
513 "/home/ubuntu/pyroscope.yml",
514 )
515 .await?;
516 rsync_file(
517 private_key,
518 pyroscope_service_path.to_str().unwrap(),
519 &monitoring_ip,
520 "/home/ubuntu/pyroscope.service",
521 )
522 .await?;
523 rsync_file(
524 private_key,
525 tempo_yml_path.to_str().unwrap(),
526 &monitoring_ip,
527 "/home/ubuntu/tempo.yml",
528 )
529 .await?;
530 rsync_file(
531 private_key,
532 tempo_service_path.to_str().unwrap(),
533 &monitoring_ip,
534 "/home/ubuntu/tempo.service",
535 )
536 .await?;
537 enable_bbr(private_key, &monitoring_ip, bbr_conf_path.to_str().unwrap()).await?;
538 ssh_execute(
539 private_key,
540 &monitoring_ip,
541 &setup_node_exporter_cmd(NODE_EXPORTER_VERSION),
542 )
543 .await?;
544 poll_service_active(private_key, &monitoring_ip, "node_exporter").await?;
545 ssh_execute(
546 private_key,
547 &monitoring_ip,
548 &install_monitoring_cmd(
549 PROMETHEUS_VERSION,
550 GRAFANA_VERSION,
551 LOKI_VERSION,
552 PYROSCOPE_VERSION,
553 TEMPO_VERSION,
554 ),
555 )
556 .await?;
557 poll_service_active(private_key, &monitoring_ip, "prometheus").await?;
558 poll_service_active(private_key, &monitoring_ip, "loki").await?;
559 poll_service_active(private_key, &monitoring_ip, "pyroscope").await?;
560 poll_service_active(private_key, &monitoring_ip, "tempo").await?;
561 poll_service_active(private_key, &monitoring_ip, "grafana-server").await?;
562 info!("configured monitoring instance");
563
564 let hosts = Hosts {
566 monitoring: monitoring_private_ip.clone().parse::<IpAddr>().unwrap(),
567 hosts: deployments
568 .iter()
569 .map(|d| Host {
570 name: d.instance.name.clone(),
571 region: d.instance.region.clone(),
572 ip: d.ip.clone().parse::<IpAddr>().unwrap(),
573 })
574 .collect(),
575 };
576 let hosts_yaml = serde_yaml::to_string(&hosts)?;
577 let hosts_path = tag_directory.join("hosts.yaml");
578 std::fs::write(&hosts_path, hosts_yaml)?;
579
580 info!("configuring binary instances");
582 let mut start_futures = Vec::new();
583 for deployment in &deployments {
584 let tag_directory = tag_directory.clone();
585 let instance = deployment.instance.clone();
586 wait_for_instances_ready(&ec2_clients[&instance.region], &[deployment.id.clone()]).await?;
587 let ip = deployment.ip.clone();
588 let monitoring_private_ip = monitoring_private_ip.clone();
589 let hosts_path = hosts_path.clone();
590 let logrotate_conf_path = logrotate_conf_path.clone();
591 let bbr_conf_path = bbr_conf_path.clone();
592 let promtail_service_path = promtail_service_path.clone();
593 let node_exporter_service_path = node_exporter_service_path.clone();
594 let binary_service_path = binary_service_path.clone();
595 let pyroscope_agent_service_path = pyroscope_agent_service_path.clone();
596 let pyroscope_agent_timer_path = pyroscope_agent_timer_path.clone();
597 let memleak_agent_service_path = memleak_agent_service_path.clone();
598 let memleak_agent_script_path = memleak_agent_script_path.clone();
599 let future = async move {
600 rsync_file(private_key, &instance.binary, &ip, "/home/ubuntu/binary").await?;
601 rsync_file(
602 private_key,
603 &instance.config,
604 &ip,
605 "/home/ubuntu/config.conf",
606 )
607 .await?;
608 rsync_file(
609 private_key,
610 hosts_path.to_str().unwrap(),
611 &ip,
612 "/home/ubuntu/hosts.yaml",
613 )
614 .await?;
615 let promtail_config_path =
616 tag_directory.join(format!("promtail_{}.yml", instance.name));
617 std::fs::write(
618 &promtail_config_path,
619 promtail_config(
620 &monitoring_private_ip,
621 &instance.name,
622 ip.as_str(),
623 instance.region.as_str(),
624 ),
625 )?;
626 rsync_file(
627 private_key,
628 promtail_config_path.to_str().unwrap(),
629 &ip,
630 "/home/ubuntu/promtail.yml",
631 )
632 .await?;
633 rsync_file(
634 private_key,
635 promtail_service_path.to_str().unwrap(),
636 &ip,
637 "/home/ubuntu/promtail.service",
638 )
639 .await?;
640 rsync_file(
641 private_key,
642 node_exporter_service_path.to_str().unwrap(),
643 &ip,
644 "/home/ubuntu/node_exporter.service",
645 )
646 .await?;
647 rsync_file(
648 private_key,
649 binary_service_path.to_str().unwrap(),
650 &ip,
651 "/home/ubuntu/binary.service",
652 )
653 .await?;
654 rsync_file(
655 private_key,
656 logrotate_conf_path.to_str().unwrap(),
657 &ip,
658 "/home/ubuntu/logrotate.conf",
659 )
660 .await?;
661 rsync_file(
662 private_key,
663 pyroscope_agent_service_path.to_str().unwrap(),
664 &ip,
665 "/home/ubuntu/pyroscope-agent.service",
666 )
667 .await?;
668 let pyroscope_agent_script_path =
669 tag_directory.join(format!("pyroscope-agent_{}.sh", instance.name));
670 std::fs::write(
671 &pyroscope_agent_script_path,
672 generate_pyroscope_script(
673 &monitoring_private_ip,
674 &instance.name,
675 &ip,
676 &instance.region,
677 ),
678 )?;
679 rsync_file(
680 private_key,
681 pyroscope_agent_script_path.to_str().unwrap(),
682 &ip,
683 "/home/ubuntu/pyroscope-agent.sh",
684 )
685 .await?;
686 rsync_file(
687 private_key,
688 pyroscope_agent_timer_path.to_str().unwrap(),
689 &ip,
690 "/home/ubuntu/pyroscope-agent.timer",
691 )
692 .await?;
693 rsync_file(
694 private_key,
695 memleak_agent_service_path.to_str().unwrap(),
696 &ip,
697 "/home/ubuntu/memleak-agent.service",
698 )
699 .await?;
700 rsync_file(
701 private_key,
702 memleak_agent_script_path.to_str().unwrap(),
703 &ip,
704 "/home/ubuntu/memleak-agent.sh",
705 )
706 .await?;
707 enable_bbr(private_key, &ip, bbr_conf_path.to_str().unwrap()).await?;
708 ssh_execute(private_key, &ip, &setup_promtail_cmd(PROMTAIL_VERSION)).await?;
709 poll_service_active(private_key, &ip, "promtail").await?;
710 ssh_execute(
711 private_key,
712 &ip,
713 &setup_node_exporter_cmd(NODE_EXPORTER_VERSION),
714 )
715 .await?;
716 poll_service_active(private_key, &ip, "node_exporter").await?;
717 ssh_execute(private_key, &ip, &install_binary_cmd(instance.profiling)).await?;
718 poll_service_active(private_key, &ip, "binary").await?;
719 info!(
720 ip = ip.as_str(),
721 instance = instance.name.as_str(),
722 "configured instance"
723 );
724 Ok::<String, Error>(ip)
725 };
726 start_futures.push(future);
727 }
728 let all_binary_ips = try_join_all(start_futures).await?;
729 info!("configured binary instances");
730
731 info!("updating monitoring security group to allow traffic from binary instances");
733 let monitoring_ec2_client = &ec2_clients[&monitoring_region];
734 if binary_regions.contains(&monitoring_region) {
735 let binary_sg_id = region_resources[&monitoring_region]
736 .binary_sg_id
737 .clone()
738 .unwrap();
739 monitoring_ec2_client
740 .authorize_security_group_ingress()
741 .group_id(&monitoring_sg_id)
742 .ip_permissions(
743 IpPermission::builder()
744 .ip_protocol("tcp")
745 .from_port(LOGS_PORT as i32)
746 .to_port(LOGS_PORT as i32)
747 .user_id_group_pairs(
748 UserIdGroupPair::builder()
749 .group_id(binary_sg_id.clone())
750 .build(),
751 )
752 .build(),
753 )
754 .ip_permissions(
755 IpPermission::builder()
756 .ip_protocol("tcp")
757 .from_port(PROFILES_PORT as i32)
758 .to_port(PROFILES_PORT as i32)
759 .user_id_group_pairs(
760 UserIdGroupPair::builder()
761 .group_id(binary_sg_id.clone())
762 .build(),
763 )
764 .build(),
765 )
766 .ip_permissions(
767 IpPermission::builder()
768 .ip_protocol("tcp")
769 .from_port(TRACES_PORT as i32)
770 .to_port(TRACES_PORT as i32)
771 .user_id_group_pairs(
772 UserIdGroupPair::builder()
773 .group_id(binary_sg_id.clone())
774 .build(),
775 )
776 .build(),
777 )
778 .send()
779 .await
780 .map_err(|err| err.into_service_error())?;
781 info!(
782 monitoring = monitoring_sg_id.as_str(),
783 binary = binary_sg_id.as_str(),
784 region = monitoring_region.as_str(),
785 "linked monitoring and binary security groups in monitoring region"
786 );
787 }
788 for region in ®ions {
789 if region != &monitoring_region && binary_regions.contains(region) {
790 let binary_cidr = ®ion_resources[region].vpc_cidr;
791 monitoring_ec2_client
792 .authorize_security_group_ingress()
793 .group_id(&monitoring_sg_id)
794 .ip_permissions(
795 IpPermission::builder()
796 .ip_protocol("tcp")
797 .from_port(LOGS_PORT as i32)
798 .to_port(LOGS_PORT as i32)
799 .ip_ranges(IpRange::builder().cidr_ip(binary_cidr).build())
800 .build(),
801 )
802 .ip_permissions(
803 IpPermission::builder()
804 .ip_protocol("tcp")
805 .from_port(PROFILES_PORT as i32)
806 .to_port(PROFILES_PORT as i32)
807 .ip_ranges(IpRange::builder().cidr_ip(binary_cidr).build())
808 .build(),
809 )
810 .ip_permissions(
811 IpPermission::builder()
812 .ip_protocol("tcp")
813 .from_port(TRACES_PORT as i32)
814 .to_port(TRACES_PORT as i32)
815 .ip_ranges(IpRange::builder().cidr_ip(binary_cidr).build())
816 .build(),
817 )
818 .send()
819 .await
820 .map_err(|err| err.into_service_error())?;
821 info!(
822 monitoring = monitoring_sg_id.as_str(),
823 binary = binary_cidr.as_str(),
824 region = region.as_str(),
825 "opened monitoring part to traffic from binary VPC"
826 );
827 }
828 }
829 info!("updated monitoring security group");
830
831 File::create(tag_directory.join(CREATED_FILE_NAME))?;
833 info!(
834 monitoring = monitoring_ip.as_str(),
835 binary = ?all_binary_ips,
836 "deployment complete"
837 );
838 Ok(())
839}