sn_testnet_deploy/
setup.rs1use crate::error::{Error, Result};
8use inquire::{Select, Text};
9
10pub fn setup_dotenv_file() -> Result<()> {
11 let default_ansible_vault_password_path = dirs_next::home_dir()
12 .ok_or_else(|| Error::SetupError)?
13 .join(".ansible")
14 .join("vault-password")
15 .to_string_lossy()
16 .to_string();
17 let ansible_vault_password_path =
18 Text::new("Please supply the path of the vault password file for Ansible:")
19 .with_help_message(
20 "If you do not have the vault password, contact a team member who can supply a \
21 copy of it. Then write it out to this file, or another one of your choosing.",
22 )
23 .with_initial_value(&default_ansible_vault_password_path)
24 .with_validator(inquire::required!())
25 .prompt()?;
26
27 let aws_access_key_id = Text::new("Please supply your AWS access key ID:")
28 .with_help_message(
29 "Even if you do not deploy to AWS, this is required for Terraform state storage.",
30 )
31 .with_validator(inquire::required!())
32 .prompt()?;
33 let aws_access_secret_access_key =
34 Text::new("Please supply the corresponding AWS secret access key:")
35 .with_help_message(
36 "Even if you do not deploy to AWS, this is required for Terraform state storage.",
37 )
38 .with_validator(inquire::required!())
39 .prompt()?;
40 let aws_region = Text::new("Please supply the AWS region:")
41 .with_help_message("This is not for the state bucket, but for the region to deploy to.")
42 .with_initial_value("eu-west-2")
43 .with_validator(inquire::required!())
44 .prompt()?;
45 let digital_ocean_pat = Text::new("Please supply your PAT for Digital Ocean:")
46 .with_help_message(
47 "This is required for creating droplets that will host the nodes. \
48 If you do not have a PAT, contact someone in your team who can get you setup with one.",
49 )
50 .with_validator(inquire::required!())
51 .prompt()?;
52 let ssh_key_files = get_ssh_key_file_candidates()?;
53 let ssh_key_path = Select::new(
54 "Please select an SSH key from your ~/.ssh directory",
55 ssh_key_files,
56 )
57 .with_help_message("This key will be used for SSH access to droplets or EC2 instances.")
58 .prompt()?;
59 let slack_webhook_url =
60 Text::new("Please supply the Slack webhook URL for sending notifications:")
61 .with_help_message(
62 "If you do not have this, contact a team member who can supply it. \
63 This is an optional value.",
64 )
65 .with_initial_value("")
66 .with_validator(inquire::required!())
67 .prompt()?;
68 let sn_testnet_dev_subnet_id =
69 Text::new("Please supply the ID of the VPC subnet for launching EC2 instances:")
70 .with_help_message("If you are unsure of this value, just accept the default.")
71 .with_initial_value("subnet-018f2ab26755df7f9")
72 .with_validator(inquire::required!())
73 .prompt()?;
74 let sn_testnet_dev_security_group_id =
75 Text::new("Please supply the ID of the VPC security group for launching EC2 instances:")
76 .with_help_message("If you are unsure of this value, just accept the default.")
77 .with_initial_value("sg-0d47df5b3f0d01e2a")
78 .with_validator(inquire::required!())
79 .prompt()?;
80 let terraform_state_bucket_name =
81 Text::new("Please supply the name of the S3 bucket for Terraform state:")
82 .with_help_message("If you are unsure of this value, just accept the default.")
83 .with_initial_value("maidsafe-org-infra-tfstate")
84 .with_validator(inquire::required!())
85 .prompt()?;
86
87 let contents = format!(
88 r#"
89ANSIBLE_VAULT_PASSWORD_PATH={ansible_vault_password_path}
90AWS_ACCESS_KEY_ID={aws_access_key_id}
91AWS_SECRET_ACCESS_KEY={aws_access_secret_access_key}
92AWS_DEFAULT_REGION={aws_region}
93DO_PAT={digital_ocean_pat}
94SSH_KEY_PATH={ssh_key_path}
95SLACK_WEBHOOK_URL={slack_webhook_url}
96SN_TESTNET_DEV_SUBNET_ID={sn_testnet_dev_subnet_id}
97SN_TESTNET_DEV_SECURITY_GROUP_ID={sn_testnet_dev_security_group_id}
98TERRAFORM_STATE_BUCKET_NAME={terraform_state_bucket_name}
99"#
100 );
101
102 std::fs::write(".env", contents.trim())?;
103 Ok(())
104}
105
106fn get_ssh_key_file_candidates() -> Result<Vec<String>> {
107 let ssh_dir_path = dirs_next::home_dir()
108 .ok_or_else(|| Error::SetupError)?
109 .join(".ssh");
110 let entries = std::fs::read_dir(ssh_dir_path)?;
111 let ssh_files: Vec<String> = entries
112 .filter_map(Result::ok)
113 .map(|res| res.path())
114 .filter(|path| path.is_file() && path.extension().unwrap_or_default() != "pub")
115 .map(|path| path.to_string_lossy().into_owned())
116 .collect();
117 Ok(ssh_files)
118}