Skip to content

Commit ca7b8d3

Browse files
committed
Further simplify list building
1 parent e6994d2 commit ca7b8d3

5 files changed

Lines changed: 103 additions & 136 deletions

File tree

bins/stackablectl/src/cli.rs

Lines changed: 19 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,9 @@ use std::env;
33
use clap::{Parser, Subcommand, ValueEnum, ValueHint};
44
use stackable::{
55
constants::DEFAULT_STACKABLE_NAMESPACE,
6-
utils::path::{IntoPathsOrUrls, ParsePathsOrUrls, PathOrUrl, PathOrUrlParseError},
6+
utils::path::{
7+
IntoPathOrUrl, IntoPathsOrUrls, ParsePathsOrUrls, PathOrUrl, PathOrUrlParseError,
8+
},
79
};
810
use tracing::Level;
911

@@ -12,7 +14,10 @@ use crate::{
1214
cache::CacheArgs, completions::CompletionsArgs, demo::DemoArgs, operator::OperatorArgs,
1315
release::ReleaseArgs, services::ServicesArgs, stack::StackArgs,
1416
},
15-
constants::{DEMO_FILES_ENV_KEY, RELEASE_FILES_ENV_KEY, STACK_FILES_ENV_KEY},
17+
constants::{
18+
DEMO_FILES_ENV_KEY, RELEASE_FILES_ENV_KEY, REMOTE_DEMO_FILE, REMOTE_RELEASE_FILE,
19+
REMOTE_STACK_FILE, STACK_FILES_ENV_KEY,
20+
},
1621
};
1722

1823
#[derive(Debug, Parser)]
@@ -86,10 +91,13 @@ to provide multiple additional stack files.")]
8691

