Skip to content

Commit

Permalink
testsys: Refactor code base
Browse files Browse the repository at this point in the history
Changes the way TestSys crd's are created while keeping the same
interface.
  • Loading branch information
ecpullen committed Nov 16, 2022
1 parent 538b9bb commit 20a197c
Show file tree
Hide file tree
Showing 18 changed files with 1,295 additions and 1,179 deletions.
50 changes: 43 additions & 7 deletions tools/Cargo.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

53 changes: 0 additions & 53 deletions tools/testsys-config/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -102,35 +102,6 @@ pub struct Test {
pub testsys_image_registry: Option<String>,
}

#[derive(Debug, Default)]
pub struct AwsK8sVariantConfig {
/// The names of all clusters this variant should be tested over. This is particularly useful
/// for testing Bottlerocket on ipv4 and ipv6 clusters.
pub cluster_names: Vec<String>,
/// The instance type that instances should be launched with
pub instance_type: Option<String>,
/// The secrets needed by the agents
pub secrets: BTreeMap<String, SecretName>,
/// The role that should be assumed for this particular variant
pub assume_role: Option<String>,
/// The kubernetes conformance image that should be used for this variant
pub kube_conformance_image: Option<String>,
/// The e2e repo containing sonobuoy images
pub e2e_repo_registry: Option<String>,
}

#[derive(Debug, Default)]
pub struct AwsEcsVariantConfig {
/// The names of all clusters this variant should be tested over
pub cluster_names: Vec<String>,
/// The instance type that instances should be launched with
pub instance_type: Option<String>,
/// The secrets needed by the agents
pub secrets: BTreeMap<String, SecretName>,
/// The role that should be assumed for this particular variant
pub assume_role: Option<String>,
}

/// Create a vec of relevant keys for this variant ordered from most specific to least specific.
fn config_keys(variant: &Variant) -> Vec<String> {
let (family_flavor, platform_flavor) = variant
Expand Down Expand Up @@ -228,30 +199,6 @@ impl GenericVariantConfig {
}
}

impl From<GenericVariantConfig> for AwsK8sVariantConfig {
fn from(val: GenericVariantConfig) -> Self {
Self {
cluster_names: val.cluster_names,
instance_type: val.instance_type,
secrets: val.secrets,
assume_role: val.agent_role,
kube_conformance_image: val.conformance_image,
e2e_repo_registry: val.conformance_registry,
}
}
}

impl From<GenericVariantConfig> for AwsEcsVariantConfig {
fn from(val: GenericVariantConfig) -> Self {
Self {
cluster_names: val.cluster_names,
instance_type: val.instance_type,
secrets: val.secrets,
assume_role: val.agent_role,
}
}
}

/// Fill in the templated cluster name with `arch` and `variant`.
pub fn rendered_cluster_name<S1, S2>(cluster_name: String, arch: S1, variant: S2) -> Result<String>
where
Expand Down
4 changes: 3 additions & 1 deletion tools/testsys/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@ edition = "2021"
publish = false

[dependencies]
anyhow = "1.0"
async-trait = "0.1"
aws-config = "0.48"
aws-sdk-ec2 = "0.18"
base64 = "0.13"
Expand All @@ -17,13 +17,15 @@ clap = { version = "3", features = ["derive", "env"] }
env_logger = "0.9"
futures = "0.3.8"
k8s-openapi = { version = "0.16", features = ["v1_20", "api"], default-features = false }
kube-client = { version = "0.75"}
log = "0.4"
maplit = "1.0.2"
model = { git = "https://github.com/bottlerocket-os/bottlerocket-test-system", version = "0.0.3", tag = "v0.0.3"}
pubsys-config = { path = "../pubsys-config/", version = "0.1.0" }
serde = { version = "1", features = ["derive"] }
serde_json = "1"
serde_plain = "1"
snafu = "0.7"
term_size = "0.3"
testsys-config = { path = "../testsys-config/", version = "0.1.0" }
tokio = { version = "1", features = ["macros", "rt-multi-thread", "fs"] }
Expand Down
175 changes: 175 additions & 0 deletions tools/testsys/src/aws_ecs.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,175 @@
use crate::aws_resources::{ami, ami_name, ec2_crd, get_ami_id, migration_crd};
use crate::crds::{
BottlerocketInput, ClusterInput, CrdCreator, CrdInput, CreateCrdOutput, MigrationInput,
TestInput,
};
use crate::error::{self, Result};
use bottlerocket_types::agent_config::{ClusterType, EcsClusterConfig, EcsTestConfig};
use log::debug;
use maplit::btreemap;
use model::{Crd, DestructionPolicy};
use snafu::OptionExt;

/// A `CrdCreator` responsible for creating crd related to `aws-ecs` variants.
pub(crate) struct AwsEcsCreator {
pub(crate) region: String,
pub(crate) ami_input: String,
pub(crate) migrate_starting_commit: Option<String>,
}

