From 287c972a237ee9f2ff10516c59635675cea117b8 Mon Sep 17 00:00:00 2001 From: marc Date: Thu, 17 Dec 2020 11:47:46 +0100 Subject: [PATCH] feat: move helper fonctions into engine --- Cargo.lock | 15 ++++ Cargo.toml | 1 + lib/aws/bootstrap/ecr-qovery.tf | 4 -- lib/aws/bootstrap/elasticsearch.tf | 5 -- lib/aws/bootstrap/helper.j2.sh | 4 -- src/cloud_provider/aws/kubernetes/mod.rs | 13 +++- src/cloud_provider/aws/kubernetes/roles.rs | 81 ++++++++++++++++++++++ 7 files changed, 109 insertions(+), 14 deletions(-) delete mode 100644 lib/aws/bootstrap/ecr-qovery.tf create mode 100644 src/cloud_provider/aws/kubernetes/roles.rs diff --git a/Cargo.lock b/Cargo.lock index c63620f1..23b3f562 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -1876,6 +1876,7 @@ dependencies = [ "rusoto_dynamodb", "rusoto_ecr", "rusoto_eks", + "rusoto_iam", "rusoto_s3", "rusoto_sts", "rust-crypto", @@ -2354,6 +2355,20 @@ dependencies = [ "serde_json", ] +[[package]] +name = "rusoto_iam" +version = "0.45.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "73f792f9b9977eb4af7dae0de5ec6ced243a75a8ba25b903d8251cc8ac42b1a5" +dependencies = [ + "async-trait", + "bytes 0.5.6", + "futures 0.3.7", + "rusoto_core", + "serde_urlencoded 0.6.1", + "xml-rs", +] + [[package]] name = "rusoto_s3" version = "0.45.0" diff --git a/Cargo.toml b/Cargo.toml index 61011210..3311cc35 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -52,6 +52,7 @@ rusoto_ecr = "0.45.0" rusoto_eks = "0.45.0" rusoto_s3 = "0.45.0" rusoto_dynamodb = "0.45.0" +rusoto_iam = "0.45.0" # Digital Ocean Deps digitalocean = "0.1.1" diff --git a/lib/aws/bootstrap/ecr-qovery.tf b/lib/aws/bootstrap/ecr-qovery.tf deleted file mode 100644 index 0d7e6275..00000000 --- a/lib/aws/bootstrap/ecr-qovery.tf +++ /dev/null @@ -1,4 +0,0 @@ -# Qovery registry repository for application images store -data "external" "ecr_qovery_repo" { - program = ["./helper.sh", "create_ecr_repository", "qovery"] -} \ No newline at end of file diff --git a/lib/aws/bootstrap/elasticsearch.tf b/lib/aws/bootstrap/elasticsearch.tf index 37ff0d50..36c7a03f 100644 --- a/lib/aws/bootstrap/elasticsearch.tf +++ b/lib/aws/bootstrap/elasticsearch.tf @@ -1,8 +1,3 @@ -# Because it needs to be uniq across all clusters and Terraform doesn't brings solution to this, I'm using this hack -data "external" "create_elasticsearch_role" { - program = ["./helper.sh", "create_elasticsearch_role_for_aws_service", "AWSServiceRoleForAmazonElasticsearchService", "es.amazonaws.com"] -} - locals { tags_elasticsearch = merge( local.tags_eks, diff --git a/lib/aws/bootstrap/helper.j2.sh b/lib/aws/bootstrap/helper.j2.sh index 8882b689..5c3b3ca1 100755 --- a/lib/aws/bootstrap/helper.j2.sh +++ b/lib/aws/bootstrap/helper.j2.sh @@ -127,10 +127,6 @@ case $1 in check_args 2 create_elasticsearch_role_for_aws_service "$2" "$3" ;; - create_ecr_repository) - check_args 1 - create_ecr_repository "$2" - ;; is_cni_handled_by_aws) check_args 1 is_cni_handled_by_aws "$2" diff --git a/src/cloud_provider/aws/kubernetes/mod.rs b/src/cloud_provider/aws/kubernetes/mod.rs index 5b2c6487..bfcb36ff 100644 --- a/src/cloud_provider/aws/kubernetes/mod.rs +++ b/src/cloud_provider/aws/kubernetes/mod.rs @@ -17,11 +17,12 @@ use crate::deletion_utilities::{get_firsts_namespaces_to_delete, get_qovery_mana use crate::dns_provider; use crate::dns_provider::DnsProvider; use crate::error::EngineErrorCause::Internal; -use crate::error::{cast_simple_error_to_engine_error, EngineError, EngineErrorCause}; +use crate::error::{cast_simple_error_to_engine_error, EngineError, EngineErrorCause, SimpleError}; use crate::fs::workspace_directory; use crate::models::{ Context, Listen, Listener, Listeners, ListenersHelper, ProgressInfo, ProgressLevel, ProgressScope, }; +use crate::cloud_provider::aws::kubernetes::roles::get_default_roles_to_create; use crate::object_storage::s3::S3; use crate::object_storage::ObjectStorage; use crate::string::terraform_list_format; @@ -30,6 +31,7 @@ use retry::Error::Operation; use retry::OperationResult; pub mod node; +pub mod roles; #[derive(Default, Debug, Clone, PartialEq, Serialize, Deserialize)] pub struct Options { @@ -416,6 +418,15 @@ impl<'a> Kubernetes for EKS<'a> { self.context.execution_id(), )); + // create AWS IAM roles + let default_role_to_create = get_default_roles_to_create(); + for role in default_role_to_create { + match role.create_service_linked_role() { + Ok(_) => info!("Role {}, is successfully linked"), + Err(_) => error!("Role {}, isn't well linked"), + } + } + let temp_dir = workspace_directory( self.context.workspace_root_dir(), self.context.execution_id(), diff --git a/src/cloud_provider/aws/kubernetes/roles.rs b/src/cloud_provider/aws/kubernetes/roles.rs new file mode 100644 index 00000000..cb45035a --- /dev/null +++ b/src/cloud_provider/aws/kubernetes/roles.rs @@ -0,0 +1,81 @@ +use self::rusoto_iam::{ + CreateServiceLinkedRoleRequest, GetRoleError, GetRoleRequest, GetRoleResponse, Iam, IamClient, +}; +use crate::error::{EngineError, SimpleError, SimpleErrorKind}; +use crate::models::Context; +use rusoto_core::{Client, HttpClient, Region, RusotoError}; +use rusoto_credential::StaticProvider; +use tokio::macros::support::Future; +use tokio::runtime::Runtime; + +extern crate rusoto_iam; + +pub struct Role { + role_name: String, + service_name: String, + description: String, +} + +pub fn get_default_roles_to_create() -> Vec { + let mut defaults_role_to_create: Vec = Vec::new(); + defaults_role_to_create.push(Role { + role_name: "create_elasticsearch_role_for_aws_service".to_string(), + service_name: "AWSServiceRoleForAmazonElasticsearchService".to_string(), + description: "role permissions policy allows Amazon ES to complete create, delete, describe, modify on ec2 and elb".to_string(), + }); + defaults_role_to_create +} + +impl Role { + pub fn new(role_name: String, service_name: String, description: String) -> Self { + Role { + role_name, + service_name, + description, + } + } + + pub async fn is_exist(&self) -> bool { + let credentials = StaticProvider::new( + access_key_id.to_string(), + secret_access_key.to_string(), + None, + None, + ); + let client = Client::new_with(credentials, HttpClient::new().unwrap()); + let iam_client = IamClient::new_with_client(client, Region::default()); + let role = iam_client.get_role(GetRoleRequest { role_name }).await; + return match role { + Ok(_) => true, + Err(_) => false, + }; + } + + pub fn create_service_linked_role(&self) -> Result<(), SimpleError> { + let future_is_exist = self.is_exist(); + Runtime::new() + .expect("Failed to create Tokio runtime to check if role exist") + .block_on(future_is_exist); + return match future_is_exist { + true => { + info!("Role {} already exist, nothing to do", &self.role_name); + Ok(()) + } + false => { + info!("Role {} doesn't exist, let's create it !", &self.role_name); + let client = Client::new_with(credentials, HttpClient::new().unwrap()); + let iam_client = IamClient::new_with_client(client, Region::default()); + iam_client.create_service_linked_role(CreateServiceLinkedRoleRequest { + aws_service_name: self.service_name.clone(), + custom_suffix: None, + description: Some(self.description.clone()), + }); + Ok(()) + } + _ => Err(SimpleError::new( + SimpleErrorKind::Other, + Some(format!("Unable to check if role {} exist", &self.role_name)), + )), + }; + } +}