8792
impl Cli {
8893
pub fn get_demo_files(&self) -> Result<Vec<PathOrUrl>, PathOrUrlParseError> {
89-
let mut files = match env::var(DEMO_FILES_ENV_KEY) {
94+
let mut files: Vec<PathOrUrl> = vec![REMOTE_DEMO_FILE.into_path_or_url()?];
95+
96+
let env_files = match env::var(DEMO_FILES_ENV_KEY) {
9097
Ok(env_files) => env_files.parse_paths_or_urls()?,
9198
Err(_) => vec![],
9299
};
100+
files.extend(env_files);
93101

94102
let arg_files = self.demo_files.clone().into_paths_or_urls()?;
95103
files.extend(arg_files);
@@ -98,10 +106,13 @@ impl Cli {
98106
}
99107

100108
pub fn get_stack_files(&self) -> Result<Vec<PathOrUrl>, PathOrUrlParseError> {
101-
let mut files = match env::var(STACK_FILES_ENV_KEY) {
109+
let mut files: Vec<PathOrUrl> = vec![REMOTE_STACK_FILE.into_path_or_url()?];
110+
111+
let env_files = match env::var(STACK_FILES_ENV_KEY) {
102112
Ok(env_files) => env_files.parse_paths_or_urls()?,
103113
Err(_) => vec![],
104114
};
115+
files.extend(env_files);
105116

106117
let arg_files = self.stack_files.clone().into_paths_or_urls()?;
107118
files.extend(arg_files);
@@ -110,10 +121,13 @@ impl Cli {
110121
}
111122

112123
pub fn get_release_files(&self) -> Result<Vec<PathOrUrl>, PathOrUrlParseError> {
113-
let mut files = match env::var(RELEASE_FILES_ENV_KEY) {
124+
let mut files: Vec<PathOrUrl> = vec![REMOTE_RELEASE_FILE.into_path_or_url()?];
125+
126+
let env_files = match env::var(RELEASE_FILES_ENV_KEY) {
114127
Ok(env_files) => env_files.parse_paths_or_urls()?,
115128
Err(_) => vec![],
116129
};
130+
files.extend(env_files);
117131

118132
let arg_files = self.release_files.clone().into_paths_or_urls()?;
119133
files.extend(arg_files);

bins/stackablectl/src/cmds/demo.rs

Lines changed: 11 additions & 32 deletions
Original file line numberDiff line numberDiff line change
@@ -11,21 +11,14 @@ use stackable::{
1111
release::ReleaseList,
1212
stack::{StackError, StackList},
1313
},
14-
utils::{
15-
params::IntoParametersError,
16-
path::PathOrUrlParseError,
17-
read::{CacheSettings, ReadError},
18-
},
14+
utils::{params::IntoParametersError, path::PathOrUrlParseError, read::CacheSettings},
1915
};
2016
use thiserror::Error;
2117
use xdg::BaseDirectoriesError;
2218

2319
use crate::{
2420
cli::{Cli, ClusterType, OutputType},
25-
constants::{
26-
CACHE_DEMOS_PATH, CACHE_HOME_PATH, CACHE_RELEASES_PATH, CACHE_STACKS_PATH,
27-
REMOTE_DEMO_FILE, REMOTE_RELEASE_FILE, REMOTE_STACK_FILE,
28-
},
21+
constants::CACHE_HOME_PATH,
2922
};
3023

3124
#[derive(Debug, Args)]
@@ -105,9 +98,6 @@ pub enum DemoCmdError {
10598
#[error("io error: {0}")]
10699
IoError(#[from] std::io::Error),
107100

108-
#[error("read error: {0}")]
109-
ReadError(#[from] ReadError),
110-
111101
#[error("yaml error: {0}")]
112102
YamlError(#[from] serde_yaml::Error),
113103

@@ -141,11 +131,10 @@ impl DemoArgs {
141131
// Build demo list based on the (default) remote demo file, and additional files provided by the
142132
// STACKABLE_DEMO_FILES env variable or the --demo-files CLI argument.
143133
let files = common_args.get_demo_files()?;
144-
let cache_file_path = xdg::BaseDirectories::with_prefix(CACHE_HOME_PATH)?
145-
.place_cache_file(CACHE_DEMOS_PATH)?;
134+
let cache_file_path = xdg::BaseDirectories::with_prefix(CACHE_HOME_PATH)?.get_cache_home();
146135

147136
let cache_settings = CacheSettings::from((cache_file_path, !common_args.no_cache));
148-
let list = DemoList::build(REMOTE_DEMO_FILE, files, cache_settings).await?;
137+
let list = DemoList::build(files, cache_settings).await?;
149138

150139
match &self.subcommand {
151140
DemoCommands::List(args) => list_cmd(args, list).await,
@@ -229,15 +218,10 @@ async fn install_cmd(
229218
// Build demo list based on the (default) remote demo file, and additional files provided by the
230219
// STACKABLE_DEMO_FILES env variable or the --demo-files CLI argument.
231220
let files = common_args.get_stack_files()?;
232-
let cache_file_path =
233-
xdg::BaseDirectories::with_prefix(CACHE_HOME_PATH)?.place_cache_file(CACHE_STACKS_PATH)?;
221+
let cache_home_path = xdg::BaseDirectories::with_prefix(CACHE_HOME_PATH)?.get_cache_home();
234222

235-
let stack_list = StackList::build(
236-
REMOTE_STACK_FILE,
237-
files,
238-
(cache_file_path, !common_args.no_cache).into(),
239-
)
240-
.await?;
223+
let stack_list =
224+
StackList::build(files, (cache_home_path, !common_args.no_cache).into()).await?;
241225

242226
// Get the stack spec based on the name defined in the demo spec
243227
let stack_spec = stack_list
@@ -246,15 +230,10 @@ async fn install_cmd(
246230

247231
// TODO (Techassi): Try to move all this boilerplate code to build the lists out of here
248232
let files = common_args.get_stack_files()?;
249-
let cache_file_path = xdg::BaseDirectories::with_prefix(CACHE_HOME_PATH)?
250-
.place_cache_file(CACHE_RELEASES_PATH)?;
251-
252-
let release_list = ReleaseList::build(
253-
REMOTE_RELEASE_FILE,
254-
files,
255-
(cache_file_path, !common_args.no_cache).into(),
256-
)
257-
.await?;
233+
let cache_home_path = xdg::BaseDirectories::with_prefix(CACHE_HOME_PATH)?.get_cache_home();
234+
235+
let release_list =
236+
ReleaseList::build(files, (cache_home_path, !common_args.no_cache).into()).await?;
258237

259238
// Install the stack
260239
stack_spec.install(release_list)?;

bins/stackablectl/src/constants.rs

Lines changed: 0 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -11,7 +11,4 @@ pub const REMOTE_STACK_FILE: &str =
1111
pub const REMOTE_RELEASE_FILE: &str =
1212
"https://raw.githubusercontent.com/stackabletech/release/main/releases.yaml";
1313

14-
pub const CACHE_RELEASES_PATH: &str = "releases.yaml";
15-
pub const CACHE_STACKS_PATH: &str = "stacks.yaml";
1614
pub const CACHE_HOME_PATH: &str = "stackablectl";
17-
pub const CACHE_DEMOS_PATH: &str = "demos.yaml";

src/common/list.rs

Lines changed: 46 additions & 47 deletions
Original file line numberDiff line numberDiff line change
@@ -1,13 +1,15 @@
1-
use std::{fs, marker::PhantomData, path::PathBuf};
1+
use std::{fs, marker::PhantomData};
22

33
use indexmap::IndexMap;
44
use serde::{Deserialize, Serialize};
55
use thiserror::Error;
6-
use url::Url;
76

87
use crate::utils::{
98
path::PathOrUrl,
10-
read::{read_cached_yaml_data, read_yaml_data, CacheSettings, CacheStatus, RemoteReadError},
9+
read::{
10+
read_cached_yaml_data, read_yaml_data_from_file, read_yaml_data_from_remote, CacheSettings,
11+
CacheStatus, CachedReadError, LocalReadError, RemoteReadError,
12+
},
1113
};
1214

1315
pub trait SpecIter<S> {
@@ -19,14 +21,23 @@ pub enum ListError {
1921
#[error("io error: {0}")]
2022
IoError(#[from] std::io::Error),
2123

22-
#[error("read error: {0}")]
23-
ReadError(#[from] RemoteReadError),
24+
#[error("local read error: {0}")]
25+
LocalReadError(#[from] LocalReadError),
26+
27+
#[error("remote read error: {0}")]
28+
RemoteReadError(#[from] RemoteReadError),
29+
30+
#[error("cached read error: {0}")]
31+
CachedReadError(#[from] CachedReadError),
2432

2533
#[error("url parse error: {0}")]
2634
ParseUrlError(#[from] url::ParseError),
2735

2836
#[error("yaml error: {0}")]
2937
YamlError(#[from] serde_yaml::Error),
38+
39+
#[error("invalid file url")]
40+
InvalidFileUrl,
3041
}
3142

3243
/// A [`List`] describes a list of specs. The list can contain any specs, for example demos, stacks or releases. The
@@ -46,35 +57,43 @@ where
4657
L: for<'a> Deserialize<'a> + Serialize + SpecIter<S>,
4758
S: for<'a> Deserialize<'a> + Serialize + Clone,
4859
{
49-
pub async fn build<U, T>(files: T, cache_settings: CacheSettings) -> Result<Self, ListError>
60+
pub async fn build<T>(files: T, cache_settings: CacheSettings) -> Result<Self, ListError>
5061
where
51-
U: AsRef<str>,
5262
T: AsRef<[PathOrUrl]>,
5363
{
5464
let mut map = IndexMap::new();
55-
// let remote_url = Url::parse(remote_url.as_ref())?;
5665

5766
for file in files.as_ref() {
58-
match file {
59-
PathOrUrl::Path(path) => todo!(),
60-
PathOrUrl::Url(_) => todo!(),
61-
}
62-
}
63-
64-
// First load the remote demo file. This uses the cached file if present, and if not, requests the remote file
65-
// and then saves the contents on disk for cached use later
66-
for (spec_name, spec) in Self::get_remote_or_cached_file(remote_url, cache_settings)
67-
.await?
68-
.inner()
69-
{
70-
map.insert(spec_name.clone(), spec.clone());
71-
}
67+
let specs = match file {
68+
PathOrUrl::Path(path) => read_yaml_data_from_file::<L>(path.clone())?,
69+
PathOrUrl::Url(url) => {
70+
if cache_settings.use_cache {
71+
let file_name = url
72+
.path_segments()
73+
.ok_or(ListError::InvalidFileUrl)?
74+
.last()
75+
.ok_or(ListError::InvalidFileUrl)?;
76+
77+
let file_path = cache_settings.base_path.join(file_name);
78+
79+
match read_cached_yaml_data::<L>(file_path.clone(), &cache_settings)? {
80+
CacheStatus::Hit(specs) => specs,
81+
CacheStatus::Expired | CacheStatus::Miss => {
82+
let data = read_yaml_data_from_remote::<L>(url.clone()).await?;
83+
let yaml = serde_yaml::to_string(&data)?;
84+
fs::write(file_path, yaml)?;
85+
86+
data
87+
}
88+
}
89+
} else {
90+
read_yaml_data_from_remote::<L>(url.clone()).await?
91+
}
92+
}
93+
};
7294

73-
// Iterate over all provided files, either from ENV var or CLI argument
74-
for file in files.as_ref() {
75-
let demos = read_yaml_data::<L>(file).await?;
76-
for (demo_name, demo) in demos.inner() {
77-
map.insert(demo_name.to_owned(), demo.to_owned());
95+
for (spec_name, spec) in specs.inner() {
96+
map.insert(spec_name.clone(), spec.clone());
7897
}
7998
}
8099

@@ -96,24 +115,4 @@ where
96115
{
97116
self.inner.get(&name.into())
98117
}
99-
100-
async fn get_remote_or_cached_file(
101-
remote_url: Url,
102-
cache_settings: CacheSettings,
103-
) -> Result<L, ListError> {
104-
let specs = if cache_settings.use_cache {
105-
match read_cached_yaml_data::<L>(&cache_settings)? {
106-
CacheStatus::Hit(demos) => demos,
107-
CacheStatus::Expired | CacheStatus::Miss => {
108-
let demos = read_yaml_data::<L>(remote_url).await?;
109-
fs::write(cache_settings.file_path, serde_yaml::to_string(&demos)?)?;
110-
demos
111-
}
112-
}
113-
} else {
114-
read_yaml_data::<L>(remote_url).await?
115-
};
116-
117-
Ok(specs)
118-
}
119118
}

0 commit comments

Comments
 (0)