Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
107 changes: 106 additions & 1 deletion src/appliance.rs
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,10 @@ use anyhow::{anyhow, Context};
use clap::{Arg, ArgMatches, Command};
use tabled::{builder::Builder, settings::Style};

use crate::edge::{new_client, Appliance, ApplianceHealthState, AppliancePortType, EdgeClient};
use crate::edge::{
new_client, Appliance, ApplianceHealthState, AppliancePortType, EdgeClient, RegionReference,
UpdateAppliancePayload,
};
use crate::{green, red};

pub(crate) fn subcommand() -> clap::Command {
Expand Down Expand Up @@ -64,6 +67,21 @@ pub(crate) fn subcommand() -> clap::Command {
.help("The name of the appliance"),
),
)
.subcommand(
Command::new("update")
.about("Update appliance settings")
.arg(
Arg::new("name")
.required(true)
.help("The name of the appliance"),
)
.arg(Arg::new("region").long("region").help("The primary region"))
.arg(
Arg::new("secondary-region")
.long("secondary-region")
.help("The secondary region (use empty string \"\" to clear)"),
),
)
}

pub(crate) fn run(subcmd: &ArgMatches) {
Expand Down Expand Up @@ -136,6 +154,18 @@ pub(crate) fn run(subcmd: &ArgMatches) {
.expect("Appliance name is mandatory");
restart(client, name)
}
Some(("update", args)) => {
let client = new_client();
let name = args
.get_one::<String>("name")
.map(|s| s.as_str())
.expect("Appliance name is mandatory");
let region = args.get_one::<String>("region").map(|s| s.as_str());
let secondary_region = args
.get_one::<String>("secondary-region")
.map(|s| s.as_str());
update(client, name, region, secondary_region)
}
_ => unreachable!("subcommand_required prevents `None` or other options"),
}
}
Expand Down Expand Up @@ -244,6 +274,10 @@ fn show(client: EdgeClient, name: &str) {
println!("Product name; {}", appliance.kind); // TODO: Pretty-print
println!("Serial number: {}", appliance.serial);
println!("Group: {}", group_name);
println!("Region: {}", appliance.region.name);
if let Some(ref sec) = appliance.secondary_region {
println!("Secondary Region: {}", sec.name);
}
println!(
"Version (control): image={}, software={}",
appliance
Expand Down Expand Up @@ -419,3 +453,74 @@ fn get_appliance(client: &EdgeClient, name: &str) -> Appliance {
}
}
}

fn update(
client: EdgeClient,
name: &str,
region_arg: Option<&str>,
secondary_region_arg: Option<&str>,
) {
if region_arg.is_none() && secondary_region_arg.is_none() {
eprintln!("At least one of --region or --secondary-region must be specified");
process::exit(1);
}

let appliance = get_appliance(&client, name);

let region = match region_arg {
Some(region_name) => {
let r = get_region(&client, region_name);
RegionReference {
id: r.id,
name: r.name,
}
}
None => appliance.region.clone(),
};

let secondary_region = match secondary_region_arg {
None => appliance.secondary_region.clone(),
Some("") => None,
Some(sec_name) => {
let r = get_region(&client, sec_name);
Some(RegionReference {
id: r.id,
name: r.name,
})
}
};

let payload = UpdateAppliancePayload {
region,
secondary_region,
};

if let Err(e) = client.update_appliance(&appliance.id, payload) {
eprintln!("Failed to update appliance: {}", e);
process::exit(1);
}
}

fn get_region(client: &EdgeClient, name: &str) -> crate::edge::Region {
let regions = match client.find_region(name) {
Ok(r) => r,
Err(e) => {
eprintln!("Failed to look up region: {}", e);
process::exit(1);
}
};

if regions.is_empty() {
eprintln!("Region '{}' not found", name);
process::exit(1);
}

for region in regions {
if region.name == name {
return region;
}
}

eprintln!("Region '{}' not found", name);
process::exit(1);
}
31 changes: 30 additions & 1 deletion src/edge.rs
Original file line number Diff line number Diff line change
Expand Up @@ -694,7 +694,8 @@ pub struct Appliance {
pub last_registered_at: Option<String>, // iso8601/rfc3339
pub health: Option<ApplianceHealth>,
pub physical_ports: Vec<AppliancePhysicalPort>,
// region { id, name }
pub region: RegionReference,
pub secondary_region: Option<RegionReference>,
#[serde(rename = "type")]
pub kind: String,
// owner is the group id
Expand Down Expand Up @@ -1025,6 +1026,20 @@ pub struct NewRegion {
pub external: ExternalRegionMode,
}

#[derive(Debug, Deserialize, Serialize, Clone)]
#[serde(rename_all = "camelCase")]
pub struct RegionReference {
pub id: String,
pub name: String,
}

#[derive(Debug, Serialize)]
#[serde(rename_all = "camelCase")]
pub struct UpdateAppliancePayload {
pub region: RegionReference,
pub secondary_region: Option<RegionReference>,
}

#[derive(Debug, Clone)]
pub enum ExternalRegionMode {
Core = 0,
Expand Down Expand Up @@ -1991,6 +2006,20 @@ impl EdgeClient {
.map(|_| ())
}

pub fn update_appliance(
&self,
id: &str,
payload: UpdateAppliancePayload,
) -> Result<(), EdgeError> {
self.client
.post(format!("{}/api/appliance/{}", self.url, id))
.header("content-type", "application/json")
.json(&payload)
.send()?
.error_if_not_success()
.map(|_| ())
}

pub fn list_regions(&self) -> Result<Vec<Region>, EdgeError> {
#[derive(Debug, Deserialize)]
struct RegionListResp {
Expand Down