k8s_test_framework/
framework.rs

1//! The test framework main entry point.
2
3use super::{
4    exec_tail, kubernetes_version, log_lookup, namespace, pod, port_forward, restart_rollout,
5    test_pod, up_down, vector, wait_for_resource, wait_for_rollout, Interface, PortForwarder,
6    Reader, Result,
7};
8
9/// Framework wraps the interface to the system with an easy-to-use rust API
10/// optimized for implementing test cases.
11#[derive(Debug)]
12pub struct Framework {
13    interface: Interface,
14}
15
16impl Framework {
17    /// Create a new [`Framework`] powered by the passed interface.
18    pub fn new(interface: Interface) -> Self {
19        Self { interface }
20    }
21
22    /// Deploy a Helm chart into a cluster.
23    pub async fn helm_chart(
24        &self,
25        namespace: &str,
26        helm_chart: &str,
27        release_name: &str,
28        helm_repo: &str,
29        config: vector::Config<'_>,
30    ) -> Result<up_down::Manager<vector::CommandBuilder>> {
31        let env = vec![("CHART_REPO".to_owned(), helm_repo.to_owned())];
32        let mut manager = vector::manager(
33            self.interface.deploy_chart_command.as_str(),
34            namespace,
35            helm_chart,
36            release_name,
37            config,
38            Some(env),
39        )?;
40        manager.up().await?;
41        Ok(manager)
42    }
43
44    /// Create a new namespace.
45    pub async fn namespace(
46        &self,
47        config: namespace::Config,
48    ) -> Result<up_down::Manager<namespace::CommandBuilder>> {
49        let mut manager = namespace::manager(&self.interface.kubectl_command, config);
50        manager.up().await?;
51        Ok(manager)
52    }
53
54    /// Create a new test `Pod`.
55    pub async fn test_pod(
56        &self,
57        config: test_pod::Config,
58    ) -> Result<up_down::Manager<test_pod::CommandBuilder>> {
59        let mut manager = test_pod::manager(&self.interface.kubectl_command, config);
60        manager.up().await?;
61        Ok(manager)
62    }
63
64    /// Initialize log lookup for a particular `resource` in a particular
65    /// `namespace`.
66    pub fn logs(&self, namespace: &str, resource: &str) -> Result<Reader> {
67        log_lookup(&self.interface.kubectl_command, namespace, resource)
68    }
69
70    /// Exec a `tail -f` command reading the specified `file` within
71    /// a `Container` in a `Pod` of a specified `resource` at the specified
72    /// `namespace`.
73    pub fn exec_tail(&self, namespace: &str, resource: &str, file: &str) -> Result<Reader> {
74        exec_tail(&self.interface.kubectl_command, namespace, resource, file)
75    }
76
77    /// Initialize port forward for a particular `resource` in a particular
78    /// `namespace` with a particular pair of local/resource ports.
79    pub fn port_forward(
80        &self,
81        namespace: &str,
82        resource: &str,
83        local_port: u16,
84        resource_port: u16,
85    ) -> Result<PortForwarder> {
86        port_forward(
87            &self.interface.kubectl_command,
88            namespace,
89            resource,
90            local_port,
91            resource_port,
92        )
93    }
94
95    /// Exect a `kubectl --version`command returning a K8sVersion Struct
96    /// containing all version information  of the running Kubernetes test cluster.
97    pub async fn kubernetes_version(&self) -> Result<kubernetes_version::K8sVersion> {
98        kubernetes_version::get(&self.interface.kubectl_command).await
99    }
100
101    /// Wait for a set of `resources` in a specified `namespace` to achieve
102    /// `wait_for` state.
103    /// Use `extra` to pass additional arguments to `kubectl`.
104    pub async fn wait<'a>(
105        &self,
106        namespace: &str,
107        resources: impl IntoIterator<Item = &'a str>,
108        wait_for: wait_for_resource::WaitFor<&'_ str>,
109        extra: impl IntoIterator<Item = &'a str>,
110    ) -> Result<()> {
111        wait_for_resource::namespace(
112            &self.interface.kubectl_command,
113            namespace,
114            resources,
115            wait_for,
116            extra,
117        )
118        .await
119    }
120
121    /// Wait for a set of `resources` in any namespace to achieve `wait_for`
122    /// state.
123    /// Use `extra` to pass additional arguments to `kubectl`.
124    pub async fn wait_all_namespaces<'a>(
125        &self,
126        resources: impl IntoIterator<Item = &'a str>,
127        wait_for: wait_for_resource::WaitFor<&'_ str>,
128        extra: impl IntoIterator<Item = &'a str>,
129    ) -> Result<()> {
130        wait_for_resource::all_namespaces(
131            &self.interface.kubectl_command,
132            resources,
133            wait_for,
134            extra,
135        )
136        .await
137    }
138
139    /// Wait for a rollout of a `resource` to complete.
140    /// Use `extra` to pass additional arguments to `kubectl`.
141    pub async fn wait_for_rollout<'a>(
142        &self,
143        namespace: &str,
144        resource: &str,
145        extra: impl IntoIterator<Item = &'a str>,
146    ) -> Result<()> {
147        wait_for_rollout::run(&self.interface.kubectl_command, namespace, resource, extra).await
148    }
149
150    /// Trigger a restart for a rollout of a `resource`.
151    /// Use `extr
152    pub async fn restart_rollout<'a>(
153        &self,
154        namespace: &str,
155        resources: &str,
156        extra: impl IntoIterator<Item = &'a str>,
157    ) -> Result<()> {
158        restart_rollout::run(&self.interface.kubectl_command, namespace, resources, extra).await
159    }
160
161    /// Gets the node for a given pod.
162    async fn get_node_for_pod(&self, namespace: &str, pod: &str) -> Result<String> {
163        pod::get_node(&self.interface.kubectl_command, namespace, pod).await
164    }
165
166    /// Gets the name of the pod implementing the service on the given node.
167    async fn get_pod_on_node(&self, namespace: &str, node: &str, service: &str) -> Result<String> {
168        pod::get_pod_on_node(&self.interface.kubectl_command, namespace, node, service).await
169    }
170
171    /// Sets a label on all nodes.
172    pub async fn label_nodes(&self, label: &str) -> Result<String> {
173        pod::label_nodes(&self.interface.kubectl_command, label).await
174    }
175
176    /// Return the Vector pod that is deployed on the same node as the given pod. We want to make
177    /// sure we are scanning the Vector instance that is deployed with the test pod.
178    pub async fn get_vector_pod_with_pod(
179        &self,
180        pod_namespace: &str,
181        pod_name: &str,
182        vector_pod_namespace: &str,
183        vector_pod_name: &str,
184    ) -> Result<String> {
185        let node = self
186            .get_node_for_pod(pod_namespace, pod_name)
187            .await
188            .map_err(|_| "need the node name")?;
189
190        Ok(self
191            .get_pod_on_node(vector_pod_namespace, &node, vector_pod_name)
192            .await
193            .map_err(|_| "cant get the vector pod running on the test node")?)
194    }
195}