Skip to content

Commit

Permalink
feat: MVP implementation of command line argument support
Browse files Browse the repository at this point in the history
  • Loading branch information
Mark S committed Jul 16, 2022
1 parent 2a9d4d1 commit 45c91dd
Show file tree
Hide file tree
Showing 5 changed files with 291 additions and 126 deletions.
1 change: 1 addition & 0 deletions Cargo.lock

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

3 changes: 2 additions & 1 deletion Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -49,5 +49,6 @@ k8s-openapi = { version = ">=0.15", features = ["v1_24"] }
clap = { version = ">=3.2", features = ["env", "derive"] }
serde = { version = "*", default-features = false, features = ["derive"] }
kube = { version = ">=0.73", default-features = false, features = ["client", "config", "rustls-tls"] }
tokio = { version = ">=1", default-features = false, features = ["net", "macros", "rt-multi-thread"] }
tokio = { version = ">=1", default-features = false, features = ["fs", "net", "macros", "rt-multi-thread"] }
futures-util = { version = "0.3.21", default-features = false, features = ["alloc", "async-await", "tokio-io"] }
log = "0.4.17"
204 changes: 191 additions & 13 deletions src/cli.rs
Original file line number Diff line number Diff line change
@@ -1,9 +1,25 @@
// Pinnothera's command line argument parsing components

use std::path::PathBuf;
use std::fmt::Formatter;
// Standard Library Imports
use std::path::{Path, PathBuf};
use std::sync::Arc;

use aws_types::SdkConfig as AWSConfig;
// Third Party Imports
use aws_sdk_sns::config::Config as SNSClientConfig;
use aws_sdk_sqs::config::Config as SQSClientConfig;
use aws_types::credentials::{
future::ProvideCredentials as ProvideAWSCredentials, Credentials as AWSCredentials,
CredentialsError as AWSCredentialsError, ProvideCredentials as AWSCredentialProvider,
SharedCredentialsProvider as SharedAWSCredentialsProvider,
};
use aws_types::{region::Region, SdkConfig as AWSConfig};
use clap::Parser;
use easy_error::{bail, Terminator};
use kube::Client;

// Project-Level Imports
use crate::{EnvName, PinnConfig, CLUSTER_ENV};

// const CLI_ABOUT: &str = "";

Expand All @@ -28,6 +44,11 @@ pub(crate) struct CLIArgs {
#[clap(short = 'c', long = "kube-context", value_parser)]
pub(crate) kube_context: Option<String>,

/// Name of the name of the "environment" the target
/// cluster is running in (i.e. 'dev' or 'production')
#[clap(short = 'e', long = "env-name", value_parser)]
pub(crate) env_name: Option<String>,

// </editor-fold desc="// Kubernetes-related Settings ...">

// <editor-fold desc="// AWS-related Settings ...">
Expand All @@ -49,12 +70,12 @@ pub(crate) struct CLIArgs {
/// The Secret Key ID that pinnothera should use
/// to communicate with AWS SNS/SQS services
#[clap(long = "aws-access-key-id", value_parser)]
pub(crate) aws_secret_key_id: Option<String>,
pub(crate) aws_access_key_id: Option<String>,

/// The Secret Access Key that pinnothera should use
/// to communicate with AWS SNS/SQS services
#[clap(long = "aws-secret-access-key", value_parser)]
pub(crate) aws_secret_access_id: Option<String>,
pub(crate) aws_secret_access_key: Option<String>,

// </editor-fold desc="// AWS-related Settings ...">

Expand Down Expand Up @@ -83,15 +104,172 @@ pub(crate) struct CLIArgs {
// </editor-fold desc="// Raw Config Data Settings ...">
}

struct CLICredentialProvider {
access_key_id: String,
secret_access_key: String,
}

impl std::fmt::Debug for CLICredentialProvider {
fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
write!(
f,
"CLICredentialProvider(access_key_id: {}, secret_access_key: {},",
self.access_key_id.as_str(),
self.secret_access_key.as_str()
)
}
}

impl CLICredentialProvider {
async fn aws_credentials(&self) -> aws_types::credentials::Result {
Ok(AWSCredentials::new(
self.access_key_id.as_str(),
self.secret_access_key.as_str(),
None,
None,
"Pinnothera CLI arguments",
))
}
}

impl AWSCredentialProvider for CLICredentialProvider {
fn provide_credentials<'a>(&'a self) -> ProvideAWSCredentials<'a>
where
Self: 'a,
{
ProvideAWSCredentials::new(self.aws_credentials())
}
}

