Skip to content

Commit d169fdb

Browse files
APT-543 Download From Artifactory Support (#82)
This PR adds Artifactory download support for Foreman
1 parent 17ea050 commit d169fdb

File tree

12 files changed

+275
-63
lines changed

12 files changed

+275
-63
lines changed

Cargo.lock

Lines changed: 2 additions & 0 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

Cargo.toml

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -33,6 +33,8 @@ toml = "0.5.9"
3333
toml_edit = "0.14.4"
3434
urlencoding = "2.1.0"
3535
zip = "0.5"
36+
url = "2.4.1"
37+
artiaa_auth = { path = "./artiaa_auth" }
3638

3739
[target.'cfg(windows)'.dependencies]
3840
command-group = "1.0.8"

artiaa_auth/src/lib.rs

Lines changed: 3 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
mod error;
1+
pub mod error;
22
mod fs;
33

44
use std::{collections::HashMap, path::Path};
@@ -10,8 +10,8 @@ use crate::error::{ArtifactoryAuthError, ArtifactoryAuthResult};
1010

1111
#[derive(Debug, Default, Serialize, Deserialize, PartialEq, Eq)]
1212
pub struct Credentials {
13-
username: String,
14-
token: String,
13+
pub username: String,
14+
pub token: String,
1515
}
1616

1717
/// Contains stored user tokens that are used to download artifacts from Artifactory.
@@ -21,7 +21,6 @@ pub struct Tokens {
2121
}
2222

2323
impl Tokens {
24-
#[allow(dead_code)]
2524
pub fn load(path: &Path) -> ArtifactoryAuthResult<Self> {
2625
if let Some(contents) = fs::try_read(path)? {
2726
let tokens: Tokens = serde_json::from_slice(&contents)
@@ -34,7 +33,6 @@ impl Tokens {
3433
}
3534
}
3635

37-
#[allow(dead_code)]
3836
pub fn get_credentials(&self, url: &Url) -> Option<&Credentials> {
3937
if let Some(domain) = url.domain() {
4038
self.tokens.get(domain)

src/auth_store.rs

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,6 @@ use crate::{
77
error::{ForemanError, ForemanResult},
88
fs,
99
};
10-
1110
pub static DEFAULT_AUTH_CONFIG: &str = include_str!("../resources/default-auth.toml");
1211

1312
/// Contains stored user tokens that Foreman can use to download tools.

src/config.rs

Lines changed: 41 additions & 27 deletions
Original file line numberDiff line numberDiff line change
@@ -11,13 +11,14 @@ use std::{
1111
env, fmt,
1212
};
1313
use toml::Value;
14+
use url::Url;
1415

1516
const GITHUB: &'static str = "https://github.com";
1617
const GITLAB: &'static str = "https://gitlab.com";
1718

1819
#[derive(Debug, Clone, PartialEq)]
1920
pub struct ToolSpec {
20-
host: String,
21+
host: Url,
2122
path: String,
2223
version: VersionReq,
2324
protocol: Protocol,
@@ -70,7 +71,7 @@ impl ToolSpec {
7071
});
7172
}
7273

73-
let host = host_source.source.to_string();
74+
let host = host_source.source.to_owned();
7475
let path = path_val
7576
.as_str()
7677
.ok_or_else(|| ConfigFileParseError::Tool {
@@ -126,6 +127,10 @@ impl ToolSpec {
126127
Protocol::Artifactory => Provider::Artifactory,
127128
}
128129
}
130+
131+
pub fn host(&self) -> &Url {
132+
&self.host
133+
}
129134
}
130135

131136
impl fmt::Display for ToolSpec {
@@ -142,21 +147,18 @@ pub struct ConfigFile {
142147

143148
#[derive(Debug, PartialEq)]
144149
pub struct Host {
145-
source: String,
150+
source: Url,
146151
protocol: Protocol,
147152
}
148153

149154
impl Host {
150-
pub fn new<S: Into<String>>(source: S, protocol: Protocol) -> Self {
151-
Self {
152-
source: source.into(),
153-
protocol,
154-
}
155+
pub fn new(source: Url, protocol: Protocol) -> Self {
156+
Self { source, protocol }
155157
}
156158

157159
pub fn from_value(value: &Value) -> ConfigFileParseResult<Self> {
158160
if let Value::Table(mut map) = value.clone() {
159-
let source = map
161+
let source_string = map
160162
.remove("source")
161163
.ok_or_else(|| ConfigFileParseError::Host {
162164
host: value.to_string(),
@@ -167,6 +169,9 @@ impl Host {
167169
})?
168170
.to_string();
169171

172+
let source = Url::parse(&source_string).map_err(|_| ConfigFileParseError::Host {
173+
host: value.to_string(),
174+
})?;
170175
let protocol_value =
171176
map.remove("protocol")
172177
.ok_or_else(|| ConfigFileParseError::Host {
@@ -211,9 +216,18 @@ impl ConfigFile {
211216
Self {
212217
tools: BTreeMap::new(),
213218
hosts: HashMap::from([
214-
("source".to_string(), Host::new(GITHUB, Protocol::Github)),
215-
("github".to_string(), Host::new(GITHUB, Protocol::Github)),
216-
("gitlab".to_string(), Host::new(GITLAB, Protocol::Gitlab)),
219+
(
220+
"source".to_string(),
221+
Host::new(Url::parse(GITHUB).unwrap(), Protocol::Github),
222+
),
223+
(
224+
"github".to_string(),
225+
Host::new(Url::parse(GITHUB).unwrap(), Protocol::Github),
226+
),
227+
(
228+
"gitlab".to_string(),
229+
Host::new(Url::parse(GITLAB).unwrap(), Protocol::Gitlab),
230+
),
217231
]),
218232
}
219233
}
@@ -332,7 +346,7 @@ mod test {
332346

333347
fn new_github<S: Into<String>>(github: S, version: VersionReq) -> ToolSpec {
334348
ToolSpec {
335-
host: GITHUB.to_string(),
349+
host: Url::parse(GITHUB).unwrap(),
336350
path: github.into(),
337351
version: version,
338352
protocol: Protocol::Github,
@@ -341,7 +355,7 @@ mod test {
341355

342356
fn new_gitlab<S: Into<String>>(gitlab: S, version: VersionReq) -> ToolSpec {
343357
ToolSpec {
344-
host: GITLAB.to_string(),
358+
host: Url::parse(GITLAB).unwrap(),
345359
path: gitlab.into(),
346360
version: version,
347361
protocol: Protocol::Gitlab,
@@ -350,7 +364,7 @@ mod test {
350364

351365
fn new_artifactory<S: Into<String>>(host: S, path: S, version: VersionReq) -> ToolSpec {
352366
ToolSpec {
353-
host: host.into(),
367+
host: Url::parse(host.into().as_str()).unwrap(),
354368
path: path.into(),
355369
version: version,
356370
protocol: Protocol::Artifactory,
@@ -367,26 +381,23 @@ mod test {
367381
VersionReq::parse(string).unwrap()
368382
}
369383

370-
fn new_host<S: Into<String>>(source: S, protocol: Protocol) -> Host {
371-
Host {
372-
source: source.into(),
373-
protocol,
374-
}
384+
fn new_host(source: Url, protocol: Protocol) -> Host {
385+
Host { source, protocol }
375386
}
376387

377388
fn default_hosts() -> HashMap<String, Host> {
378389
HashMap::from([
379390
(
380391
"source".to_string(),
381-
Host::new(GITHUB.to_string(), Protocol::Github),
392+
Host::new(Url::parse(GITHUB).unwrap(), Protocol::Github),
382393
),
383394
(
384395
"github".to_string(),
385-
Host::new(GITHUB.to_string(), Protocol::Github),
396+
Host::new(Url::parse(GITHUB).unwrap(), Protocol::Github),
386397
),
387398
(
388399
"gitlab".to_string(),
389-
Host::new(GITLAB.to_string(), Protocol::Gitlab),
400+
Host::new(Url::parse(GITLAB).unwrap(), Protocol::Gitlab),
390401
),
391402
])
392403
}
@@ -395,7 +406,7 @@ mod test {
395406
let mut hosts = default_hosts();
396407
hosts.insert(
397408
"artifactory".to_string(),
398-
Host::new(ARTIFACTORY.to_string(), Protocol::Artifactory),
409+
Host::new(Url::parse(ARTIFACTORY).unwrap(), Protocol::Artifactory),
399410
);
400411
hosts
401412
}
@@ -469,7 +480,10 @@ mod test {
469480
let host = Host::from_value(&value).unwrap();
470481
assert_eq!(
471482
host,
472-
new_host("https://artifactory.com", Protocol::Artifactory)
483+
new_host(
484+
Url::parse("https://artifactory.com").unwrap(),
485+
Protocol::Artifactory
486+
)
473487
)
474488
}
475489

@@ -546,7 +560,7 @@ mod test {
546560
BTreeMap::from([(
547561
"tool".to_string(),
548562
ToolSpec {
549-
host: "https://artifactory.com".to_string(),
563+
host: Url::parse("https://artifactory.com").unwrap(),
550564
path: "path/to/tool".to_string(),
551565
version: VersionReq::parse("1.0.0").unwrap(),
552566
protocol: Protocol::Artifactory
@@ -555,7 +569,7 @@ mod test {
555569
HashMap::from([(
556570
"artifactory".to_string(),
557571
Host {
558-
source: "https://artifactory.com".to_string(),
572+
source: Url::parse("https://artifactory.com").unwrap(),
559573
protocol: Protocol::Artifactory
560574
}
561575
)])

src/error.rs

Lines changed: 11 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,7 @@ use std::{fmt, io, path::PathBuf};
33
use semver::Version;
44

55
use crate::config::{ConfigFile, ToolSpec};
6-
6+
use artiaa_auth::error::ArtifactoryAuthError;
77
pub type ForemanResult<T> = Result<T, ForemanError>;
88
pub type ConfigFileParseResult<T> = Result<T, ConfigFileParseError>;
99
#[derive(Debug)]
@@ -71,8 +71,11 @@ pub enum ForemanError {
7171
ToolsNotDownloaded {
7272
tools: Vec<String>,
7373
},
74-
Other {
75-
message: String,
74+
EnvVarNotFound {
75+
env_var: String,
76+
},
77+
ArtiAAError {
78+
error: ArtifactoryAuthError,
7679
},
7780
}
7881

@@ -319,8 +322,11 @@ impl fmt::Display for ForemanError {
319322
Self::ToolsNotDownloaded { tools } => {
320323
write!(f, "The following tools were not installed:\n{:#?}", tools)
321324
}
322-
Self::Other { message } => {
323-
write!(f, "{}", message)
325+
Self::EnvVarNotFound { env_var } => {
326+
write!(f, "Environment Variable not found: {}", env_var)
327+
}
328+
Self::ArtiAAError { error } => {
329+
write!(f, "{}", error)
324330
}
325331
}
326332
}

src/paths.rs

Lines changed: 52 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,15 @@
11
//! Contains all of the paths that Foreman needs to deal with.
22
3-
use std::path::{Path, PathBuf};
3+
use std::{
4+
env,
5+
path::{Path, PathBuf},
6+
};
47

5-
use crate::{auth_store::DEFAULT_AUTH_CONFIG, error::ForemanError, fs};
8+
use crate::{
9+
auth_store::DEFAULT_AUTH_CONFIG,
10+
error::{ForemanError, ForemanResult},
11+
fs,
12+
};
613

714
static DEFAULT_USER_CONFIG: &str = include_str!("../resources/default-foreman.toml");
815

@@ -20,7 +27,7 @@ impl ForemanPaths {
2027
.ok()
2128
.and_then(|path| {
2229
if path.is_dir() {
23-
Some(Self { root_dir:path })
30+
Some(Self { root_dir: path })
2431
} else {
2532
if path.exists() {
2633
log::warn!(
@@ -87,6 +94,48 @@ impl ForemanPaths {
8794

8895
Ok(())
8996
}
97+
98+
pub fn artiaa_path(&self) -> ForemanResult<PathBuf> {
99+
get_artiaa_path_based_on_os()
100+
}
101+
}
102+
103+
#[cfg(target_os = "windows")]
104+
fn get_artiaa_path_based_on_os() -> ForemanResult<PathBuf> {
105+
let localappdata = env::var("LOCALAPPDATA").map_err(|_| ForemanError::EnvVarNotFound {
106+
env_var: "%$LOCALAPPDATA%".to_string(),
107+
})?;
108+
Ok(PathBuf::from(format!(
109+
"{}\\ArtiAA\\artiaa-tokens.json",
110+
localappdata
111+
)))
112+
}
113+
114+
#[cfg(target_os = "macos")]
115+
fn get_artiaa_path_based_on_os() -> ForemanResult<PathBuf> {
116+
let home = env::var("HOME").map_err(|_| ForemanError::EnvVarNotFound {
117+
env_var: "$HOME".to_string(),
118+
})?;
119+
Ok(PathBuf::from(format!(
120+
"{}/Library/Application Support/ArtiAA/artiaa-tokens.json",
121+
home
122+
)))
123+
}
124+
125+
#[cfg(all(not(target_os = "macos"), target_family = "unix"))]
126+
fn get_artiaa_path_based_on_os() -> ForemanResult<PathBuf> {
127+
let xdg_data_home = env::var("XDG_DATA_HOME").map_err(|_| ForemanError::EnvVarNotFound {
128+
env_var: "$XDG_DATA_HOME".to_string(),
129+
})?;
130+
Ok(PathBuf::from(format!(
131+
"{}/ArtiAA/artiaa-tokens.json",
132+
xdg_data_home
133+
)))
134+
}
135+
136+
#[cfg(other)]
137+
fn get_artiaa_path_based_on_os() -> PathBuf {
138+
unimplemented!("artiaa_path is only defined for windows or unix operating systems")
90139
}
91140

92141
impl Default for ForemanPaths {

src/tool_cache.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -103,7 +103,7 @@ impl ToolCache {
103103
log::info!("Downloading {}", tool);
104104

105105
let provider = providers.get(&tool.provider());
106-
let releases = provider.get_releases(tool.path())?;
106+
let releases = provider.get_releases(tool.path(), tool.host())?;
107107

108108
// Filter down our set of releases to those that are valid versions and
109109
// have release assets for our current platform.

0 commit comments

Comments
 (0)