Skip to content

Commit

Permalink
feat: fvm show and fvm current command (#3601)
Browse files Browse the repository at this point in the history
Introduces two utility commands for FVM used to check on the active version and
listing installed versions.

Use `fvm current` to print the active Fluvio Version and use `fvm show` to list
installed Fluvio versions.

## Demo

https://github.com/infinyon/fluvio/assets/34756077/a2327a63-1d9e-4ceb-86d3-02522de0aa42
  • Loading branch information
EstebanBorai committed Oct 17, 2023
1 parent 11d4cf0 commit 34fb9ab
Show file tree
Hide file tree
Showing 15 changed files with 292 additions and 8 deletions.
8 changes: 8 additions & 0 deletions Cargo.lock

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

2 changes: 1 addition & 1 deletion crates/fluvio-hub-util/src/fvm/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -149,7 +149,7 @@ mod tests {
#[test]
fn performs_comparisons_between_tags() {
let ver_a = Channel::parse("0.10.10").unwrap();
let ver_b = Channel::parse("0.10.13").unwrap();
let ver_b = Channel::parse("0.10.13-mirroring347239873+20231016").unwrap();

assert!(ver_b > ver_a);
}
Expand Down
4 changes: 4 additions & 0 deletions crates/fluvio-version-manager/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,7 @@ anyhow = { workspace = true }
async-std = { workspace = true, features = ["attributes"] }
clap = { workspace = true, features = ["std", "color", "derive", "env", "help"] }
colored = { workspace = true }
comfy-table = { workspace = true }
dialoguer = { workspace = true }
dirs = { workspace = true }
hex = { workspace = true }
Expand All @@ -36,3 +37,6 @@ url = { workspace = true }
# Workspace Crates
fluvio-hub-util = { workspace = true }
fluvio-future = { workspace = true, features = ["subscriber"] }

[dev-dependencies]
fs_extra = "1.3.0"
2 changes: 2 additions & 0 deletions crates/fluvio-version-manager/fixtures/version/0.10.15/fluvio
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
#!/bin/bash
echo "Fluvio Test Executable 0.10.15"
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
{
"channel": {
"tag": "0.10.15"
},
"version": "0.10.15"
}
2 changes: 2 additions & 0 deletions crates/fluvio-version-manager/fixtures/version/stable/fluvio
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
#!/bin/bash
echo "Fluvio Test Executable STABLE"
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
{
"channel": "stable",
"version": "0.10.16"
}
36 changes: 36 additions & 0 deletions crates/fluvio-version-manager/src/command/current.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,36 @@
//! Show Intalled Versions Command
//!
//! The `show` command is responsible of listing all the installed Fluvio Versions

use anyhow::Result;
use clap::Parser;
use colored::Colorize;

use fluvio_hub_util::fvm::Channel;

use crate::common::notify::Notify;
use crate::common::settings::Settings;

#[derive(Debug, Parser)]
pub struct CurrentOpt;

impl CurrentOpt {
pub async fn process(&self, notify: Notify) -> Result<()> {
let settings = Settings::open()?;

if let (Some(channel), Some(version)) = (settings.channel, settings.version) {
match channel {
Channel::Latest | Channel::Stable => println!("{} ({})", version, channel),
Channel::Tag(_) => println!("{}", version),
}
} else {
notify.warn("No active version set");
notify.help(format!(
"You can use {} to set the active version",
"fvm switch".bold()
));
}

Ok(())
}
}
2 changes: 2 additions & 0 deletions crates/fluvio-version-manager/src/command/mod.rs
Original file line number Diff line number Diff line change
@@ -1,3 +1,5 @@
pub mod current;
pub mod install;
pub mod itself;
pub mod show;
pub mod switch;
81 changes: 81 additions & 0 deletions crates/fluvio-version-manager/src/command/show.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,81 @@
//! Show Intalled Versions Command
//!
//! The `show` command is responsible of listing all the installed Fluvio Versions

use anyhow::{Result, anyhow};
use clap::Parser;
use colored::Colorize;
use comfy_table::{Table, Row};

use crate::common::manifest::VersionManifest;
use crate::common::notify::Notify;
use crate::common::settings::Settings;
use crate::common::version_directory::VersionDirectory;
use crate::common::workdir::fvm_versions_path;

#[derive(Debug, Parser)]
pub struct ShowOpt;

impl ShowOpt {
pub async fn process(&self, notify: Notify) -> Result<()> {
let versions_path = fvm_versions_path()?;

if !versions_path.exists() {
notify.warn("Cannot list installed versions because there are no versions installed");
notify.help(format!(
"You can install a Fluvio version using the command {}",
"fvm install".bold()
));

return Err(anyhow!("No versions installed"));
}

let settings = Settings::open()?;
let (manifests, maybe_active) =
VersionDirectory::scan_versions_manifests(versions_path, settings.channel)?;

if manifests.is_empty() && maybe_active.is_none() {
notify.warn("No installed versions found");
notify.help(format!(
"You can install a Fluvio version using the command {}",
"fvm install".bold()
));

return Ok(());
}

Self::render_table(manifests, maybe_active);

Ok(())
}

/// Creates a `Table` and renders it to the terminal.
fn render_table(manifests: Vec<VersionManifest>, maybe_active: Option<VersionManifest>) {
let mut table = Table::new();

table.set_header(Row::from([" ", "CHANNEL", "VERSION"]));

if let Some(active) = maybe_active {
table.add_row(Row::from([
"✓".to_string(),
active.channel.to_string(),
active.version.to_string(),
]));
}

let mut sorted_manifests = manifests;
sorted_manifests.sort_by(|a, b| b.channel.cmp(&a.channel));

for manifest in sorted_manifests {
table.add_row(Row::from([
" ".to_string(),
manifest.channel.to_string(),
manifest.version.to_string(),
]));
}

table.load_preset(comfy_table::presets::NOTHING);

println!("{}", table);
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -2,18 +2,15 @@
//!
//! The `switch` command is responsible of changing the active Fluvio Version

mod version_directory;

use anyhow::Result;
use clap::Parser;
use colored::Colorize;

use fluvio_hub_util::fvm::Channel;

use crate::common::workdir::fvm_versions_path;
use crate::common::notify::Notify;

use self::version_directory::VersionDirectory;
use crate::common::version_directory::VersionDirectory;
use crate::common::workdir::fvm_versions_path;

#[derive(Debug, Parser)]
pub struct SwitchOpt {
Expand Down
1 change: 1 addition & 0 deletions crates/fluvio-version-manager/src/common/mod.rs
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
pub mod manifest;
pub mod notify;
pub mod settings;
pub mod version_directory;
pub mod workdir;

use std::path::PathBuf;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,8 @@ use std::path::PathBuf;

use anyhow::Result;

use fluvio_hub_util::fvm::Channel;

use crate::common::manifest::{PACKAGE_SET_MANIFEST_FILENAME, VersionManifest};
use crate::common::settings::Settings;
use crate::common::workdir::fluvio_binaries_path;
Expand Down Expand Up @@ -80,6 +82,41 @@ impl VersionDirectory {

Ok(())
}

/// Retrieves the sorted list of installed versions [`VersionManifest`]
/// instances. In parallel, it also retrieves the active version if any.
///
/// If theres any Active Version, the [`VersionManifest`] is not included
/// as part of the first tuple value, instead it is returned as the second
/// tuple value.
pub fn scan_versions_manifests(
versions_path: PathBuf,
maybe_active: Option<Channel>,
) -> Result<(Vec<VersionManifest>, Option<VersionManifest>)> {
let dir_entries = versions_path.read_dir()?;
let mut manifests: Vec<VersionManifest> = Vec::new();
let mut active_version: Option<VersionManifest> = None;

for entry in dir_entries {
let entry = entry?;
let path = entry.path();

if path.is_dir() {
let version_dir = VersionDirectory::open(path.to_path_buf())?;

if let Some(ref active_channel) = maybe_active {
if *active_channel == version_dir.manifest.channel {
active_version = Some(version_dir.manifest);
continue;
}
}

manifests.push(version_dir.manifest);
}
}

Ok((manifests, active_version))
}
}

#[cfg(test)]
Expand All @@ -88,7 +125,7 @@ mod tests {
use std::path::Path;

use anyhow::Result;

use fs_extra::dir::{copy as copy_dir, CopyOptions};
use tempfile::TempDir;

use fluvio_hub_util::sha256_digest;
Expand Down Expand Up @@ -129,6 +166,19 @@ mod tests {
Ok(tmp)
}

/// Creates a Temporal Directory with the contents of `fixtures/version`
/// directory
fn make_versions_directory() -> Result<TempDir> {
let fixtures_path = Path::new(env!("CARGO_MANIFEST_DIR"))
.join("fixtures")
.join("version");
let tmp = TempDir::new()?;

copy_dir(fixtures_path, tmp.path(), &CopyOptions::default())?;

Ok(tmp)
}

#[test]
fn opens_directory_as_version_dir() {
let tmpdir = make_version_directory().unwrap();
Expand Down Expand Up @@ -224,4 +274,34 @@ mod tests {

delete_fvm_dir();
}

#[test]
fn lists_versions_in_dir() {
let tmpdir = make_versions_directory().unwrap();
let (manifests, _) = VersionDirectory::scan_versions_manifests(
tmpdir.path().to_path_buf().join("version"),
None,
)
.unwrap();
let expected_versions = vec!["0.10.14", "0.10.15", "stable"];

for ver in expected_versions {
assert!(
manifests.iter().any(|m| m.channel.to_string() == ver),
"version {ver} not found",
);
}
}

#[test]
fn determines_active_version() {
let tmpdir = make_versions_directory().unwrap();
let (_, active) = VersionDirectory::scan_versions_manifests(
tmpdir.path().to_path_buf().join("version"),
Some(Channel::Stable),
)
.unwrap();

assert_eq!(active.unwrap().channel, Channel::Stable);
}
}
10 changes: 10 additions & 0 deletions crates/fluvio-version-manager/src/main.rs
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,8 @@ mod common;

use anyhow::Result;
use clap::Parser;
use command::current::CurrentOpt;
use command::show::ShowOpt;

use self::command::install::InstallOpt;
use self::command::itself::SelfOpt;
Expand Down Expand Up @@ -35,12 +37,18 @@ pub struct Cli {

#[derive(Debug, Parser)]
pub enum Command {
/// Print the current active Fluvio Version
#[command(name = "current")]
Current(CurrentOpt),
/// Manage FVM
#[command(name = "self")]
Itself(SelfOpt),
/// Install a Fluvio Version
#[command(name = "install")]
Install(InstallOpt),
/// List installed Fluvio Versions
#[command(name = "show")]
Show(ShowOpt),
/// Set a installed Fluvio Version as active
#[command(name = "switch")]
Switch(SwitchOpt),
Expand All @@ -53,8 +61,10 @@ impl Cli {
let notify = Notify::new(self.quiet);

match command {
Command::Current(cmd) => cmd.process(notify).await,
Command::Itself(cmd) => cmd.process(notify).await,
Command::Install(cmd) => cmd.process(notify).await,
Command::Show(cmd) => cmd.process(notify).await,
Command::Switch(cmd) => cmd.process(notify).await,
}
}
Expand Down
Loading

0 comments on commit 34fb9ab

Please sign in to comment.