impl TryFrom<&CLIArgs> for CLICredentialProvider {
type Error = AWSCredentialsError;

fn try_from(args: &CLIArgs) -> Result<Self, Self::Error> {
if !args.aws_access_key_id.is_some() {
Err(AWSCredentialsError::provider_error(
"Missing or empty access key id!",
))
} else if !args.aws_secret_access_key.is_some() {
Err(AWSCredentialsError::provider_error(
"Missing or empty secret access key!",
))
} else {
Ok(CLICredentialProvider {
access_key_id: args.aws_access_key_id.as_ref().unwrap().clone(),
secret_access_key: args.aws_secret_access_key.as_ref().unwrap().clone(),
})
}
}
}

impl CLIArgs {
pub async fn aws_config(&self) -> AWSConfig {
// let config: AWSConfig = aws_types::SdkConfig::builder()
// .region(aws_types::region::Region::new("us-east-2"))
// .credentials_provider(LocalstackCredentialProvider::new_shared_provider())
// .endpoint_resolver(aws_smithy_http::endpoint::Endpoint::immutable(
// http::Uri::from_static("http://aws.localstack"),
// ))
// .build();
todo!()
// <editor-fold desc="// AWS Configuration Utilities ...">
pub async fn aws_client_configs(
&'static self,
) -> Result<(SNSClientConfig, SQSClientConfig), Terminator> {
if self.aws_role_arn.is_some() {
bail!("Support for explicit AWS Role ARNs not yet implemented!")
}
// Infer and create an AWS `Config` from the current environment
let config: AWSConfig = aws_config::load_from_env().await;

let (sns_config, sqs_config) = (
aws_sdk_sns::config::Builder::from(&config),
aws_sdk_sqs::config::Builder::from(&config),
);

let (mut sns_config, mut sqs_config) = match &self.aws_region {
Some(region) => (
sns_config.region(Region::new(region)),
sqs_config.region(Region::new(region)),
),
None => (sns_config, sqs_config),
};

let endpoint = if let Some(url) = &self.aws_endpoint {
Some(url.as_str())
} else if CLUSTER_ENV
.get()
.unwrap_or(&Arc::new(EnvName::Unknown))
.is_local()
{
Some("http://aws.localstack")
} else {
None
};

if let Some(url) = endpoint {
sns_config.set_endpoint_resolver(Some(Arc::new(
aws_smithy_http::endpoint::Endpoint::immutable(http::Uri::from_static(url)),
)));
sqs_config.set_endpoint_resolver(Some(Arc::new(
aws_smithy_http::endpoint::Endpoint::immutable(http::Uri::from_static(url)),
)));
}

if self.aws_access_key_id.is_some() & self.aws_secret_access_key.is_some() {
sns_config.set_credentials_provider(Some(SharedAWSCredentialsProvider::new(
CLICredentialProvider::try_from(self)?,
)));
sqs_config.set_credentials_provider(Some(SharedAWSCredentialsProvider::new(
CLICredentialProvider::try_from(self)?,
)));
}

Ok((sns_config.build(), sqs_config.build()))
}

// </editor-fold desc="// AWS Configuration Utilities ...">

// <editor-fold desc="// Kubernetes Configuration Utilities ...">

async fn kube_config(&self) -> Result<kube::Config, Terminator> {
let options = kube::config::KubeConfigOptions {
context: self.kube_context.clone(),
cluster: None,
user: None,
};

let config =
kube::Config::from_custom_kubeconfig(kube::config::Kubeconfig::read()?, &options)
.await?;

Ok(config)
}

// </editor-fold desc="// Kubernetes Configuration Utilities ...">

// <editor-fold desc="// Pinnothera Configuration Utilities ...">

pub async fn pinn_config(&mut self) -> Result<(EnvName, PinnConfig), Terminator> {
if let Some(json_path) = &self.json_file {
self.json_data = Some(tokio::fs::read_to_string(json_path).await?);
} else if let Some(yaml_path) = &self.yaml_file {
self.yaml_data = Some(tokio::fs::read_to_string(yaml_path).await?);
}

if let Some(json_data) = &self.json_data {
return Ok((
EnvName::from(self.env_name.clone()),
PinnConfig::from_json(json_data)?,
));
} else if let Some(yaml_data) = &self.yaml_data {
return Ok((
EnvName::from(self.env_name.clone()),
PinnConfig::from_yaml(yaml_data)?,
));
}

let client = kube::Client::try_from(self.kube_config().await?)?;

PinnConfig::from_cluster(
client,
&self.env_name,
&self.namespace,
&self.configmap_name,
)
.await
}

// </editor-fold desc="// Pinnothera Configuration Utilities ...">
}
Loading

0 comments on commit 45c91dd

Please sign in to comment.