use prettytable::{format, Table};
use rocl::apis::client::APIClient;
use rocl::models::{ServiceBindingRequest, ServiceInstanceProvisionRequest};
use serde_json::json;
use spinners::{Spinner, Spinners};
use std::error::Error;
use std::{thread, time};
use uuid::Uuid;
use models::{ServiceBindingOutput, ServiceInstanceOutput};
const USER_AGENT: &str = "ROCS v0.1";
const DEFAULT_API_VERSION: &str = "2.15";
pub struct Options {
pub json_output: bool,
pub synchronous: bool,
}
pub fn catalog(
_: &clap::ArgMatches,
client: APIClient,
options: Options,
) -> Result<(), Box<dyn Error>> {
let catalog_api = client.catalog_api();
let catalog = catalog_api
.catalog_get(DEFAULT_API_VERSION)
.expect("catalog request failed");
match options.json_output {
false => {
let mut services_table = Table::new();
services_table.add_row(row!["Service", "Description", "Plans", "Extensions"]);
for s in catalog.services.unwrap().iter() {
let mut plans_table = Table::new();
let mut extensions_table = Table::new();
plans_table.set_format(*format::consts::FORMAT_NO_BORDER_LINE_SEPARATOR);
for p in s.plans.iter() {
plans_table.add_row(row![p.name]);
}
if let Some(extensions) = &s.extensions {
for p in extensions.iter() {
extensions_table.add_row(row![p.id]);
}
}
services_table.add_row(row![s.name, s.description, plans_table]);
}
services_table.printstd();
}
true => {
println!(
"{}",
serde_json::to_string(&catalog.services.unwrap()).unwrap()
);
}
};
Ok(())
}
pub fn deprovision(
matches: &clap::ArgMatches,
client: APIClient,
options: Options,
) -> Result<(), Box<dyn Error>> {
let instance_id = matches.value_of("instance-id").unwrap().to_string();
let si_api = client.service_instances_api();
si_api
.service_instance_deprovision(
DEFAULT_API_VERSION,
&*instance_id,
"",
"",
USER_AGENT,
!options.synchronous,
)
.expect("deprovisioning request failed");
Ok(())
}
pub fn provision(
matches: &clap::ArgMatches,
client: APIClient,
options: Options,
) -> Result<(), Box<dyn Error>> {
let service = matches.value_of("service").unwrap().to_string();
let plan = matches.value_of("plan").unwrap().to_string();
let si_api = client.service_instances_api();
let (service_id, plan_id) =
find_service_plan_id(&client, service, plan).expect("service or plan id not found");
let mut provision_request = ServiceInstanceProvisionRequest::new(
service_id.clone(),
plan_id.clone(),
String::from(""),
String::from(""),
);
provision_request.parameters = Some(json!({"region": "us-east-1"}));
let instance_id = Uuid::new_v4().to_hyphenated().to_string();
let _provision_response = si_api
.service_instance_provision(
DEFAULT_API_VERSION,
&*instance_id, provision_request,
USER_AGENT,
!options.synchronous, )
.expect("provision request failed");
if matches.is_present("wait") {
let sp = Spinner::new(Spinners::Point, "provisioning service instance".to_string());
loop {
let last_op = si_api.service_instance_last_operation_get(
DEFAULT_API_VERSION,
&*instance_id,
&*service_id, &*plan_id, "", );
if let Ok(lo) = last_op {
match lo.state {
rocl::models::State::InProgress => {
thread::sleep(time::Duration::new(2, 0));
}
_ => break,
}
}
}
println!("");
sp.stop();
}
let provisioned_instance = si_api
.service_instance_get(DEFAULT_API_VERSION, &*instance_id, USER_AGENT, "", "")
.expect("service instance fetch failed");
match options.json_output {
false => {
let mut table = Table::new();
table.add_row(row!["Instance ID", "Dashboard URL"]);
table.add_row(row![
&*instance_id,
provisioned_instance.dashboard_url.unwrap()
]);
table.printstd();
}
true => {
let si_out = ServiceInstanceOutput {
service_instance_id: Some(instance_id),
service_instance_resource: Some(provisioned_instance),
};
println!("{}", serde_json::to_string(&si_out).unwrap());
}
};
Ok(())
}
pub fn bind(
matches: &clap::ArgMatches,
client: APIClient,
options: Options,
) -> Result<(), Box<dyn Error>> {
let binding_api = client.service_bindings_api();
let binding_id = Uuid::new_v4().to_hyphenated().to_string();
let instance_id = matches.value_of("instance-id").unwrap().to_string();
let binding_request = ServiceBindingRequest::new("".into(), "".into());
let _binding_response = binding_api.service_binding_binding(
DEFAULT_API_VERSION,
&*instance_id,
&*binding_id,
binding_request,
USER_AGENT,
!options.synchronous,
);
if !options.synchronous {
loop {
let last_op = binding_api.service_binding_last_operation_get(
DEFAULT_API_VERSION,
&*instance_id,
&*binding_id,
"", "", "", );
if let Ok(lo) = last_op {
match lo.state {
rocl::models::State::InProgress => {
thread::sleep(time::Duration::new(2, 0));
}
_ => break,
}
}
}
println!("");
}
let provisioned_binding = binding_api
.service_binding_get(
DEFAULT_API_VERSION,
&*instance_id,
&*binding_id,
USER_AGENT,
"",
"",
)
.expect("service binding fetch failed");
match options.json_output {
false => {
let mut table = Table::new();
table.add_row(row!["Instance ID"]);
table.add_row(row![&*instance_id]);
table.add_row(row!["Binding ID"]);
table.add_row(row![&*binding_id]);
table.add_row(row!["Credentials"]);
table.add_row(row![serde_json::to_string_pretty(&provisioned_binding)?]);
table.printstd();
}
true => {
let sb_out = ServiceBindingOutput {
service_binding_id: Some(instance_id),
service_binding_resource: Some(provisioned_binding),
};
println!("{}", serde_json::to_string(&sb_out).unwrap());
}
};
Ok(())
}
pub fn unbind(
matches: &clap::ArgMatches,
client: APIClient,
options: Options,
) -> Result<(), Box<dyn Error>> {
let binding_api = client.service_bindings_api();
let instance_id = matches.value_of("instance-id").unwrap().to_string();
let binding_id = matches.value_of("binding-id").unwrap().to_string();
let _unbinding_response = binding_api
.service_binding_unbinding(
DEFAULT_API_VERSION,
&*instance_id,
&*binding_id,
"", "", USER_AGENT,
!options.synchronous,
)
.expect("service binding unbind failed");
Ok(())
}
pub fn creds(
matches: &clap::ArgMatches,
client: APIClient,
options: Options,
) -> Result<(), Box<dyn Error>> {
let binding_api = client.service_bindings_api();
let instance_id = matches.value_of("instance-id").unwrap().to_string();
let binding_id = matches.value_of("binding-id").unwrap().to_string();
let provisioned_binding = binding_api
.service_binding_get(
DEFAULT_API_VERSION,
&*instance_id,
&*binding_id,
USER_AGENT,
"",
"",
)
.expect("service binding fetch failed");
match options.json_output {
false => {
let mut table = Table::new();
table.add_row(row!["Instance ID"]);
table.add_row(row![&*instance_id]);
table.add_row(row!["Binding ID"]);
table.add_row(row![&*binding_id]);
table.add_row(row!["Credentials"]);
table.add_row(row![serde_json::to_string_pretty(&provisioned_binding)?]);
table.printstd();
}
true => println!("{}", serde_json::to_string(&provisioned_binding).unwrap()),
};
Ok(())
}
fn find_service_plan_id(
client: &APIClient,
service: String,
plan: String,
) -> Result<(String, String), &'static str> {
let si_api = client.catalog_api();
let catalog = si_api
.catalog_get(DEFAULT_API_VERSION)
.expect("failed to fetch catalog");
let mut service_id = String::from("");
let mut plan_id = String::from("");
'outer: for s in catalog.services.unwrap() {
if s.name == service {
service_id = s.id;
for p in s.plans {
if p.name == plan {
plan_id = p.id;
break 'outer;
}
}
}
}
if plan_id == "" || service_id == "" {
return Err("plan or service not found");
}
Ok((service_id, plan_id))
}