#[async_trait::async_trait]
impl CrdCreator for AwsEcsCreator {
/// Determine the AMI from `amis.json`.
fn image_id(&self, _: &CrdInput) -> Result<String> {
ami(&self.ami_input, &self.region)
}

/// Determine the starting image from EC2 using standard Bottlerocket naming conventions.
async fn starting_image_id(&self, crd_input: &CrdInput) -> Result<String> {
get_ami_id(ami_name(&crd_input.arch,&crd_input.variant,crd_input.starting_version
.as_ref()
.context(error::InvalidSnafu{
what: "The starting version must be provided for migration testing"
})?, self.migrate_starting_commit
.as_ref()
.context(error::InvalidSnafu{
what: "The commit for the starting version must be provided if the starting image id is not"
})?)
, &crd_input.arch,
& self.region,
)
.await
}

/// Create an ECS cluster CRD with the `cluster_name` in `cluster_input`.
async fn cluster_crd<'a>(&self, cluster_input: ClusterInput<'a>) -> Result<CreateCrdOutput> {
debug!("Creating ECS cluster CRD");
// Create labels that will be used for identifying existing CRDs for an ECS cluster.
let labels = cluster_input.crd_input.labels(btreemap! {
"testsys/type".to_string() => "cluster".to_string(),
"testsys/cluster".to_string() => cluster_input.cluster_name.to_string(),
"testsys/region".to_string() => self.region.clone()
});

// Check if the cluster already has a CRD in the TestSys cluster.
if let Some(cluster_crd) = cluster_input
.crd_input
.existing_crds(
&labels,
&["testsys/cluster", "testsys/type", "testsys/region"],
)
.await?
.pop()
{
// Return the name of the existing CRD for the cluster.
debug!("ECS cluster CRD already exists with name '{}'", cluster_crd);
return Ok(CreateCrdOutput::ExistingCrd(cluster_crd));
}

// Create the CRD for ECS cluster creation.
let ecs_crd = EcsClusterConfig::builder()
.cluster_name(cluster_input.cluster_name)
.region(Some(self.region.to_owned()))
.assume_role(cluster_input.crd_input.config.agent_role.clone())
.destruction_policy(DestructionPolicy::OnTestSuccess)
.image(
cluster_input
.crd_input
.images
.ecs_resource_agent_image
.as_ref()
.expect("The default ecs resource provider image uri is missing."),
)
.set_image_pull_secret(
cluster_input
.crd_input
.images
.testsys_agent_pull_secret
.to_owned(),
)
.set_secrets(Some(cluster_input.crd_input.config.secrets.clone()))
.build(cluster_input.cluster_name)
.map_err(|e| error::Error::Build {
what: "ECS cluster CRD".to_string(),
error: e.to_string(),
})?;

Ok(CreateCrdOutput::NewCrd(Box::new(Crd::Resource(ecs_crd))))
}

/// Create an EC2 provider CRD to launch Bottlerocket instances on the cluster created by
/// `cluster_crd`.
async fn bottlerocket_crd<'a>(
&self,
bottlerocket_input: BottlerocketInput<'a>,
) -> Result<CreateCrdOutput> {
Ok(CreateCrdOutput::NewCrd(Box::new(Crd::Resource(
ec2_crd(bottlerocket_input, ClusterType::Ecs, &self.region).await?,
))))
}

async fn migration_crd<'a>(
&self,
migration_input: MigrationInput<'a>,
) -> Result<CreateCrdOutput> {
Ok(CreateCrdOutput::NewCrd(Box::new(Crd::Test(migration_crd(
migration_input,
)?))))
}

async fn test_crd<'a>(&self, test_input: TestInput<'a>) -> Result<CreateCrdOutput> {
let cluster_resource_name = test_input
.cluster_crd_name
.as_ref()
.expect("A cluster name is required for migrations");
let bottlerocket_resource_name = test_input
.bottlerocket_crd_name
.as_ref()
.expect("A cluster name is required for migrations");

// Create labels that are used to help filter status.
let labels = test_input.crd_input.labels(btreemap! {
"testsys/type".to_string() => test_input.test_type.to_string(),
"testsys/cluster".to_string() => cluster_resource_name.to_string(),
});

let test_crd = EcsTestConfig::builder()
.cluster_name_template(cluster_resource_name, "clusterName")
.region(Some(self.region.to_owned()))
.task_count(1)
.assume_role(test_input.crd_input.config.agent_role.to_owned())
.resources(bottlerocket_resource_name)
.resources(cluster_resource_name)
.set_depends_on(Some(test_input.prev_tests))
.set_retries(Some(5))
.image(
test_input
.crd_input
.images
.ecs_test_agent_image
.to_owned()
.expect("The default ECS testing image is missing"),
)
.set_image_pull_secret(
test_input
.crd_input
.images
.testsys_agent_pull_secret
.to_owned(),
)
.keep_running(true)
.set_secrets(Some(test_input.crd_input.config.secrets.to_owned()))
.set_labels(Some(labels))
.build(format!(
"{}-test{}",
cluster_resource_name,
test_input.name_suffix.unwrap_or_default()
))
.map_err(|e| error::Error::Build {
what: "ECS test CRD".to_string(),
error: e.to_string(),
})?;

Ok(CreateCrdOutput::NewCrd(Box::new(Crd::Test(test_crd))))
}
}
Loading

0 comments on commit 20a197c

Please sign in to comment.