1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
// Copyright (c) Aptos
// SPDX-License-Identifier: Apache-2.0

use crate::{Factory, GenesisConfig, Result, Swarm, Version};
use anyhow::bail;
use aptos_logger::info;
use rand::rngs::StdRng;
use std::{convert::TryInto, num::NonZeroUsize};

mod cluster_helper;
mod node;
mod swarm;

pub use cluster_helper::*;
pub use node::K8sNode;
pub use swarm::*;

use aptos_sdk::crypto::ed25519::ED25519_PRIVATE_KEY_LENGTH;

pub struct K8sFactory {
    root_key: [u8; ED25519_PRIVATE_KEY_LENGTH],
    image_tag: String,
    base_image_tag: String,
    kube_namespace: String,
    use_port_forward: bool,
}

// These are test keys for forge ephemeral networks. Do not use these elsewhere!
pub const DEFAULT_ROOT_KEY: &str =
    "48136DF3174A3DE92AFDB375FFE116908B69FF6FAB9B1410E548A33FEA1D159D";
const DEFAULT_ROOT_PRIV_KEY: &str =
    "E25708D90C72A53B400B27FC7602C4D546C7B7469FA6E12544F0EBFB2F16AE19";

impl K8sFactory {
    pub fn new(
        kube_namespace: String,
        image_tag: String,
        base_image_tag: String,
        use_port_forward: bool,
    ) -> Result<K8sFactory> {
        let root_key: [u8; ED25519_PRIVATE_KEY_LENGTH] =
            hex::decode(DEFAULT_ROOT_PRIV_KEY)?.try_into().unwrap();

        match kube_namespace.as_str() {
            "default" => {
                info!("Using the default kubernetes namespace");
            }
            s if s.starts_with("forge") => {
                info!("Using forge namespace: {}", s);
            }
            _ => {
                bail!(
                    "Invalid kubernetes namespace provided: {}. Use forge-*",
                    kube_namespace
                );
            }
        }

        Ok(Self {
            root_key,
            image_tag,
            base_image_tag,
            kube_namespace,
            use_port_forward,
        })
    }
}

#[async_trait::async_trait]
impl Factory for K8sFactory {
    fn versions<'a>(&'a self) -> Box<dyn Iterator<Item = Version> + 'a> {
        let version = vec![
            Version::new(0, self.base_image_tag.clone()),
            Version::new(1, self.image_tag.clone()),
        ];
        Box::new(version.into_iter())
    }

    async fn launch_swarm(
        &self,
        _rng: &mut StdRng,
        node_num: NonZeroUsize,
        init_version: &Version,
        genesis_version: &Version,
        genesis_config: Option<&GenesisConfig>,
    ) -> Result<Box<dyn Swarm>> {
        let genesis_modules_path = match genesis_config {
            Some(config) => match config {
                GenesisConfig::Bytes(_) => {
                    bail!("k8s forge backend does not support raw bytes as genesis modules. please specify a path instead")
                }
                GenesisConfig::Path(path) => Some(path.clone()),
            },
            None => None,
        };

        let (_era, validators, fullnodes) = install_testnet_resources(
            self.kube_namespace.clone(),
            node_num.get(),
            format!("{}", init_version),
            format!("{}", genesis_version),
            genesis_modules_path,
            self.use_port_forward,
        )
        .await?;

        let swarm = K8sSwarm::new(
            &self.root_key,
            &self.image_tag,
            &self.base_image_tag,
            &self.kube_namespace,
            validators,
            fullnodes,
        )
        .await
        .unwrap();
        Ok(Box::new(swarm))
    }
}