sn_testnet_deploy/
setup.rs

1// Copyright (c) 2023, MaidSafe.
2// All rights reserved.
3//
4// This SAFE Network Software is licensed under the BSD-3-Clause license.
5// Please see the LICENSE file for more details.
6
7use 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}