Skip to content
This repository was archived by the owner on Dec 21, 2021. It is now read-only.

Skip invalid or unreachable repositories when searching for packages #229

Merged
merged 1 commit into from
Jul 22, 2021
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 5 additions & 0 deletions CHANGELOG.adoc
Original file line number Diff line number Diff line change
Expand Up @@ -3,10 +3,15 @@
== 0.5.0 - unreleased

:224: https://github.com/stackabletech/agent/pull/224[#224]
:229: https://github.com/stackabletech/agent/pull/229[#229]

=== Added
* `hostIP` and `podIP` added to the pod status ({224}).

=== Fixed
* Invalid or unreachable repositories are skipped when searching for
packages ({229}).

== 0.4.0 - 2021-06-23

:188: https://github.com/stackabletech/agent/pull/188[#188]
Expand Down
114 changes: 79 additions & 35 deletions src/provider/repository/mod.rs
Original file line number Diff line number Diff line change
@@ -1,52 +1,96 @@
use std::convert::TryFrom;
//! Functions to deal with Stackable repositories

use kube::api::ListParams;
use kube::api::{ListParams, ObjectList};
use kube::{Api, Client};
use log::{debug, trace};
use log::{debug, info, warn};
use std::convert::TryFrom;

use crate::provider::error::StackableError;
use crate::provider::repository::package::Package;
use crate::provider::repository::repository_spec::Repository;
use crate::provider::repository::stackablerepository::StackableRepoProvider;
use package::Package;
use repository_spec::Repository;
use stackablerepository::StackableRepoProvider;

pub mod package;
pub mod repository_spec;
pub mod stackablerepository;

/// Searches for the given package in all registered repositories.
///
/// The available repositories are retrieved from the API server and if
/// the given package is provided by one of them then
/// `Ok(Some(repository))` else `Ok(None)` is returned.
///
/// If the repositories cannot be retrieved then `Err(error)` is
/// returned.
///
/// The repositories are sorted by their name to provide a deterministic
/// behavior especially for tests.
pub async fn find_repository(
client: Client,
package: &Package,
repository_reference: Option<String>,
) -> Result<Option<StackableRepoProvider>, StackableError> {
let repositories: Api<Repository> = Api::namespaced(client.clone(), "default");
if let Some(repository_name) = repository_reference {
// A repository name was provided, just check that exact repository for the package
let repo = repositories.get(&repository_name).await?;
let mut repo = StackableRepoProvider::try_from(&repo)?;
if repo.provides_package(package.clone()).await? {
return Ok(Some(repo));
}
return Ok(None);
let repositories = retrieve_repositories(client).await?;

let mut repo_providers = repositories
.iter()
.filter_map(convert_to_repo_provider)
.collect::<Vec<_>>();

repo_providers.sort_unstable_by_key(|repo_provider| repo_provider.name.to_owned());

let maybe_repo_provider = choose_repo_provider(&mut repo_providers, package).await;

if let Some(repo_provider) = &maybe_repo_provider {
debug!(
"Package [{}] found in repository [{}]",
&package, &repo_provider
);
} else {
// No repository name was provided, retrieve all repositories from the orchestrator/apiserver
// and check which one provides the package
let list_params = ListParams::default();
let repos = repositories.list(&list_params).await?;
for repository in repos.iter() {
debug!("got repo definition: [{:?}]", repository);
// Convert repository to object implementing our trait
let mut repo = StackableRepoProvider::try_from(repository)?;
trace!("converted to stackable repo: {:?}", repository);
if repo.provides_package(package.clone()).await? {
debug!("Found package [{}] in repository [{}]", &package, repo);
return Ok(Some(repo));
} else {
debug!(
"Package [{}] not provided by repository [{}]",
&package, repo
);
}
let repository_names = repo_providers
.iter()
.map(|repo_provider| repo_provider.name.as_str())
.collect::<Vec<_>>();
info!(
"Package [{}] not found in the following repositories: {:?}",
package, repository_names
);
}

Ok(maybe_repo_provider)
}

/// Retrieves all Stackable repositories in the default namespace from
/// the API server.
async fn retrieve_repositories(client: Client) -> Result<ObjectList<Repository>, StackableError> {
let api: Api<Repository> = Api::namespaced(client, "default");
let repositories = api.list(&ListParams::default()).await?;
Ok(repositories)
}

/// Converts the given Stackable repository into a repository provider.
///
/// If this fails then a warning is emitted and `None` is returned.
fn convert_to_repo_provider(repository: &Repository) -> Option<StackableRepoProvider> {
let result = StackableRepoProvider::try_from(repository);

if let Err(error) = &result {
warn!("Invalid repository definition: {}", error);
}

result.ok()
}

/// Retrieves the provided packages for the given repository providers
/// and returns the first provider which provides the given package or
/// `None` if none provides it.
async fn choose_repo_provider(
repo_providers: &mut [StackableRepoProvider],
package: &Package,
) -> Option<StackableRepoProvider> {
for repo_provider in repo_providers {
if let Ok(true) = repo_provider.provides_package(package.to_owned()).await {
return Some(repo_provider.to_owned());
}
}
Ok(None)
None
}
2 changes: 1 addition & 1 deletion src/provider/states/pod/downloading.rs
Original file line number Diff line number Diff line change
Expand Up @@ -60,7 +60,7 @@ impl State<PodState> for Downloading {
},
);
}
let repo = find_repository(client, &package, None).await;
let repo = find_repository(client, &package).await;
return match repo {
Ok(Some(mut repo)) => {
// We found a repository providing the package, proceed with download
Expand Down