mirror of
https://github.com/rustfs/rustfs.git
synced 2026-08-20 19:42:17 +00:00
fix: 调整endpoint位置
This commit is contained in:
@@ -0,0 +1,381 @@
|
|||||||
|
use crate::error::{Error, Result};
|
||||||
|
use crate::utils::net;
|
||||||
|
use path_absolutize::Absolutize;
|
||||||
|
use path_clean::PathClean;
|
||||||
|
use std::{fmt::Display, path::Path};
|
||||||
|
use url::{ParseError, Url};
|
||||||
|
|
||||||
|
/// enum for endpoint type.
|
||||||
|
#[derive(PartialEq, Eq, Debug)]
|
||||||
|
pub enum EndpointType {
|
||||||
|
/// path style endpoint type enum.
|
||||||
|
Path,
|
||||||
|
|
||||||
|
/// URL style endpoint type enum.
|
||||||
|
Url,
|
||||||
|
}
|
||||||
|
|
||||||
|
/// any type of endpoint.
|
||||||
|
#[derive(Debug, PartialEq, Eq, Clone)]
|
||||||
|
pub struct Endpoint {
|
||||||
|
pub url: url::Url,
|
||||||
|
pub is_local: bool,
|
||||||
|
|
||||||
|
pub pool_idx: Option<usize>,
|
||||||
|
pub set_idx: Option<usize>,
|
||||||
|
pub disk_idx: Option<usize>,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Display for Endpoint {
|
||||||
|
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
|
||||||
|
if self.url.scheme() == "file" {
|
||||||
|
write!(f, "{}", self.url.path())
|
||||||
|
} else {
|
||||||
|
write!(f, "{}", self.url)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl TryFrom<&str> for Endpoint {
|
||||||
|
/// The type returned in the event of a conversion error.
|
||||||
|
type Error = Error;
|
||||||
|
|
||||||
|
/// Performs the conversion.
|
||||||
|
fn try_from(value: &str) -> Result<Self, Self::Error> {
|
||||||
|
/// check whether given path is not empty.
|
||||||
|
fn is_empty_path(path: impl AsRef<Path>) -> bool {
|
||||||
|
["", "/", "\\"].iter().any(|&v| Path::new(v).eq(path.as_ref()))
|
||||||
|
}
|
||||||
|
|
||||||
|
if is_empty_path(value) {
|
||||||
|
return Err(Error::from_string("empty or root endpoint is not supported"));
|
||||||
|
}
|
||||||
|
|
||||||
|
let mut is_local = false;
|
||||||
|
let url = match Url::parse(value) {
|
||||||
|
#[allow(unused_mut)]
|
||||||
|
Ok(mut url) if url.has_host() => {
|
||||||
|
// URL style of endpoint.
|
||||||
|
// Valid URL style endpoint is
|
||||||
|
// - Scheme field must contain "http" or "https"
|
||||||
|
// - All field should be empty except Host and Path.
|
||||||
|
if !((url.scheme() == "http" || url.scheme() == "https")
|
||||||
|
&& url.username().is_empty()
|
||||||
|
&& url.fragment().is_none()
|
||||||
|
&& url.query().is_none())
|
||||||
|
{
|
||||||
|
return Err(Error::from_string("invalid URL endpoint format"));
|
||||||
|
}
|
||||||
|
|
||||||
|
let path = Path::new(url.path()).clean();
|
||||||
|
if is_empty_path(&path) {
|
||||||
|
return Err(Error::from_string("empty or root path is not supported in URL endpoint"));
|
||||||
|
}
|
||||||
|
|
||||||
|
if let Some(v) = path.to_str() {
|
||||||
|
url.set_path(v)
|
||||||
|
}
|
||||||
|
|
||||||
|
// On windows having a preceding SlashSeparator will cause problems, if the
|
||||||
|
// command line already has C:/<export-folder/ in it. Final resulting
|
||||||
|
// path on windows might become C:/C:/ this will cause problems
|
||||||
|
// of starting rustfs server properly in distributed mode on windows.
|
||||||
|
// As a special case make sure to trim the separator.
|
||||||
|
|
||||||
|
// NOTE: It is also perfectly fine for windows users to have a path
|
||||||
|
// without C:/ since at that point we treat it as relative path
|
||||||
|
// and obtain the full filesystem path as well. Providing C:/
|
||||||
|
// style is necessary to provide paths other than C:/,
|
||||||
|
// such as F:/, D:/ etc.
|
||||||
|
//
|
||||||
|
// Another additional benefit here is that this style also
|
||||||
|
// supports providing \\host\share support as well.
|
||||||
|
#[cfg(windows)]
|
||||||
|
{
|
||||||
|
let path = url.path().to_owned();
|
||||||
|
if Path::new(&path[1..]).is_absolute() {
|
||||||
|
url.set_path(&path[1..]);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
url
|
||||||
|
}
|
||||||
|
Ok(_) => {
|
||||||
|
// like d:/foo
|
||||||
|
is_local = true;
|
||||||
|
url_parse_from_file_path(value)?
|
||||||
|
}
|
||||||
|
Err(e) => match e {
|
||||||
|
ParseError::InvalidPort => {
|
||||||
|
return Err(Error::from_string("invalid URL endpoint format: port number must be between 1 to 65535"))
|
||||||
|
}
|
||||||
|
ParseError::EmptyHost => return Err(Error::from_string("invalid URL endpoint format: empty host name")),
|
||||||
|
ParseError::RelativeUrlWithoutBase => {
|
||||||
|
// like /foo
|
||||||
|
is_local = true;
|
||||||
|
url_parse_from_file_path(value)?
|
||||||
|
}
|
||||||
|
_ => return Err(Error::from_string(format!("invalid URL endpoint format: {}", e))),
|
||||||
|
},
|
||||||
|
};
|
||||||
|
|
||||||
|
Ok(Endpoint {
|
||||||
|
url,
|
||||||
|
is_local,
|
||||||
|
pool_idx: None,
|
||||||
|
set_idx: None,
|
||||||
|
disk_idx: None,
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Endpoint {
|
||||||
|
/// returns type of endpoint.
|
||||||
|
pub fn get_type(&self) -> EndpointType {
|
||||||
|
if self.url.scheme() == "file" {
|
||||||
|
EndpointType::Path
|
||||||
|
} else {
|
||||||
|
EndpointType::Url
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// sets a specific pool number to this node
|
||||||
|
pub fn set_pool_index(&mut self, idx: usize) {
|
||||||
|
self.pool_idx = Some(idx)
|
||||||
|
}
|
||||||
|
|
||||||
|
/// sets a specific set number to this node
|
||||||
|
pub fn set_set_index(&mut self, idx: usize) {
|
||||||
|
self.set_idx = Some(idx)
|
||||||
|
}
|
||||||
|
|
||||||
|
/// sets a specific disk number to this node
|
||||||
|
pub fn set_disk_index(&mut self, idx: usize) {
|
||||||
|
self.disk_idx = Some(idx)
|
||||||
|
}
|
||||||
|
|
||||||
|
/// resolves the host and updates if it is local or not.
|
||||||
|
pub fn update_is_local(&mut self, local_port: u16) -> Result<()> {
|
||||||
|
match (self.url.scheme(), self.url.host()) {
|
||||||
|
(v, Some(host)) if v != "file" => {
|
||||||
|
self.is_local = net::is_local_host(host, self.url.port().unwrap_or_default(), local_port)?;
|
||||||
|
}
|
||||||
|
_ => {}
|
||||||
|
}
|
||||||
|
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|
||||||
|
/// returns the host to be used for grid connections.
|
||||||
|
pub fn grid_host(&self) -> String {
|
||||||
|
match (self.url.host(), self.url.port()) {
|
||||||
|
(Some(host), Some(port)) => format!("{}://{}:{}", self.url.scheme(), host, port),
|
||||||
|
(Some(host), None) => format!("{}://{}", self.url.scheme(), host),
|
||||||
|
_ => String::new(),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn host_port(&self) -> String {
|
||||||
|
match (self.url.host(), self.url.port()) {
|
||||||
|
(Some(host), Some(port)) => format!("{}:{}", host, port),
|
||||||
|
(Some(host), None) => format!("{}", host),
|
||||||
|
_ => String::new(),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// parse a file path into an URL.
|
||||||
|
fn url_parse_from_file_path(value: &str) -> Result<url::Url> {
|
||||||
|
// Only check if the arg is an ip address and ask for scheme since its absent.
|
||||||
|
// localhost, example.com, any FQDN cannot be disambiguated from a regular file path such as
|
||||||
|
// /mnt/export1. So we go ahead and start the rustfs server in FS modes in these cases.
|
||||||
|
let addr: Vec<&str> = value.splitn(2, '/').collect();
|
||||||
|
if net::is_socket_addr(addr[0]) {
|
||||||
|
return Err(Error::from_string("invalid URL endpoint format: missing scheme http or https"));
|
||||||
|
}
|
||||||
|
|
||||||
|
let file_path = match Path::new(value).absolutize() {
|
||||||
|
Ok(path) => path,
|
||||||
|
Err(err) => return Err(Error::from_string(format!("absolute path failed: {}", err))),
|
||||||
|
};
|
||||||
|
|
||||||
|
match Url::from_file_path(file_path) {
|
||||||
|
Ok(url) => Ok(url),
|
||||||
|
Err(_) => Err(Error::from_string("Convert a file path into an URL failed")),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[cfg(test)]
|
||||||
|
mod test {
|
||||||
|
|
||||||
|
use super::*;
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_new_endpoint() {
|
||||||
|
#[derive(Default)]
|
||||||
|
struct TestCase<'a> {
|
||||||
|
arg: &'a str,
|
||||||
|
expected_endpoint: Option<Endpoint>,
|
||||||
|
expected_type: Option<EndpointType>,
|
||||||
|
expected_err: Option<Error>,
|
||||||
|
}
|
||||||
|
|
||||||
|
let u2 = url::Url::parse("https://example.org/path").unwrap();
|
||||||
|
let u4 = url::Url::parse("http://192.168.253.200/path").unwrap();
|
||||||
|
let u6 = url::Url::parse("http://server:/path").unwrap();
|
||||||
|
let root_slash_foo = url::Url::from_file_path("/foo").unwrap();
|
||||||
|
|
||||||
|
let test_cases = [
|
||||||
|
TestCase {
|
||||||
|
arg: "/foo",
|
||||||
|
expected_endpoint: Some(Endpoint {
|
||||||
|
url: root_slash_foo,
|
||||||
|
is_local: true,
|
||||||
|
pool_idx: None,
|
||||||
|
set_idx: None,
|
||||||
|
disk_idx: None,
|
||||||
|
}),
|
||||||
|
expected_type: Some(EndpointType::Path),
|
||||||
|
expected_err: None,
|
||||||
|
},
|
||||||
|
TestCase {
|
||||||
|
arg: "https://example.org/path",
|
||||||
|
expected_endpoint: Some(Endpoint {
|
||||||
|
url: u2,
|
||||||
|
is_local: false,
|
||||||
|
pool_idx: None,
|
||||||
|
set_idx: None,
|
||||||
|
disk_idx: None,
|
||||||
|
}),
|
||||||
|
expected_type: Some(EndpointType::Url),
|
||||||
|
expected_err: None,
|
||||||
|
},
|
||||||
|
TestCase {
|
||||||
|
arg: "http://192.168.253.200/path",
|
||||||
|
expected_endpoint: Some(Endpoint {
|
||||||
|
url: u4,
|
||||||
|
is_local: false,
|
||||||
|
pool_idx: None,
|
||||||
|
set_idx: None,
|
||||||
|
disk_idx: None,
|
||||||
|
}),
|
||||||
|
expected_type: Some(EndpointType::Url),
|
||||||
|
expected_err: None,
|
||||||
|
},
|
||||||
|
TestCase {
|
||||||
|
arg: "",
|
||||||
|
expected_endpoint: None,
|
||||||
|
expected_type: None,
|
||||||
|
expected_err: Some(Error::from_string("empty or root endpoint is not supported")),
|
||||||
|
},
|
||||||
|
TestCase {
|
||||||
|
arg: "/",
|
||||||
|
expected_endpoint: None,
|
||||||
|
expected_type: None,
|
||||||
|
expected_err: Some(Error::from_string("empty or root endpoint is not supported")),
|
||||||
|
},
|
||||||
|
TestCase {
|
||||||
|
arg: "\\",
|
||||||
|
expected_endpoint: None,
|
||||||
|
expected_type: None,
|
||||||
|
expected_err: Some(Error::from_string("empty or root endpoint is not supported")),
|
||||||
|
},
|
||||||
|
TestCase {
|
||||||
|
arg: "c://foo",
|
||||||
|
expected_endpoint: None,
|
||||||
|
expected_type: None,
|
||||||
|
expected_err: Some(Error::from_string("invalid URL endpoint format")),
|
||||||
|
},
|
||||||
|
TestCase {
|
||||||
|
arg: "ftp://foo",
|
||||||
|
expected_endpoint: None,
|
||||||
|
expected_type: None,
|
||||||
|
expected_err: Some(Error::from_string("invalid URL endpoint format")),
|
||||||
|
},
|
||||||
|
TestCase {
|
||||||
|
arg: "http://server/path?location",
|
||||||
|
expected_endpoint: None,
|
||||||
|
expected_type: None,
|
||||||
|
expected_err: Some(Error::from_string("invalid URL endpoint format")),
|
||||||
|
},
|
||||||
|
TestCase {
|
||||||
|
arg: "http://:/path",
|
||||||
|
expected_endpoint: None,
|
||||||
|
expected_type: None,
|
||||||
|
expected_err: Some(Error::from_string("invalid URL endpoint format: empty host name")),
|
||||||
|
},
|
||||||
|
TestCase {
|
||||||
|
arg: "http://:8080/path",
|
||||||
|
expected_endpoint: None,
|
||||||
|
expected_type: None,
|
||||||
|
expected_err: Some(Error::from_string("invalid URL endpoint format: empty host name")),
|
||||||
|
},
|
||||||
|
TestCase {
|
||||||
|
arg: "http://server:/path",
|
||||||
|
expected_endpoint: Some(Endpoint {
|
||||||
|
url: u6,
|
||||||
|
is_local: false,
|
||||||
|
pool_idx: None,
|
||||||
|
set_idx: None,
|
||||||
|
disk_idx: None,
|
||||||
|
}),
|
||||||
|
expected_type: Some(EndpointType::Url),
|
||||||
|
expected_err: None,
|
||||||
|
},
|
||||||
|
TestCase {
|
||||||
|
arg: "https://93.184.216.34:808080/path",
|
||||||
|
expected_endpoint: None,
|
||||||
|
expected_type: None,
|
||||||
|
expected_err: Some(Error::from_string("invalid URL endpoint format: port number must be between 1 to 65535")),
|
||||||
|
},
|
||||||
|
TestCase {
|
||||||
|
arg: "http://server:8080//",
|
||||||
|
expected_endpoint: None,
|
||||||
|
expected_type: None,
|
||||||
|
expected_err: Some(Error::from_string("empty or root path is not supported in URL endpoint")),
|
||||||
|
},
|
||||||
|
TestCase {
|
||||||
|
arg: "http://server:8080/",
|
||||||
|
expected_endpoint: None,
|
||||||
|
expected_type: None,
|
||||||
|
expected_err: Some(Error::from_string("empty or root path is not supported in URL endpoint")),
|
||||||
|
},
|
||||||
|
TestCase {
|
||||||
|
arg: "192.168.1.210:9000",
|
||||||
|
expected_endpoint: None,
|
||||||
|
expected_type: None,
|
||||||
|
expected_err: Some(Error::from_string("invalid URL endpoint format: missing scheme http or https")),
|
||||||
|
},
|
||||||
|
];
|
||||||
|
|
||||||
|
for test_case in test_cases {
|
||||||
|
let ret = Endpoint::try_from(test_case.arg);
|
||||||
|
if test_case.expected_err.is_none() && ret.is_err() {
|
||||||
|
panic!("{}: error: expected = <nil>, got = {:?}", test_case.arg, ret);
|
||||||
|
}
|
||||||
|
if test_case.expected_err.is_some() && ret.is_ok() {
|
||||||
|
panic!("{}: error: expected = {:?}, got = <nil>", test_case.arg, test_case.expected_err);
|
||||||
|
}
|
||||||
|
match (test_case.expected_err, ret) {
|
||||||
|
(None, Err(e)) => panic!("{}: error: expected = <nil>, got = {}", test_case.arg, e),
|
||||||
|
(None, Ok(mut ep)) => {
|
||||||
|
let _ = ep.update_is_local(9000);
|
||||||
|
if test_case.expected_type != Some(ep.get_type()) {
|
||||||
|
panic!(
|
||||||
|
"{}: type: expected = {:?}, got = {:?}",
|
||||||
|
test_case.arg,
|
||||||
|
test_case.expected_type,
|
||||||
|
ep.get_type()
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
assert_eq!(test_case.expected_endpoint, Some(ep), "{}: endpoint", test_case.arg);
|
||||||
|
}
|
||||||
|
(Some(e), Ok(_)) => panic!("{}: error: expected = {}, got = <nil>", test_case.arg, e),
|
||||||
|
(Some(e), Err(e2)) => {
|
||||||
|
assert_eq!(e.to_string(), e2.to_string(), "{}: error: expected = {}, got = {}", test_case.arg, e, e2)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -1,4 +1,4 @@
|
|||||||
use super::{error::DiskError, format::FormatV3};
|
use super::{endpoint::Endpoint, error::DiskError, format::FormatV3};
|
||||||
use super::{
|
use super::{
|
||||||
DeleteOptions, DiskAPI, FileReader, FileWriter, ReadMultipleReq, ReadMultipleResp, ReadOptions, RenameDataResp, VolumeInfo,
|
DeleteOptions, DiskAPI, FileReader, FileWriter, ReadMultipleReq, ReadMultipleResp, ReadOptions, RenameDataResp, VolumeInfo,
|
||||||
};
|
};
|
||||||
@@ -17,7 +17,7 @@ use tracing::{debug, warn};
|
|||||||
use uuid::Uuid;
|
use uuid::Uuid;
|
||||||
|
|
||||||
use crate::{
|
use crate::{
|
||||||
endpoint::{Endpoint, Endpoints},
|
endpoints::Endpoints,
|
||||||
error::{Error, Result},
|
error::{Error, Result},
|
||||||
file_meta::FileMeta,
|
file_meta::FileMeta,
|
||||||
store_api::{FileInfo, RawFileInfo},
|
store_api::{FileInfo, RawFileInfo},
|
||||||
|
|||||||
@@ -1,3 +1,4 @@
|
|||||||
|
pub mod endpoint;
|
||||||
pub mod error;
|
pub mod error;
|
||||||
pub mod format;
|
pub mod format;
|
||||||
mod local;
|
mod local;
|
||||||
|
|||||||
+27
-37
@@ -1,4 +1,4 @@
|
|||||||
use super::error::{Error, Result};
|
use crate::error::{Error, Result};
|
||||||
use crate::utils::ellipses::*;
|
use crate::utils::ellipses::*;
|
||||||
use serde::Deserialize;
|
use serde::Deserialize;
|
||||||
use std::collections::HashSet;
|
use std::collections::HashSet;
|
||||||
@@ -13,51 +13,31 @@ pub struct PoolDisksLayout {
|
|||||||
layout: Vec<Vec<String>>,
|
layout: Vec<Vec<String>>,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl AsRef<Vec<Vec<String>>> for PoolDisksLayout {
|
|
||||||
fn as_ref(&self) -> &Vec<Vec<String>> {
|
|
||||||
&self.layout
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl AsMut<Vec<Vec<String>>> for PoolDisksLayout {
|
|
||||||
fn as_mut(&mut self) -> &mut Vec<Vec<String>> {
|
|
||||||
&mut self.layout
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl PoolDisksLayout {
|
impl PoolDisksLayout {
|
||||||
pub fn new(args: impl Into<String>, layout: Vec<Vec<String>>) -> Self {
|
fn new(args: impl Into<String>, layout: Vec<Vec<String>>) -> Self {
|
||||||
PoolDisksLayout {
|
PoolDisksLayout {
|
||||||
cmd_line: args.into(),
|
cmd_line: args.into(),
|
||||||
layout,
|
layout,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn count(&self) -> usize {
|
fn count(&self) -> usize {
|
||||||
self.layout.len()
|
self.layout.len()
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn get_cmd_line(&self) -> &str {
|
fn get_cmd_line(&self) -> &str {
|
||||||
&self.cmd_line
|
&self.cmd_line
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub fn iter(&self) -> impl Iterator<Item = &Vec<String>> {
|
||||||
|
self.layout.iter()
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Deserialize, Debug, Default)]
|
#[derive(Deserialize, Debug, Default)]
|
||||||
pub struct DisksLayout {
|
pub struct DisksLayout {
|
||||||
pub legacy: bool,
|
pub legacy: bool,
|
||||||
pools: Vec<PoolDisksLayout>,
|
pub pools: Vec<PoolDisksLayout>,
|
||||||
}
|
|
||||||
|
|
||||||
impl AsRef<Vec<PoolDisksLayout>> for DisksLayout {
|
|
||||||
fn as_ref(&self) -> &Vec<PoolDisksLayout> {
|
|
||||||
&self.pools
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl AsMut<Vec<PoolDisksLayout>> for DisksLayout {
|
|
||||||
fn as_mut(&mut self) -> &mut Vec<PoolDisksLayout> {
|
|
||||||
&mut self.pools
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<T: AsRef<str>> TryFrom<&[T]> for DisksLayout {
|
impl<T: AsRef<str>> TryFrom<&[T]> for DisksLayout {
|
||||||
@@ -119,8 +99,19 @@ impl DisksLayout {
|
|||||||
&self.pools[0].layout[0][0]
|
&self.pools[0].layout[0][0]
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn get_layout(&self, idx: usize) -> Option<&PoolDisksLayout> {
|
/// returns the total number of sets in the layout.
|
||||||
self.pools.get(idx)
|
pub fn get_set_count(&self, i: usize) -> usize {
|
||||||
|
self.pools.get(i).map_or(0, |v| v.count())
|
||||||
|
}
|
||||||
|
|
||||||
|
/// returns the total number of drives in the layout.
|
||||||
|
pub fn get_drives_per_set(&self, i: usize) -> usize {
|
||||||
|
self.pools.get(i).map_or(0, |v| v.layout.first().map_or(0, |v| v.len()))
|
||||||
|
}
|
||||||
|
|
||||||
|
/// returns the command line for the given index.
|
||||||
|
pub fn get_cmd_line(&self, i: usize) -> String {
|
||||||
|
self.pools.get(i).map_or(String::new(), |v| v.get_cmd_line().to_owned())
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -162,10 +153,10 @@ fn get_all_sets<T: AsRef<str>>(is_ellipses: bool, args: &[T]) -> Result<Vec<Vec<
|
|||||||
/// represents parsed ellipses values, also provides
|
/// represents parsed ellipses values, also provides
|
||||||
/// methods to get the sets of endpoints.
|
/// methods to get the sets of endpoints.
|
||||||
#[derive(Debug, Default)]
|
#[derive(Debug, Default)]
|
||||||
pub struct EndpointSet {
|
struct EndpointSet {
|
||||||
pub _arg_patterns: Vec<ArgPattern>,
|
_arg_patterns: Vec<ArgPattern>,
|
||||||
pub endpoints: Vec<String>,
|
endpoints: Vec<String>,
|
||||||
pub set_indexes: Vec<Vec<usize>>,
|
set_indexes: Vec<Vec<usize>>,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<T: AsRef<str>> TryFrom<&[T]> for EndpointSet {
|
impl<T: AsRef<str>> TryFrom<&[T]> for EndpointSet {
|
||||||
@@ -357,7 +348,6 @@ fn get_total_sizes(arg_patterns: &[ArgPattern]) -> Vec<usize> {
|
|||||||
mod test {
|
mod test {
|
||||||
|
|
||||||
use super::*;
|
use super::*;
|
||||||
use crate::utils::ellipses;
|
|
||||||
|
|
||||||
impl PartialEq for EndpointSet {
|
impl PartialEq for EndpointSet {
|
||||||
fn eq(&self, other: &Self) -> bool {
|
fn eq(&self, other: &Self) -> bool {
|
||||||
@@ -532,7 +522,7 @@ mod test {
|
|||||||
for test_case in test_cases {
|
for test_case in test_cases {
|
||||||
let mut arg_patterns = Vec::new();
|
let mut arg_patterns = Vec::new();
|
||||||
for v in test_case.args.iter() {
|
for v in test_case.args.iter() {
|
||||||
match ellipses::find_ellipses_patterns(v) {
|
match find_ellipses_patterns(v) {
|
||||||
Ok(patterns) => {
|
Ok(patterns) => {
|
||||||
arg_patterns.push(patterns);
|
arg_patterns.push(patterns);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,26 +1,13 @@
|
|||||||
use super::disks_layout::DisksLayout;
|
use crate::{
|
||||||
use super::error::{Error, Result};
|
disk::endpoint::{Endpoint, EndpointType},
|
||||||
use super::utils::net;
|
disks_layout::DisksLayout,
|
||||||
use path_absolutize::Absolutize;
|
error::{Error, Result},
|
||||||
use path_clean::PathClean;
|
utils::net,
|
||||||
use std::collections::HashSet;
|
};
|
||||||
use std::fmt::Display;
|
use std::{
|
||||||
use std::net::IpAddr;
|
collections::{hash_map::Entry, HashMap, HashSet},
|
||||||
use std::{
|
net::IpAddr,
|
||||||
collections::{hash_map::Entry, HashMap},
|
|
||||||
path::Path,
|
|
||||||
};
|
};
|
||||||
use url::{ParseError, Url};
|
|
||||||
|
|
||||||
/// enum for endpoint type.
|
|
||||||
#[derive(PartialEq, Eq, Debug)]
|
|
||||||
pub enum EndpointType {
|
|
||||||
/// path style endpoint type enum.
|
|
||||||
Path,
|
|
||||||
|
|
||||||
/// URL style endpoint type enum.
|
|
||||||
Url,
|
|
||||||
}
|
|
||||||
|
|
||||||
/// enum for setup type.
|
/// enum for setup type.
|
||||||
#[derive(PartialEq, Eq, Debug)]
|
#[derive(PartialEq, Eq, Debug)]
|
||||||
@@ -50,175 +37,6 @@ pub struct Node {
|
|||||||
pub grid_host: String,
|
pub grid_host: String,
|
||||||
}
|
}
|
||||||
|
|
||||||
/// any type of endpoint.
|
|
||||||
#[derive(Debug, PartialEq, Eq, Clone)]
|
|
||||||
pub struct Endpoint {
|
|
||||||
pub url: url::Url,
|
|
||||||
pub is_local: bool,
|
|
||||||
|
|
||||||
pub pool_idx: Option<usize>,
|
|
||||||
pub set_idx: Option<usize>,
|
|
||||||
pub disk_idx: Option<usize>,
|
|
||||||
}
|
|
||||||
|
|
||||||
impl Display for Endpoint {
|
|
||||||
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
|
|
||||||
if self.url.scheme() == "file" {
|
|
||||||
write!(f, "{}", self.url.path())
|
|
||||||
} else {
|
|
||||||
write!(f, "{}", self.url)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl TryFrom<&str> for Endpoint {
|
|
||||||
/// The type returned in the event of a conversion error.
|
|
||||||
type Error = Error;
|
|
||||||
|
|
||||||
/// Performs the conversion.
|
|
||||||
fn try_from(value: &str) -> Result<Self, Self::Error> {
|
|
||||||
/// check whether given path is not empty.
|
|
||||||
fn is_empty_path(path: impl AsRef<Path>) -> bool {
|
|
||||||
["", "/", "\\"].iter().any(|&v| Path::new(v).eq(path.as_ref()))
|
|
||||||
}
|
|
||||||
|
|
||||||
if is_empty_path(value) {
|
|
||||||
return Err(Error::from_string("empty or root endpoint is not supported"));
|
|
||||||
}
|
|
||||||
|
|
||||||
let mut is_local = false;
|
|
||||||
let url = match Url::parse(value) {
|
|
||||||
#[allow(unused_mut)]
|
|
||||||
Ok(mut url) if url.has_host() => {
|
|
||||||
// URL style of endpoint.
|
|
||||||
// Valid URL style endpoint is
|
|
||||||
// - Scheme field must contain "http" or "https"
|
|
||||||
// - All field should be empty except Host and Path.
|
|
||||||
if !((url.scheme() == "http" || url.scheme() == "https")
|
|
||||||
&& url.username().is_empty()
|
|
||||||
&& url.fragment().is_none()
|
|
||||||
&& url.query().is_none())
|
|
||||||
{
|
|
||||||
return Err(Error::from_string("invalid URL endpoint format"));
|
|
||||||
}
|
|
||||||
|
|
||||||
let path = Path::new(url.path()).clean();
|
|
||||||
if is_empty_path(&path) {
|
|
||||||
return Err(Error::from_string("empty or root path is not supported in URL endpoint"));
|
|
||||||
}
|
|
||||||
|
|
||||||
if let Some(v) = path.to_str() {
|
|
||||||
url.set_path(v)
|
|
||||||
}
|
|
||||||
|
|
||||||
// On windows having a preceding SlashSeparator will cause problems, if the
|
|
||||||
// command line already has C:/<export-folder/ in it. Final resulting
|
|
||||||
// path on windows might become C:/C:/ this will cause problems
|
|
||||||
// of starting rustfs server properly in distributed mode on windows.
|
|
||||||
// As a special case make sure to trim the separator.
|
|
||||||
|
|
||||||
// NOTE: It is also perfectly fine for windows users to have a path
|
|
||||||
// without C:/ since at that point we treat it as relative path
|
|
||||||
// and obtain the full filesystem path as well. Providing C:/
|
|
||||||
// style is necessary to provide paths other than C:/,
|
|
||||||
// such as F:/, D:/ etc.
|
|
||||||
//
|
|
||||||
// Another additional benefit here is that this style also
|
|
||||||
// supports providing \\host\share support as well.
|
|
||||||
#[cfg(windows)]
|
|
||||||
{
|
|
||||||
let path = url.path().to_owned();
|
|
||||||
if Path::new(&path[1..]).is_absolute() {
|
|
||||||
url.set_path(&path[1..]);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
url
|
|
||||||
}
|
|
||||||
Ok(_) => {
|
|
||||||
// like d:/foo
|
|
||||||
is_local = true;
|
|
||||||
url_parse_from_file_path(value)?
|
|
||||||
}
|
|
||||||
Err(e) => match e {
|
|
||||||
ParseError::InvalidPort => {
|
|
||||||
return Err(Error::from_string("invalid URL endpoint format: port number must be between 1 to 65535"))
|
|
||||||
}
|
|
||||||
ParseError::EmptyHost => return Err(Error::from_string("invalid URL endpoint format: empty host name")),
|
|
||||||
ParseError::RelativeUrlWithoutBase => {
|
|
||||||
// like /foo
|
|
||||||
is_local = true;
|
|
||||||
url_parse_from_file_path(value)?
|
|
||||||
}
|
|
||||||
_ => return Err(Error::from_string(format!("invalid URL endpoint format: {}", e))),
|
|
||||||
},
|
|
||||||
};
|
|
||||||
|
|
||||||
Ok(Endpoint {
|
|
||||||
url,
|
|
||||||
is_local,
|
|
||||||
pool_idx: None,
|
|
||||||
set_idx: None,
|
|
||||||
disk_idx: None,
|
|
||||||
})
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl Endpoint {
|
|
||||||
/// returns type of endpoint.
|
|
||||||
pub fn get_type(&self) -> EndpointType {
|
|
||||||
if self.url.scheme() == "file" {
|
|
||||||
EndpointType::Path
|
|
||||||
} else {
|
|
||||||
EndpointType::Url
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/// sets a specific pool number to this node
|
|
||||||
pub fn set_pool_index(&mut self, idx: usize) {
|
|
||||||
self.pool_idx = Some(idx)
|
|
||||||
}
|
|
||||||
|
|
||||||
/// sets a specific set number to this node
|
|
||||||
pub fn set_set_index(&mut self, idx: usize) {
|
|
||||||
self.set_idx = Some(idx)
|
|
||||||
}
|
|
||||||
|
|
||||||
/// sets a specific disk number to this node
|
|
||||||
pub fn set_disk_index(&mut self, idx: usize) {
|
|
||||||
self.disk_idx = Some(idx)
|
|
||||||
}
|
|
||||||
|
|
||||||
/// resolves the host and updates if it is local or not.
|
|
||||||
fn update_is_local(&mut self, local_port: u16) -> Result<()> {
|
|
||||||
match (self.url.scheme(), self.url.host()) {
|
|
||||||
(v, Some(host)) if v != "file" => {
|
|
||||||
self.is_local = net::is_local_host(host, self.url.port().unwrap_or_default(), local_port)?;
|
|
||||||
}
|
|
||||||
_ => {}
|
|
||||||
}
|
|
||||||
|
|
||||||
Ok(())
|
|
||||||
}
|
|
||||||
|
|
||||||
/// returns the host to be used for grid connections.
|
|
||||||
fn grid_host(&self) -> String {
|
|
||||||
match (self.url.host(), self.url.port()) {
|
|
||||||
(Some(host), Some(port)) => format!("{}://{}:{}", self.url.scheme(), host, port),
|
|
||||||
(Some(host), None) => format!("{}://{}", self.url.scheme(), host),
|
|
||||||
_ => String::new(),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
fn host_port(&self) -> String {
|
|
||||||
match (self.url.host(), self.url.port()) {
|
|
||||||
(Some(host), Some(port)) => format!("{}:{}", host, port),
|
|
||||||
(Some(host), None) => format!("{}", host),
|
|
||||||
_ => String::new(),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/// list of same type of endpoint.
|
/// list of same type of endpoint.
|
||||||
#[derive(Debug, Default, Clone)]
|
#[derive(Debug, Default, Clone)]
|
||||||
pub struct Endpoints(Vec<Endpoint>);
|
pub struct Endpoints(Vec<Endpoint>);
|
||||||
@@ -241,11 +59,11 @@ impl From<Vec<Endpoint>> for Endpoints {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl TryFrom<&[String]> for Endpoints {
|
impl<T: AsRef<str>> TryFrom<&[T]> for Endpoints {
|
||||||
type Error = Error;
|
type Error = Error;
|
||||||
|
|
||||||
/// returns new endpoint list based on input args.
|
/// returns new endpoint list based on input args.
|
||||||
fn try_from(args: &[String]) -> Result<Self> {
|
fn try_from(args: &[T]) -> Result<Self> {
|
||||||
let mut endpoint_type = None;
|
let mut endpoint_type = None;
|
||||||
let mut schema = None;
|
let mut schema = None;
|
||||||
let mut endpoints = Vec::with_capacity(args.len());
|
let mut endpoints = Vec::with_capacity(args.len());
|
||||||
@@ -253,9 +71,9 @@ impl TryFrom<&[String]> for Endpoints {
|
|||||||
|
|
||||||
// Loop through args and adds to endpoint list.
|
// Loop through args and adds to endpoint list.
|
||||||
for (i, arg) in args.iter().enumerate() {
|
for (i, arg) in args.iter().enumerate() {
|
||||||
let endpoint = match Endpoint::try_from(arg.as_str()) {
|
let endpoint = match Endpoint::try_from(arg.as_ref()) {
|
||||||
Ok(ep) => ep,
|
Ok(ep) => ep,
|
||||||
Err(e) => return Err(Error::from_string(format!("'{}': {}", arg, e))),
|
Err(e) => return Err(Error::from_string(format!("'{}': {}", arg.as_ref(), e))),
|
||||||
};
|
};
|
||||||
|
|
||||||
// All endpoints have to be same type and scheme if applicable.
|
// All endpoints have to be same type and scheme if applicable.
|
||||||
@@ -282,8 +100,19 @@ impl TryFrom<&[String]> for Endpoints {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
impl Endpoints {
|
||||||
|
/// Converts `self` into its inner representation.
|
||||||
|
///
|
||||||
|
/// This method consumes the `self` object and returns its inner `Vec<Endpoint>`.
|
||||||
|
/// It is useful for when you need to take the endpoints out of their container
|
||||||
|
/// without needing a reference to the container itself.
|
||||||
|
pub fn into_inner(self) -> Vec<Endpoint> {
|
||||||
|
self.0
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/// a temporary type to holds the list of endpoints
|
/// a temporary type to holds the list of endpoints
|
||||||
pub struct PoolEndpointList {
|
struct PoolEndpointList {
|
||||||
inner: Vec<Endpoints>,
|
inner: Vec<Endpoints>,
|
||||||
setup_type: SetupType,
|
setup_type: SetupType,
|
||||||
}
|
}
|
||||||
@@ -303,7 +132,7 @@ impl AsMut<Vec<Endpoints>> for PoolEndpointList {
|
|||||||
impl PoolEndpointList {
|
impl PoolEndpointList {
|
||||||
/// creates a list of endpoints per pool, resolves their relevant
|
/// creates a list of endpoints per pool, resolves their relevant
|
||||||
/// hostnames and discovers those are local or remote.
|
/// hostnames and discovers those are local or remote.
|
||||||
pub fn create_pool_endpoints(server_addr: &str, disks_layout: &DisksLayout) -> Result<Self> {
|
fn create_pool_endpoints(server_addr: &str, disks_layout: &DisksLayout) -> Result<Self> {
|
||||||
if disks_layout.is_empty_layout() {
|
if disks_layout.is_empty_layout() {
|
||||||
return Err(Error::from_string("invalid number of endpoints"));
|
return Err(Error::from_string("invalid number of endpoints"));
|
||||||
}
|
}
|
||||||
@@ -331,10 +160,10 @@ impl PoolEndpointList {
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
let mut pool_endpoints = Vec::<Endpoints>::with_capacity(disks_layout.as_ref().len());
|
let mut pool_endpoints = Vec::<Endpoints>::with_capacity(disks_layout.pools.len());
|
||||||
for (pool_idx, pool) in disks_layout.as_ref().iter().enumerate() {
|
for (pool_idx, pool) in disks_layout.pools.iter().enumerate() {
|
||||||
let mut endpoints = Endpoints::default();
|
let mut endpoints = Endpoints::default();
|
||||||
for (set_idx, set_layout) in pool.as_ref().iter().enumerate() {
|
for (set_idx, set_layout) in pool.iter().enumerate() {
|
||||||
// Convert args to endpoints
|
// Convert args to endpoints
|
||||||
let mut eps = Endpoints::try_from(set_layout.as_slice())?;
|
let mut eps = Endpoints::try_from(set_layout.as_slice())?;
|
||||||
|
|
||||||
@@ -582,7 +411,7 @@ impl EndpointServerPools {
|
|||||||
/// validates and creates new endpoints from input args, supports
|
/// validates and creates new endpoints from input args, supports
|
||||||
/// both ellipses and without ellipses transparently.
|
/// both ellipses and without ellipses transparently.
|
||||||
pub fn create_server_endpoints(server_addr: &str, disks_layout: &DisksLayout) -> Result<(EndpointServerPools, SetupType)> {
|
pub fn create_server_endpoints(server_addr: &str, disks_layout: &DisksLayout) -> Result<(EndpointServerPools, SetupType)> {
|
||||||
if disks_layout.as_ref().is_empty() {
|
if disks_layout.pools.is_empty() {
|
||||||
return Err(Error::from_string("Invalid arguments specified"));
|
return Err(Error::from_string("Invalid arguments specified"));
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -590,17 +419,12 @@ impl EndpointServerPools {
|
|||||||
|
|
||||||
let mut ret: EndpointServerPools = Vec::with_capacity(pool_eps.as_ref().len()).into();
|
let mut ret: EndpointServerPools = Vec::with_capacity(pool_eps.as_ref().len()).into();
|
||||||
for (i, eps) in pool_eps.inner.into_iter().enumerate() {
|
for (i, eps) in pool_eps.inner.into_iter().enumerate() {
|
||||||
let layout = disks_layout.get_layout(i);
|
|
||||||
let set_count = layout.map_or(0, |v| v.count());
|
|
||||||
let drives_per_set = layout.map_or(0, |v| v.as_ref().first().map_or(0, |v| v.len()));
|
|
||||||
let cmd_line = layout.map_or(String::new(), |v| v.get_cmd_line().to_owned());
|
|
||||||
|
|
||||||
let ep = PoolEndpoints {
|
let ep = PoolEndpoints {
|
||||||
legacy: disks_layout.legacy,
|
legacy: disks_layout.legacy,
|
||||||
set_count,
|
set_count: disks_layout.get_set_count(i),
|
||||||
drives_per_set,
|
drives_per_set: disks_layout.get_drives_per_set(i),
|
||||||
endpoints: eps,
|
endpoints: eps,
|
||||||
cmd_line,
|
cmd_line: disks_layout.get_cmd_line(i),
|
||||||
platform: format!("OS: {} | Arch: {}", std::env::consts::OS, std::env::consts::ARCH),
|
platform: format!("OS: {} | Arch: {}", std::env::consts::OS, std::env::consts::ARCH),
|
||||||
};
|
};
|
||||||
|
|
||||||
@@ -667,200 +491,11 @@ impl EndpointServerPools {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// parse a file path into an URL.
|
|
||||||
fn url_parse_from_file_path(value: &str) -> Result<url::Url> {
|
|
||||||
// Only check if the arg is an ip address and ask for scheme since its absent.
|
|
||||||
// localhost, example.com, any FQDN cannot be disambiguated from a regular file path such as
|
|
||||||
// /mnt/export1. So we go ahead and start the rustfs server in FS modes in these cases.
|
|
||||||
let addr: Vec<&str> = value.splitn(2, '/').collect();
|
|
||||||
if net::is_socket_addr(addr[0]) {
|
|
||||||
return Err(Error::from_string("invalid URL endpoint format: missing scheme http or https"));
|
|
||||||
}
|
|
||||||
|
|
||||||
let file_path = match Path::new(value).absolutize() {
|
|
||||||
Ok(path) => path,
|
|
||||||
Err(err) => return Err(Error::from_string(format!("absolute path failed: {}", err))),
|
|
||||||
};
|
|
||||||
|
|
||||||
match Url::from_file_path(file_path) {
|
|
||||||
Ok(url) => Ok(url),
|
|
||||||
Err(_) => Err(Error::from_string("Convert a file path into an URL failed")),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
#[cfg(test)]
|
#[cfg(test)]
|
||||||
mod test {
|
mod test {
|
||||||
|
|
||||||
use super::*;
|
use super::*;
|
||||||
|
use std::path::Path;
|
||||||
#[test]
|
|
||||||
fn test_new_endpoint() {
|
|
||||||
#[derive(Default)]
|
|
||||||
struct TestCase<'a> {
|
|
||||||
arg: &'a str,
|
|
||||||
expected_endpoint: Option<Endpoint>,
|
|
||||||
expected_type: Option<EndpointType>,
|
|
||||||
expected_err: Option<Error>,
|
|
||||||
}
|
|
||||||
|
|
||||||
let u2 = url::Url::parse("https://example.org/path").unwrap();
|
|
||||||
let u4 = url::Url::parse("http://192.168.253.200/path").unwrap();
|
|
||||||
let u6 = url::Url::parse("http://server:/path").unwrap();
|
|
||||||
let root_slash_foo = url::Url::from_file_path("/foo").unwrap();
|
|
||||||
|
|
||||||
let test_cases = [
|
|
||||||
TestCase {
|
|
||||||
arg: "/foo",
|
|
||||||
expected_endpoint: Some(Endpoint {
|
|
||||||
url: root_slash_foo,
|
|
||||||
is_local: true,
|
|
||||||
pool_idx: None,
|
|
||||||
set_idx: None,
|
|
||||||
disk_idx: None,
|
|
||||||
}),
|
|
||||||
expected_type: Some(EndpointType::Path),
|
|
||||||
expected_err: None,
|
|
||||||
},
|
|
||||||
TestCase {
|
|
||||||
arg: "https://example.org/path",
|
|
||||||
expected_endpoint: Some(Endpoint {
|
|
||||||
url: u2,
|
|
||||||
is_local: false,
|
|
||||||
pool_idx: None,
|
|
||||||
set_idx: None,
|
|
||||||
disk_idx: None,
|
|
||||||
}),
|
|
||||||
expected_type: Some(EndpointType::Url),
|
|
||||||
expected_err: None,
|
|
||||||
},
|
|
||||||
TestCase {
|
|
||||||
arg: "http://192.168.253.200/path",
|
|
||||||
expected_endpoint: Some(Endpoint {
|
|
||||||
url: u4,
|
|
||||||
is_local: false,
|
|
||||||
pool_idx: None,
|
|
||||||
set_idx: None,
|
|
||||||
disk_idx: None,
|
|
||||||
}),
|
|
||||||
expected_type: Some(EndpointType::Url),
|
|
||||||
expected_err: None,
|
|
||||||
},
|
|
||||||
TestCase {
|
|
||||||
arg: "",
|
|
||||||
expected_endpoint: None,
|
|
||||||
expected_type: None,
|
|
||||||
expected_err: Some(Error::from_string("empty or root endpoint is not supported")),
|
|
||||||
},
|
|
||||||
TestCase {
|
|
||||||
arg: "/",
|
|
||||||
expected_endpoint: None,
|
|
||||||
expected_type: None,
|
|
||||||
expected_err: Some(Error::from_string("empty or root endpoint is not supported")),
|
|
||||||
},
|
|
||||||
TestCase {
|
|
||||||
arg: "\\",
|
|
||||||
expected_endpoint: None,
|
|
||||||
expected_type: None,
|
|
||||||
expected_err: Some(Error::from_string("empty or root endpoint is not supported")),
|
|
||||||
},
|
|
||||||
TestCase {
|
|
||||||
arg: "c://foo",
|
|
||||||
expected_endpoint: None,
|
|
||||||
expected_type: None,
|
|
||||||
expected_err: Some(Error::from_string("invalid URL endpoint format")),
|
|
||||||
},
|
|
||||||
TestCase {
|
|
||||||
arg: "ftp://foo",
|
|
||||||
expected_endpoint: None,
|
|
||||||
expected_type: None,
|
|
||||||
expected_err: Some(Error::from_string("invalid URL endpoint format")),
|
|
||||||
},
|
|
||||||
TestCase {
|
|
||||||
arg: "http://server/path?location",
|
|
||||||
expected_endpoint: None,
|
|
||||||
expected_type: None,
|
|
||||||
expected_err: Some(Error::from_string("invalid URL endpoint format")),
|
|
||||||
},
|
|
||||||
TestCase {
|
|
||||||
arg: "http://:/path",
|
|
||||||
expected_endpoint: None,
|
|
||||||
expected_type: None,
|
|
||||||
expected_err: Some(Error::from_string("invalid URL endpoint format: empty host name")),
|
|
||||||
},
|
|
||||||
TestCase {
|
|
||||||
arg: "http://:8080/path",
|
|
||||||
expected_endpoint: None,
|
|
||||||
expected_type: None,
|
|
||||||
expected_err: Some(Error::from_string("invalid URL endpoint format: empty host name")),
|
|
||||||
},
|
|
||||||
TestCase {
|
|
||||||
arg: "http://server:/path",
|
|
||||||
expected_endpoint: Some(Endpoint {
|
|
||||||
url: u6,
|
|
||||||
is_local: false,
|
|
||||||
pool_idx: None,
|
|
||||||
set_idx: None,
|
|
||||||
disk_idx: None,
|
|
||||||
}),
|
|
||||||
expected_type: Some(EndpointType::Url),
|
|
||||||
expected_err: None,
|
|
||||||
},
|
|
||||||
TestCase {
|
|
||||||
arg: "https://93.184.216.34:808080/path",
|
|
||||||
expected_endpoint: None,
|
|
||||||
expected_type: None,
|
|
||||||
expected_err: Some(Error::from_string("invalid URL endpoint format: port number must be between 1 to 65535")),
|
|
||||||
},
|
|
||||||
TestCase {
|
|
||||||
arg: "http://server:8080//",
|
|
||||||
expected_endpoint: None,
|
|
||||||
expected_type: None,
|
|
||||||
expected_err: Some(Error::from_string("empty or root path is not supported in URL endpoint")),
|
|
||||||
},
|
|
||||||
TestCase {
|
|
||||||
arg: "http://server:8080/",
|
|
||||||
expected_endpoint: None,
|
|
||||||
expected_type: None,
|
|
||||||
expected_err: Some(Error::from_string("empty or root path is not supported in URL endpoint")),
|
|
||||||
},
|
|
||||||
TestCase {
|
|
||||||
arg: "192.168.1.210:9000",
|
|
||||||
expected_endpoint: None,
|
|
||||||
expected_type: None,
|
|
||||||
expected_err: Some(Error::from_string("invalid URL endpoint format: missing scheme http or https")),
|
|
||||||
},
|
|
||||||
];
|
|
||||||
|
|
||||||
for test_case in test_cases {
|
|
||||||
let ret = Endpoint::try_from(test_case.arg);
|
|
||||||
if test_case.expected_err.is_none() && ret.is_err() {
|
|
||||||
panic!("{}: error: expected = <nil>, got = {:?}", test_case.arg, ret);
|
|
||||||
}
|
|
||||||
if test_case.expected_err.is_some() && ret.is_ok() {
|
|
||||||
panic!("{}: error: expected = {:?}, got = <nil>", test_case.arg, test_case.expected_err);
|
|
||||||
}
|
|
||||||
match (test_case.expected_err, ret) {
|
|
||||||
(None, Err(e)) => panic!("{}: error: expected = <nil>, got = {}", test_case.arg, e),
|
|
||||||
(None, Ok(mut ep)) => {
|
|
||||||
let _ = ep.update_is_local(9000);
|
|
||||||
if test_case.expected_type != Some(ep.get_type()) {
|
|
||||||
panic!(
|
|
||||||
"{}: type: expected = {:?}, got = {:?}",
|
|
||||||
test_case.arg,
|
|
||||||
test_case.expected_type,
|
|
||||||
ep.get_type()
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
assert_eq!(test_case.expected_endpoint, Some(ep), "{}: endpoint", test_case.arg);
|
|
||||||
}
|
|
||||||
(Some(e), Ok(_)) => panic!("{}: error: expected = {}, got = <nil>", test_case.arg, e),
|
|
||||||
(Some(e), Err(e2)) => {
|
|
||||||
assert_eq!(e.to_string(), e2.to_string(), "{}: error: expected = {}, got = {}", test_case.arg, e, e2)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn test_new_endpoints() {
|
fn test_new_endpoints() {
|
||||||
+1
-1
@@ -2,7 +2,7 @@ mod bucket_meta;
|
|||||||
mod chunk_stream;
|
mod chunk_stream;
|
||||||
pub mod disk;
|
pub mod disk;
|
||||||
mod disks_layout;
|
mod disks_layout;
|
||||||
mod endpoint;
|
mod endpoints;
|
||||||
mod erasure;
|
mod erasure;
|
||||||
pub mod error;
|
pub mod error;
|
||||||
mod file_meta;
|
mod file_meta;
|
||||||
|
|||||||
+1
-1
@@ -6,7 +6,7 @@ use tracing::warn;
|
|||||||
use crate::{
|
use crate::{
|
||||||
disk::VolumeInfo,
|
disk::VolumeInfo,
|
||||||
disk::{error::DiskError, DiskStore},
|
disk::{error::DiskError, DiskStore},
|
||||||
endpoint::{EndpointServerPools, Node},
|
endpoints::{EndpointServerPools, Node},
|
||||||
error::{Error, Result},
|
error::{Error, Result},
|
||||||
store_api::{BucketInfo, BucketOptions, MakeBucketOptions},
|
store_api::{BucketInfo, BucketOptions, MakeBucketOptions},
|
||||||
};
|
};
|
||||||
|
|||||||
+1
-1
@@ -4,7 +4,7 @@ use uuid::Uuid;
|
|||||||
use crate::{
|
use crate::{
|
||||||
disk::format::{DistributionAlgoVersion, FormatV3},
|
disk::format::{DistributionAlgoVersion, FormatV3},
|
||||||
disk::DiskStore,
|
disk::DiskStore,
|
||||||
endpoint::PoolEndpoints,
|
endpoints::PoolEndpoints,
|
||||||
error::Result,
|
error::Result,
|
||||||
set_disk::SetDisks,
|
set_disk::SetDisks,
|
||||||
store_api::{
|
store_api::{
|
||||||
|
|||||||
@@ -3,7 +3,7 @@ use crate::{
|
|||||||
disk::error::DiskError,
|
disk::error::DiskError,
|
||||||
disk::{self, DiskOption, DiskStore, RUSTFS_META_BUCKET},
|
disk::{self, DiskOption, DiskStore, RUSTFS_META_BUCKET},
|
||||||
disks_layout::DisksLayout,
|
disks_layout::DisksLayout,
|
||||||
endpoint::EndpointServerPools,
|
endpoints::EndpointServerPools,
|
||||||
error::{Error, Result},
|
error::{Error, Result},
|
||||||
peer::{PeerS3Client, S3PeerSys},
|
peer::{PeerS3Client, S3PeerSys},
|
||||||
sets::Sets,
|
sets::Sets,
|
||||||
|
|||||||
Reference in New Issue
Block a user