extern crate wasmcloud_control_interface;
use crate::{
ctl::manifest::HostManifest,
util::{convert_error, labels_vec_to_hashmap, Output, OutputKind, Result},
};
use spinners::{Spinner, Spinners};
use std::{path::Path, time::Duration};
use structopt::StructOpt;
use wasmcloud_control_interface::{
Client as CtlClient, CtlOperationAck, GetClaimsResponse, Host, HostInventory,
LinkDefinitionList,
};
mod manifest;
mod output;
pub(crate) use output::*;
#[derive(Debug, Clone, StructOpt)]
pub(crate) struct CtlCli {
#[structopt(flatten)]
command: CtlCliCommand,
}
impl CtlCli {
pub(crate) fn command(self) -> CtlCliCommand {
self.command
}
}
#[derive(Debug, Clone, StructOpt)]
pub(crate) struct ConnectionOpts {
#[structopt(
short = "r",
long = "ctl-host",
default_value = "0.0.0.0",
env = "WASH_CTL_HOST"
)]
ctl_host: String,
#[structopt(
short = "p",
long = "ctl-port",
default_value = "4222",
env = "WASH_CTL_PORT"
)]
ctl_port: String,
#[structopt(long = "ctl-jwt", env = "WASH_CTL_JWT", hide_env_values = true)]
ctl_jwt: Option<String>,
#[structopt(long = "ctl-seed", env = "WASH_CTL_SEED", hide_env_values = true)]
ctl_seed: Option<String>,
#[structopt(long = "ctl-credsfile", env = "WASH_CTL_CREDS", hide_env_values = true)]
ctl_credsfile: Option<String>,
#[structopt(
short = "n",
long = "ns-prefix",
default_value = "default",
env = "WASH_CTL_NSPREFIX"
)]
ns_prefix: String,
#[structopt(long = "timeout", default_value = "1")]
timeout: u64,
}
impl Default for ConnectionOpts {
fn default() -> Self {
ConnectionOpts {
ctl_host: "0.0.0.0".to_string(),
ctl_port: "4222".to_string(),
ctl_jwt: None,
ctl_seed: None,
ctl_credsfile: None,
ns_prefix: "default".to_string(),
timeout: 1,
}
}
}
#[derive(Debug, Clone, StructOpt)]
pub(crate) enum CtlCliCommand {
#[structopt(name = "get")]
Get(GetCommand),
#[structopt(name = "link")]
Link(LinkCommand),
#[structopt(name = "start")]
Start(StartCommand),
#[structopt(name = "stop")]
Stop(StopCommand),
#[structopt(name = "update")]
Update(UpdateCommand),
#[structopt(name = "apply")]
Apply(ApplyCommand),
}
#[derive(StructOpt, Debug, Clone)]
pub(crate) struct ApplyCommand {
#[structopt(name = "host-key")]
pub(crate) host_key: String,
#[structopt(name = "path")]
pub(crate) path: String,
#[structopt(name = "expand-env", short = "e", long = "expand-env")]
pub(crate) expand_env: bool,
#[structopt(flatten)]
opts: ConnectionOpts,
#[structopt(flatten)]
pub(crate) output: Output,
}
#[derive(Debug, Clone, StructOpt)]
pub(crate) enum GetCommand {
#[structopt(name = "hosts")]
Hosts(GetHostsCommand),
#[structopt(name = "inventory")]
HostInventory(GetHostInventoryCommand),
#[structopt(name = "claims")]
Claims(GetClaimsCommand),
}
#[derive(Debug, Clone, StructOpt)]
pub(crate) enum LinkCommand {
#[structopt(name = "query")]
Query(LinkQueryCommand),
#[structopt(name = "put")]
Put(LinkPutCommand),
#[structopt(name = "del")]
Del(LinkDelCommand),
}
#[derive(StructOpt, Debug, Clone)]
pub(crate) struct LinkQueryCommand {
#[structopt(flatten)]
opts: ConnectionOpts,
#[structopt(flatten)]
pub(crate) output: Output,
}
#[derive(StructOpt, Debug, Clone)]
pub(crate) struct LinkDelCommand {
#[structopt(flatten)]
opts: ConnectionOpts,
#[structopt(flatten)]
pub(crate) output: Output,
#[structopt(name = "actor-id")]
pub(crate) actor_id: String,
#[structopt(name = "contract-id")]
pub(crate) contract_id: String,
#[structopt(short = "l", long = "link-name")]
pub(crate) link_name: Option<String>,
}
#[derive(StructOpt, Debug, Clone)]
pub(crate) struct LinkPutCommand {
#[structopt(flatten)]
opts: ConnectionOpts,
#[structopt(flatten)]
pub(crate) output: Output,
#[structopt(name = "actor-id")]
pub(crate) actor_id: String,
#[structopt(name = "provider-id")]
pub(crate) provider_id: String,
#[structopt(name = "contract-id")]
pub(crate) contract_id: String,
#[structopt(short = "l", long = "link-name")]
pub(crate) link_name: Option<String>,
#[structopt(name = "values")]
pub(crate) values: Vec<String>,
}
#[derive(Debug, Clone, StructOpt)]
pub(crate) enum StartCommand {
#[structopt(name = "actor")]
Actor(StartActorCommand),
#[structopt(name = "provider")]
Provider(StartProviderCommand),
}
#[derive(Debug, Clone, StructOpt)]
pub(crate) enum StopCommand {
#[structopt(name = "actor")]
Actor(StopActorCommand),
#[structopt(name = "provider")]
Provider(StopProviderCommand),
}
#[derive(Debug, Clone, StructOpt)]
pub(crate) enum UpdateCommand {
#[structopt(name = "actor")]
Actor(UpdateActorCommand),
}
#[derive(Debug, Clone, StructOpt)]
pub(crate) struct GetHostsCommand {
#[structopt(flatten)]
opts: ConnectionOpts,
#[structopt(flatten)]
pub(crate) output: Output,
}
#[derive(Debug, Clone, StructOpt)]
pub(crate) struct GetHostInventoryCommand {
#[structopt(flatten)]
opts: ConnectionOpts,
#[structopt(flatten)]
pub(crate) output: Output,
#[structopt(name = "host-id")]
pub(crate) host_id: String,
}
#[derive(Debug, Clone, StructOpt)]
pub(crate) struct GetClaimsCommand {
#[structopt(flatten)]
opts: ConnectionOpts,
#[structopt(flatten)]
pub(crate) output: Output,
}
#[derive(Debug, Clone, StructOpt)]
pub(crate) struct StartActorCommand {
#[structopt(flatten)]
opts: ConnectionOpts,
#[structopt(flatten)]
pub(crate) output: Output,
#[structopt(short = "h", long = "host-id", name = "host-id")]
pub(crate) host_id: Option<String>,
#[structopt(name = "actor-ref")]
pub(crate) actor_ref: String,
#[structopt(short = "c", long = "constraint", name = "constraints")]
constraints: Option<Vec<String>>,
#[structopt(long = "auction-timeout", default_value = "1")]
auction_timeout: u64,
}
#[derive(Debug, Clone, StructOpt)]
pub(crate) struct StartProviderCommand {
#[structopt(flatten)]
opts: ConnectionOpts,
#[structopt(flatten)]
pub(crate) output: Output,
#[structopt(short = "h", long = "host-id", name = "host-id")]
host_id: Option<String>,
#[structopt(name = "provider-ref")]
pub(crate) provider_ref: String,
#[structopt(short = "l", long = "link-name", default_value = "default")]
pub(crate) link_name: String,
#[structopt(short = "c", long = "constraint", name = "constraints")]
constraints: Option<Vec<String>>,
#[structopt(long = "auction-timeout", default_value = "1")]
auction_timeout: u64,
}
#[derive(Debug, Clone, StructOpt)]
pub(crate) struct StopActorCommand {
#[structopt(flatten)]
opts: ConnectionOpts,
#[structopt(flatten)]
pub(crate) output: Output,
#[structopt(name = "host-id")]
pub(crate) host_id: String,
#[structopt(name = "actor-id")]
pub(crate) actor_id: String,
#[structopt(long = "count", default_value = "1")]
pub(crate) count: u16,
}
#[derive(Debug, Clone, StructOpt)]
pub(crate) struct StopProviderCommand {
#[structopt(flatten)]
opts: ConnectionOpts,
#[structopt(flatten)]
pub(crate) output: Output,
#[structopt(name = "host-id")]
host_id: String,
#[structopt(name = "provider-id")]
pub(crate) provider_id: String,
#[structopt(name = "link-name")]
pub(crate) link_name: String,
#[structopt(name = "contract-id")]
pub(crate) contract_id: String,
}
#[derive(Debug, Clone, StructOpt)]
pub(crate) struct UpdateActorCommand {
#[structopt(flatten)]
opts: ConnectionOpts,
#[structopt(flatten)]
pub(crate) output: Output,
#[structopt(name = "host-id")]
pub(crate) host_id: String,
#[structopt(name = "actor-id")]
pub(crate) actor_id: String,
#[structopt(name = "new-actor-ref")]
pub(crate) new_actor_ref: String,
}
pub(crate) async fn handle_command(command: CtlCliCommand) -> Result<String> {
use CtlCliCommand::*;
let mut sp: Option<Spinner> = None;
let out = match command {
Apply(cmd) => {
let output = cmd.output;
sp = update_spinner_message(sp, " Applying manifest ...".to_string(), &output);
let results = apply_manifest(cmd).await?;
apply_manifest_output(results, &output.kind)
}
Get(GetCommand::Hosts(cmd)) => {
let output = cmd.output;
sp = update_spinner_message(sp, " Retrieving Hosts ...".to_string(), &output);
let hosts = get_hosts(cmd).await?;
get_hosts_output(hosts, &output.kind)
}
Get(GetCommand::HostInventory(cmd)) => {
let output = cmd.output;
sp = update_spinner_message(
sp,
format!(" Retrieving inventory for host {} ...", cmd.host_id),
&output,
);
let inv = get_host_inventory(cmd).await?;
get_host_inventory_output(inv, &output.kind)
}
Get(GetCommand::Claims(cmd)) => {
let output = cmd.output;
sp = update_spinner_message(sp, " Retrieving claims ... ".to_string(), &output);
let claims = get_claims(cmd).await?;
get_claims_output(claims, &output.kind)
}
Link(LinkCommand::Del(cmd)) => {
let link_name = &cmd
.link_name
.clone()
.unwrap_or_else(|| "default".to_string());
sp = update_spinner_message(
sp,
format!(
"Deleting link for {} on {} ({}) ... ",
cmd.actor_id, cmd.contract_id, link_name,
),
&cmd.output,
);
let failure = link_del(cmd.clone())
.await
.map_or_else(|e| Some(format!("{}", e)), |_| None);
link_del_output(
&cmd.actor_id,
&cmd.contract_id,
link_name,
failure,
&cmd.output.kind,
)
}
Link(LinkCommand::Put(cmd)) => {
sp = update_spinner_message(
sp,
format!(
"Defining link between {} and {} ... ",
cmd.actor_id, cmd.provider_id
),
&cmd.output,
);
let failure = link_put(cmd.clone())
.await
.map_or_else(|e| Some(format!("{}", e)), |_| None);
link_put_output(&cmd.actor_id, &cmd.provider_id, failure, &cmd.output.kind)
}
Link(LinkCommand::Query(cmd)) => {
sp = update_spinner_message(sp, "Querying Links ... ".to_string(), &cmd.output);
let result = link_query(cmd.clone()).await?;
link_query_output(result, &cmd.output.kind)
}
Start(StartCommand::Actor(cmd)) => {
let output = cmd.output;
let actor_ref = &cmd.actor_ref.to_string();
sp = update_spinner_message(sp, format!(" Starting actor {} ... ", actor_ref), &output);
let ack = start_actor(cmd).await?;
ctl_operation_output(
ack.accepted,
&format!("Actor {} started successfully", actor_ref),
&ack.error,
&output.kind,
)
}
Start(StartCommand::Provider(cmd)) => {
let output = cmd.output;
let provider_ref = &cmd.provider_ref.to_string();
sp = update_spinner_message(
sp,
format!(" Starting provider {} ... ", provider_ref),
&output,
);
let ack = start_provider(cmd).await?;
ctl_operation_output(
ack.accepted,
&format!("Provider {} started successfully", provider_ref),
&ack.error,
&output.kind,
)
}
Stop(StopCommand::Actor(cmd)) => {
let output = cmd.output;
sp = update_spinner_message(
sp,
format!(" Stopping actor {} ... ", cmd.actor_id),
&output,
);
let ack = stop_actor(cmd.clone()).await?;
ctl_operation_output(
ack.accepted,
&format!("Actor {} stopped successfully", cmd.actor_id),
&ack.error,
&output.kind,
)
}
Stop(StopCommand::Provider(cmd)) => {
let output = cmd.output;
sp = update_spinner_message(
sp,
format!(" Stopping provider {} ... ", cmd.provider_id),
&output,
);
let ack = stop_provider(cmd.clone()).await?;
ctl_operation_output(
ack.accepted,
&format!("Provider {} stopped successfully", cmd.provider_id),
&ack.error,
&output.kind,
)
}
Update(UpdateCommand::Actor(cmd)) => {
let output = cmd.output;
sp = update_spinner_message(
sp,
format!(
" Updating Actor {} to {} ... ",
cmd.actor_id, cmd.new_actor_ref
),
&output,
);
let ack = update_actor(cmd.clone()).await?;
ctl_operation_output(
ack.accepted,
&format!("Actor {} updated to {}", cmd.actor_id, cmd.new_actor_ref),
&ack.error,
&output.kind,
)
}
};
if sp.is_some() {
sp.unwrap().stop()
}
Ok(out)
}
pub(crate) async fn get_hosts(cmd: GetHostsCommand) -> Result<Vec<Host>> {
let timeout = Duration::from_secs(cmd.opts.timeout);
let client = ctl_client_from_opts(cmd.opts).await?;
client.get_hosts(timeout).await.map_err(convert_error)
}
pub(crate) async fn get_host_inventory(cmd: GetHostInventoryCommand) -> Result<HostInventory> {
let client = ctl_client_from_opts(cmd.opts).await?;
client
.get_host_inventory(&cmd.host_id)
.await
.map_err(convert_error)
}
pub(crate) async fn get_claims(cmd: GetClaimsCommand) -> Result<GetClaimsResponse> {
let client = ctl_client_from_opts(cmd.opts).await?;
client.get_claims().await.map_err(convert_error)
}
pub(crate) async fn link_del(cmd: LinkDelCommand) -> Result<CtlOperationAck> {
let client = ctl_client_from_opts(cmd.opts).await?;
client
.remove_link(
&cmd.actor_id,
&cmd.contract_id,
&cmd.link_name.unwrap_or_else(|| "default".to_string()),
)
.await
.map_err(convert_error)
}
pub(crate) async fn link_put(cmd: LinkPutCommand) -> Result<CtlOperationAck> {
let client = ctl_client_from_opts(cmd.opts).await?;
client
.advertise_link(
&cmd.actor_id,
&cmd.provider_id,
&cmd.contract_id,
&cmd.link_name.unwrap_or_else(|| "default".to_string()),
labels_vec_to_hashmap(cmd.values)?,
)
.await
.map_err(convert_error)
}
pub(crate) async fn link_query(cmd: LinkQueryCommand) -> Result<LinkDefinitionList> {
let client = ctl_client_from_opts(cmd.opts).await?;
client.query_links().await.map_err(convert_error)
}
pub(crate) async fn start_actor(cmd: StartActorCommand) -> Result<CtlOperationAck> {
let opts = if cmd.opts.timeout == 1 {
ConnectionOpts {
timeout: 15,
..cmd.opts
}
} else {
cmd.opts
};
let client = ctl_client_from_opts(opts).await?;
let host = match cmd.host_id {
Some(host) => host,
None => {
let suitable_hosts = client
.perform_actor_auction(
&cmd.actor_ref,
labels_vec_to_hashmap(cmd.constraints.unwrap_or_default())?,
Duration::from_secs(cmd.auction_timeout),
)
.await
.map_err(convert_error)?;
if suitable_hosts.is_empty() {
return Err(format!("No suitable hosts found for actor {}", cmd.actor_ref).into());
} else {
suitable_hosts[0].host_id.to_string()
}
}
};
client
.start_actor(&host, &cmd.actor_ref)
.await
.map_err(convert_error)
}
pub(crate) async fn start_provider(cmd: StartProviderCommand) -> Result<CtlOperationAck> {
let opts = if cmd.opts.timeout == 1 {
ConnectionOpts {
timeout: 60,
..cmd.opts
}
} else {
cmd.opts
};
let client = ctl_client_from_opts(opts).await?;
let host = match cmd.host_id {
Some(host) => host,
None => {
let suitable_hosts = client
.perform_provider_auction(
&cmd.provider_ref,
&cmd.link_name,
labels_vec_to_hashmap(cmd.constraints.unwrap_or_default())?,
Duration::from_secs(cmd.auction_timeout),
)
.await
.map_err(convert_error)?;
if suitable_hosts.is_empty() {
return Err(
format!("No suitable hosts found for provider {}", cmd.provider_ref).into(),
);
} else {
suitable_hosts[0].host_id.to_string()
}
}
};
client
.start_provider(&host, &cmd.provider_ref, Some(cmd.link_name))
.await
.map_err(convert_error)
}
pub(crate) async fn stop_provider(cmd: StopProviderCommand) -> Result<CtlOperationAck> {
let client = ctl_client_from_opts(cmd.opts).await?;
client
.stop_provider(
&cmd.host_id,
&cmd.provider_id,
&cmd.link_name,
&cmd.contract_id,
)
.await
.map_err(convert_error)
}
pub(crate) async fn stop_actor(cmd: StopActorCommand) -> Result<CtlOperationAck> {
let client = ctl_client_from_opts(cmd.opts).await?;
client
.stop_actor(&cmd.host_id, &cmd.actor_id, cmd.count)
.await
.map_err(convert_error)
}
pub(crate) async fn update_actor(cmd: UpdateActorCommand) -> Result<CtlOperationAck> {
let client = ctl_client_from_opts(cmd.opts).await?;
client
.update_actor(&cmd.host_id, &cmd.actor_id, &cmd.new_actor_ref)
.await
.map_err(convert_error)
}
pub(crate) async fn apply_manifest(cmd: ApplyCommand) -> Result<Vec<String>> {
let client = ctl_client_from_opts(cmd.opts).await?;
let hm = match HostManifest::from_path(Path::new(&cmd.path), cmd.expand_env) {
Ok(hm) => hm,
Err(e) => return Err(format!("Failed to load manifest: {}", e).into()),
};
let mut results = vec![];
results.extend_from_slice(&apply_manifest_actors(&cmd.host_key, &client, &hm).await?);
results.extend_from_slice(&apply_manifest_providers(&cmd.host_key, &client, &hm).await?);
results.extend_from_slice(&apply_manifest_linkdefs(&client, &hm).await?);
Ok(results)
}
async fn apply_manifest_actors(
host_id: &str,
client: &CtlClient,
hm: &HostManifest,
) -> Result<Vec<String>> {
let mut results = vec![];
for actor in hm.actors.iter() {
match client.start_actor(host_id, actor).await {
Ok(ack) => {
if ack.accepted {
results.push(format!(
"Instruction to start actor {} acknowledged.",
actor
));
} else {
results.push(format!(
"Instruction to start actor {} not acked: {}",
actor, ack.error
));
}
}
Err(e) => results.push(format!("Failed to send start actor: {}", e)),
}
}
Ok(results)
}
async fn apply_manifest_linkdefs(client: &CtlClient, hm: &HostManifest) -> Result<Vec<String>> {
let mut results = vec![];
for ld in hm.links.iter() {
match client
.advertise_link(
&ld.actor,
&ld.provider_id,
&ld.contract_id,
ld.link_name.as_ref().unwrap_or(&"default".to_string()),
ld.values.clone().unwrap_or_default(),
)
.await
{
Ok(ack) => {
if ack.accepted {
results.push(format!(
"Link def submission from {} to {} acknowledged.",
ld.actor, ld.provider_id
));
} else {
results.push(format!(
"Link def submission from {} to {} not acked: {}",
ld.actor, ld.provider_id, ack.error
));
}
}
Err(e) => results.push(format!("Failed to send link def: {}", e)),
}
}
Ok(results)
}
async fn apply_manifest_providers(
host_id: &str,
client: &CtlClient,
hm: &HostManifest,
) -> Result<Vec<String>> {
let mut results = vec![];
for cap in hm.capabilities.iter() {
match client
.start_provider(host_id, &cap.image_ref, cap.link_name.clone())
.await
{
Ok(ack) => {
if ack.accepted {
results.push(format!(
"Instruction to start provider {} acknowledged.",
cap.image_ref
));
} else {
results.push(format!(
"Instruction to start provider {} not acked: {}",
cap.image_ref, ack.error
));
}
}
Err(e) => results.push(format!("Failed to send start capability message: {}", e)),
}
}
Ok(results)
}
async fn ctl_client_from_opts(opts: ConnectionOpts) -> Result<CtlClient> {
let timeout = Duration::from_secs(opts.timeout);
let nc = crate::util::nats_client_from_opts(
&opts.ctl_host,
&opts.ctl_port,
opts.ctl_jwt,
opts.ctl_seed,
opts.ctl_credsfile,
)
.await?;
let ctl_client = CtlClient::new(nc, Some(opts.ns_prefix.clone()), timeout);
Ok(ctl_client)
}
fn update_spinner_message(
spinner: Option<Spinner>,
msg: String,
output: &Output,
) -> Option<Spinner> {
if let Some(sp) = spinner {
sp.message(msg);
Some(sp)
} else if matches!(output.kind, OutputKind::Text) {
Some(Spinner::new(&Spinners::Dots12, msg))
} else {
None
}
}
#[cfg(test)]
mod test {
use super::*;
const CTL_HOST: &str = "0.0.0.0";
const CTL_PORT: &str = "4222";
const NS_PREFIX: &str = "default";
const ACTOR_ID: &str = "MDPDJEYIAK6MACO67PRFGOSSLODBISK4SCEYDY3HEOY4P5CVJN6UCWUK";
const PROVIDER_ID: &str = "VBKTSBG2WKP6RJWLQ5O7RDVIIB4LMW6U5R67A7QMIDBZDGZWYTUE3TSI";
const HOST_ID: &str = "NCE7YHGI42RWEKBRDJZWXBEJJCFNE5YIWYMSTLGHQBEGFY55BKJ3EG3G";
#[test]
fn test_ctl_comprehensive() -> Result<()> {
let start_actor_all = CtlCli::from_iter_safe(&[
"ctl",
"start",
"actor",
"-o",
"json",
"--ns-prefix",
NS_PREFIX,
"--ctl-host",
CTL_HOST,
"--ctl-port",
CTL_PORT,
"--timeout",
"1",
"--auction-timeout",
"1",
"--constraint",
"arch=x86_64",
"--host-id",
HOST_ID,
"wasmcloud.azurecr.io/actor:v1",
])?;
match start_actor_all.command {
CtlCliCommand::Start(StartCommand::Actor(super::StartActorCommand {
opts,
output,
host_id,
actor_ref,
constraints,
auction_timeout,
})) => {
assert_eq!(opts.ctl_host, CTL_HOST);
assert_eq!(opts.ctl_port, CTL_PORT);
assert_eq!(opts.ns_prefix, NS_PREFIX);
assert_eq!(opts.timeout, 1);
assert_eq!(auction_timeout, 1);
assert_eq!(output.kind, OutputKind::Json);
assert_eq!(host_id.unwrap(), HOST_ID.to_string());
assert_eq!(actor_ref, "wasmcloud.azurecr.io/actor:v1".to_string());
assert_eq!(constraints.unwrap(), vec!["arch=x86_64".to_string()]);
}
cmd => panic!("ctl start actor constructed incorrect command {:?}", cmd),
}
let start_provider_all = CtlCli::from_iter_safe(&[
"ctl",
"start",
"provider",
"-o",
"json",
"--ns-prefix",
NS_PREFIX,
"--ctl-host",
CTL_HOST,
"--ctl-port",
CTL_PORT,
"--timeout",
"1",
"--auction-timeout",
"1",
"--constraint",
"arch=x86_64",
"--host-id",
HOST_ID,
"--link-name",
"default",
"wasmcloud.azurecr.io/provider:v1",
])?;
match start_provider_all.command {
CtlCliCommand::Start(StartCommand::Provider(super::StartProviderCommand {
opts,
output,
host_id,
provider_ref,
link_name,
constraints,
auction_timeout,
})) => {
assert_eq!(opts.ctl_host, CTL_HOST);
assert_eq!(opts.ctl_port, CTL_PORT);
assert_eq!(opts.ns_prefix, NS_PREFIX);
assert_eq!(opts.timeout, 1);
assert_eq!(auction_timeout, 1);
assert_eq!(output.kind, OutputKind::Json);
assert_eq!(link_name, "default".to_string());
assert_eq!(constraints.unwrap(), vec!["arch=x86_64".to_string()]);
assert_eq!(host_id.unwrap(), HOST_ID.to_string());
assert_eq!(provider_ref, "wasmcloud.azurecr.io/provider:v1".to_string());
}
cmd => panic!("ctl start provider constructed incorrect command {:?}", cmd),
}
let stop_actor_all = CtlCli::from_iter_safe(&[
"ctl",
"stop",
"actor",
"-o",
"json",
"--ns-prefix",
NS_PREFIX,
"--ctl-host",
CTL_HOST,
"--ctl-port",
CTL_PORT,
"--timeout",
"1",
"--count",
"2",
HOST_ID,
ACTOR_ID,
])?;
match stop_actor_all.command {
CtlCliCommand::Stop(StopCommand::Actor(super::StopActorCommand {
opts,
output,
host_id,
actor_id,
count,
})) => {
assert_eq!(opts.ctl_host, CTL_HOST);
assert_eq!(opts.ctl_port, CTL_PORT);
assert_eq!(opts.ns_prefix, NS_PREFIX);
assert_eq!(opts.timeout, 1);
assert_eq!(output.kind, OutputKind::Json);
assert_eq!(host_id, HOST_ID.to_string());
assert_eq!(actor_id, ACTOR_ID.to_string());
assert_eq!(count, 2);
}
cmd => panic!("ctl stop actor constructed incorrect command {:?}", cmd),
}
let stop_provider_all = CtlCli::from_iter_safe(&[
"ctl",
"stop",
"provider",
"-o",
"json",
"--ns-prefix",
NS_PREFIX,
"--ctl-host",
CTL_HOST,
"--ctl-port",
CTL_PORT,
"--timeout",
"1",
HOST_ID,
PROVIDER_ID,
"default",
"wasmcloud:provider",
])?;
match stop_provider_all.command {
CtlCliCommand::Stop(StopCommand::Provider(super::StopProviderCommand {
opts,
output,
host_id,
provider_id,
link_name,
contract_id,
})) => {
assert_eq!(opts.ctl_host, CTL_HOST);
assert_eq!(opts.ctl_port, CTL_PORT);
assert_eq!(opts.ns_prefix, NS_PREFIX);
assert_eq!(opts.timeout, 1);
assert_eq!(output.kind, OutputKind::Json);
assert_eq!(host_id, HOST_ID.to_string());
assert_eq!(provider_id, PROVIDER_ID.to_string());
assert_eq!(link_name, "default".to_string());
assert_eq!(contract_id, "wasmcloud:provider".to_string());
}
cmd => panic!("ctl stop actor constructed incorrect command {:?}", cmd),
}
let get_hosts_all = CtlCli::from_iter_safe(&[
"ctl",
"get",
"hosts",
"-o",
"json",
"--ns-prefix",
NS_PREFIX,
"--ctl-host",
CTL_HOST,
"--ctl-port",
CTL_PORT,
"--timeout",
"1",
])?;
match get_hosts_all.command {
CtlCliCommand::Get(GetCommand::Hosts(GetHostsCommand { opts, output })) => {
assert_eq!(opts.ctl_host, CTL_HOST);
assert_eq!(opts.ctl_port, CTL_PORT);
assert_eq!(opts.ns_prefix, NS_PREFIX);
assert_eq!(opts.timeout, 1);
assert_eq!(output.kind, OutputKind::Json);
}
cmd => panic!("ctl get hosts constructed incorrect command {:?}", cmd),
}
let get_host_inventory_all = CtlCli::from_iter_safe(&[
"ctl",
"get",
"inventory",
"-o",
"json",
"--ns-prefix",
NS_PREFIX,
"--ctl-host",
CTL_HOST,
"--ctl-port",
CTL_PORT,
"--timeout",
"1",
HOST_ID,
])?;
match get_host_inventory_all.command {
CtlCliCommand::Get(GetCommand::HostInventory(GetHostInventoryCommand {
opts,
output,
host_id,
})) => {
assert_eq!(opts.ctl_host, CTL_HOST);
assert_eq!(opts.ctl_port, CTL_PORT);
assert_eq!(opts.ns_prefix, NS_PREFIX);
assert_eq!(opts.timeout, 1);
assert_eq!(output.kind, OutputKind::Json);
assert_eq!(host_id, HOST_ID.to_string());
}
cmd => panic!("ctl get inventory constructed incorrect command {:?}", cmd),
}
let get_claims_all = CtlCli::from_iter_safe(&[
"ctl",
"get",
"claims",
"-o",
"json",
"--ns-prefix",
NS_PREFIX,
"--ctl-host",
CTL_HOST,
"--ctl-port",
CTL_PORT,
"--timeout",
"1",
])?;
match get_claims_all.command {
CtlCliCommand::Get(GetCommand::Claims(GetClaimsCommand { opts, output })) => {
assert_eq!(opts.ctl_host, CTL_HOST);
assert_eq!(opts.ctl_port, CTL_PORT);
assert_eq!(opts.ns_prefix, NS_PREFIX);
assert_eq!(opts.timeout, 1);
assert_eq!(output.kind, OutputKind::Json);
}
cmd => panic!("ctl get claims constructed incorrect command {:?}", cmd),
}
let link_all = CtlCli::from_iter_safe(&[
"ctl",
"link",
"put",
"-o",
"json",
"--ns-prefix",
NS_PREFIX,
"--ctl-host",
CTL_HOST,
"--ctl-port",
CTL_PORT,
"--timeout",
"1",
"--link-name",
"default",
ACTOR_ID,
PROVIDER_ID,
"wasmcloud:provider",
"THING=foo",
])?;
match link_all.command {
CtlCliCommand::Link(LinkCommand::Put(LinkPutCommand {
opts,
output,
actor_id,
provider_id,
contract_id,
link_name,
values,
})) => {
assert_eq!(opts.ctl_host, CTL_HOST);
assert_eq!(opts.ctl_port, CTL_PORT);
assert_eq!(opts.ns_prefix, NS_PREFIX);
assert_eq!(opts.timeout, 1);
assert_eq!(output.kind, OutputKind::Json);
assert_eq!(actor_id, ACTOR_ID.to_string());
assert_eq!(provider_id, PROVIDER_ID.to_string());
assert_eq!(contract_id, "wasmcloud:provider".to_string());
assert_eq!(link_name.unwrap(), "default".to_string());
assert_eq!(values, vec!["THING=foo".to_string()]);
}
cmd => panic!("ctl link put constructed incorrect command {:?}", cmd),
}
let update_all = CtlCli::from_iter_safe(&[
"ctl",
"update",
"actor",
"-o",
"json",
"--ns-prefix",
NS_PREFIX,
"--ctl-host",
CTL_HOST,
"--ctl-port",
CTL_PORT,
"--timeout",
"1",
HOST_ID,
ACTOR_ID,
"wasmcloud.azurecr.io/actor:v2",
])?;
match update_all.command {
CtlCliCommand::Update(UpdateCommand::Actor(super::UpdateActorCommand {
opts,
output,
host_id,
actor_id,
new_actor_ref,
})) => {
assert_eq!(opts.ctl_host, CTL_HOST);
assert_eq!(opts.ctl_port, CTL_PORT);
assert_eq!(opts.ns_prefix, NS_PREFIX);
assert_eq!(opts.timeout, 1);
assert_eq!(output.kind, OutputKind::Json);
assert_eq!(host_id, HOST_ID.to_string());
assert_eq!(actor_id, ACTOR_ID.to_string());
assert_eq!(new_actor_ref, "wasmcloud.azurecr.io/actor:v2".to_string());
}
cmd => panic!("ctl get claims constructed incorrect command {:?}", cmd),
}
Ok(())
}
}