Skip to content

Commit

Permalink
More error adjustments
Browse files Browse the repository at this point in the history
  • Loading branch information
Techassi committed Oct 19, 2023
1 parent b11c3f4 commit 37751bb
Show file tree
Hide file tree
Showing 8 changed files with 127 additions and 64 deletions.
8 changes: 4 additions & 4 deletions rust/stackable-cockpit/src/platform/credentials.rs
Original file line number Diff line number Diff line change
Expand Up @@ -10,8 +10,8 @@ pub type Result<T, E = CredentialsError> = std::result::Result<T, E>;

#[derive(Debug, Snafu)]
pub enum CredentialsError {
#[snafu(display("kubernetes error"))]
KubeError { source: KubeClientError },
#[snafu(display("failed to fetch data from kubernetes api"))]
KubeClientFetchError { source: KubeClientError },

#[snafu(display("no credentials secret found"))]
NoSecret,
Expand Down Expand Up @@ -64,7 +64,7 @@ pub async fn get_credentials(
"adminUser.password",
)
.await
.context(KubeSnafu)?
.context(KubeClientFetchSnafu)?
}
"nifi" => {
let secret_name = stacklet.data["spec"]["clusterConfig"]["credentialsSecret"]
Expand All @@ -79,7 +79,7 @@ pub async fn get_credentials(
"password",
)
.await
.context(KubeSnafu)?
.context(KubeClientFetchSnafu)?
}
_ => return Ok(None),
};
Expand Down
4 changes: 2 additions & 2 deletions rust/stackable-cockpit/src/platform/demo/spec.rs
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
use serde::{Deserialize, Serialize};
use snafu::{ResultExt, Snafu};
use snafu::{OptionExt, ResultExt, Snafu};
use tracing::{debug, instrument, warn};

#[cfg(feature = "openapi")]
Expand Down Expand Up @@ -125,7 +125,7 @@ impl DemoSpecV2 {
skip_release: bool,
) -> Result<(), DemoError> {
// Get the stack spec based on the name defined in the demo spec
let stack_spec = stack_list.get(&self.stack).ok_or(DemoError::NoSuchStack {
let stack_spec = stack_list.get(&self.stack).context(NoSuchStackSnafu {
name: self.stack.clone(),
})?;

Expand Down
105 changes: 71 additions & 34 deletions rust/stackable-cockpit/src/platform/stack/spec.rs
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
use std::collections::HashMap;

use serde::{Deserialize, Serialize};
use snafu::{ResultExt, Snafu};
use snafu::{OptionExt, ResultExt, Snafu};
use tracing::{debug, info, instrument, log::warn};

#[cfg(feature = "openapi")]
Expand Down Expand Up @@ -32,44 +32,67 @@ pub type RawStackParameterParseError = RawParameterParseError;
pub type RawStackParameter = RawParameter;
pub type StackParameter = Parameter;

/// This error indicates that the stack, the stack manifests or the demo
/// manifests failed to install.
#[derive(Debug, Snafu)]
pub enum StackError {
/// This error indicates that parsing a string into stack / demo parameters
/// failed.
#[snafu(display("parameter parse error: {source}"))]
#[snafu(display("failed to parse demo / stack parameters"))]
ParameterError { source: IntoParametersError },

/// This error indicates that the requested stack doesn't exist in the
/// loaded list of stacks.
#[snafu(display("no such stack"))]
NoSuchStack,
/// This error indicates that the requested release doesn't exist in the
/// loaded list of releases.
#[snafu(display("no release named {name}"))]
NoSuchRelease { name: String },

/// This error indicates that the release failed to install.
#[snafu(display("release install error: {source}"))]
#[snafu(display("failed to install release"))]
ReleaseInstallError { source: ReleaseInstallError },

/// This error indicates the Helm wrapper encountered an error.
#[snafu(display("Helm error: {source}"))]
HelmError { source: HelmError },
/// This error indicates that the Helm wrapper failed to add the Helm
/// repository.
#[snafu(display("failed to add Helm repository {repo_name}"))]
HelmAddRepositoryError {
source: HelmError,
repo_name: String,
},

/// This error indicates that the Hlm wrapper failed to install the Helm
/// release.
#[snafu(display("failed to install Helm release {release_name}"))]
HelmInstallReleaseError {
release_name: String,
source: HelmError,
},

/// This error indicates that the creation of a kube client failed.
#[snafu(display("failed to create kubernetes client"))]
KubeClientCreateError { source: KubeClientError },

#[snafu(display("kube error: {source}"))]
KubeError { source: KubeClientError },
/// This error indicates that the kube client failed to deloy manifests.
#[snafu(display("failed to deploy manifests using the kube client"))]
ManifestDeployError { source: KubeClientError },

/// This error indicates a YAML error occurred.
#[snafu(display("yaml error: {source}"))]
YamlError { source: serde_yaml::Error },
/// This error indicates that Helm chart options could not be serialized
/// into YAML.
#[snafu(display("failed to serialize Helm chart options"))]
SerializeOptionsError { source: serde_yaml::Error },

#[snafu(display("stack resource requests error"), context(false))]
StackResourceRequestsError { source: ResourceRequestsError },

#[snafu(display("path or url parse error"))]
PathOrUrlParseError { source: PathOrUrlParseError },
/// This error indicates that parsing a string into a path or URL failed.
#[snafu(display("failed to parse '{path_or_url}' as path/url"))]
PathOrUrlParseError {
source: PathOrUrlParseError,
path_or_url: String,
},

#[snafu(display("transfer error"))]
/// This error indicates that receiving remote content failed.
#[snafu(display("failed to receive remote content"))]
TransferError { source: FileTransferError },

/// This error indicates that the stack doesn't support being installed in
/// the provided namespace.
#[snafu(display("cannot install stack in namespace '{requested}', only '{}' supported", supported.join(", ")))]
UnsupportedNamespace {
requested: String,
Expand Down Expand Up @@ -165,7 +188,9 @@ impl StackSpecV2 {
// Get the release by name
let release = release_list
.get(&self.release)
.ok_or(StackError::NoSuchStack)?;
.context(NoSuchReleaseSnafu {
name: self.release.clone(),
})?;

// Install the release
release
Expand Down Expand Up @@ -235,7 +260,10 @@ impl StackSpecV2 {
debug!("Installing manifest from Helm chart {}", helm_file);

// Read Helm chart YAML and apply templating
let helm_file = helm_file.into_path_or_url().context(PathOrUrlParseSnafu)?;
let helm_file = helm_file.into_path_or_url().context(PathOrUrlParseSnafu {
path_or_url: helm_file.clone(),
})?;

let helm_chart: HelmChart = transfer_client
.get(&helm_file, &Template::new(parameters).then(Yaml::new()))
.await
Expand All @@ -246,12 +274,15 @@ impl StackSpecV2 {
helm_chart.name, helm_chart.version
);

helm::add_repo(&helm_chart.repo.name, &helm_chart.repo.url)
.context(HelmSnafu)?;
helm::add_repo(&helm_chart.repo.name, &helm_chart.repo.url).context(
HelmAddRepositorySnafu {
repo_name: helm_chart.repo.name.clone(),
},
)?;

// Serialize chart options to string
let values_yaml =
serde_yaml::to_string(&helm_chart.options).context(YamlSnafu)?;
let values_yaml = serde_yaml::to_string(&helm_chart.options)
.context(SerializeOptionsSnafu)?;

// Install the Helm chart using the Helm wrapper
helm::install_release_from_repo(
Expand All @@ -266,26 +297,32 @@ impl StackSpecV2 {
product_namespace,
false,
)
.context(HelmSnafu)?;
.context(HelmInstallReleaseSnafu {
release_name: helm_chart.release_name,
})?;
}
ManifestSpec::PlainYaml(path_or_url) => {
debug!("Installing YAML manifest from {}", path_or_url);
ManifestSpec::PlainYaml(manifest_file) => {
debug!("Installing YAML manifest from {}", manifest_file);

// Read YAML manifest and apply templating
let path_or_url = path_or_url
.into_path_or_url()
.context(PathOrUrlParseSnafu)?;
let path_or_url =
manifest_file
.into_path_or_url()
.context(PathOrUrlParseSnafu {
path_or_url: manifest_file.clone(),
})?;

let manifests = transfer_client
.get(&path_or_url, &Template::new(parameters))
.await
.context(TransferSnafu)?;

let kube_client = KubeClient::new().await.context(KubeSnafu)?;
let kube_client = KubeClient::new().await.context(KubeClientCreateSnafu)?;

kube_client
.deploy_manifests(&manifests, product_namespace)
.await
.context(KubeSnafu)?
.context(ManifestDeploySnafu)?
}
}
}
Expand Down
10 changes: 7 additions & 3 deletions rust/stackable-cockpit/src/platform/stacklet/grafana.rs
Original file line number Diff line number Diff line change
@@ -1,9 +1,10 @@
use kube::{api::ListParams, ResourceExt};
use snafu::ResultExt;

use crate::{
platform::{
service::get_service_endpoint_urls,
stacklet::{Stacklet, StackletError},
stacklet::{KubeClientFetchSnafu, ServiceSnafu, Stacklet, StackletError},
},
utils::k8s::{KubeClient, ListParamsExt, ProductLabel},
};
Expand All @@ -15,13 +16,16 @@ pub(super) async fn list(
let mut stacklets = Vec::new();

let params = ListParams::from_product("grafana", None, ProductLabel::Name);
let services = kube_client.list_services(namespace, &params).await?;
let services = kube_client
.list_services(namespace, &params)
.await
.context(KubeClientFetchSnafu)?;

for service in services {
let service_name = service.name_any();
let endpoints = get_service_endpoint_urls(kube_client, &service, &service_name)
.await
.map_err(|err| StackletError::ServiceError { source: err })?;
.context(ServiceSnafu)?;

stacklets.push(Stacklet {
conditions: Vec::new(),
Expand Down
11 changes: 8 additions & 3 deletions rust/stackable-cockpit/src/platform/stacklet/minio.rs
Original file line number Diff line number Diff line change
@@ -1,9 +1,10 @@
use kube::{api::ListParams, ResourceExt};
use snafu::ResultExt;

use crate::{
platform::{
service::get_service_endpoint_urls,
stacklet::{Stacklet, StackletError},
stacklet::{KubeClientFetchSnafu, ServiceSnafu, Stacklet, StackletError},
},
utils::k8s::KubeClient,
};
Expand All @@ -16,7 +17,11 @@ pub(super) async fn list(

// The helm-chart uses `app` instead of `app.kubernetes.io/app`, so we can't use `ListParams::from_product` here
let params = ListParams::default().labels("app=minio,app.kubernetes.io/managed-by=Helm");
let services = kube_client.list_services(namespace, &params).await?;
let services = kube_client
.list_services(namespace, &params)
.await
.context(KubeClientFetchSnafu)?;

let console_services = services
.iter()
.filter(|s| s.name_unchecked().ends_with("-console"));
Expand All @@ -25,7 +30,7 @@ pub(super) async fn list(
let service_name = service.name_any();
let endpoints = get_service_endpoint_urls(kube_client, service, &service_name)
.await
.map_err(|err| StackletError::ServiceError { source: err })?;
.context(ServiceSnafu)?;

stacklets.push(Stacklet {
product: "minio".to_string(),
Expand Down
33 changes: 21 additions & 12 deletions rust/stackable-cockpit/src/platform/stacklet/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -48,14 +48,17 @@ pub struct Stacklet {

#[derive(Debug, Snafu)]
pub enum StackletError {
#[snafu(display("kubernetes error"), context(false))]
KubeError { source: KubeClientError },
#[snafu(display("failed to create kubernetes client"))]
KubeClientCreateError { source: KubeClientError },

#[snafu(display("failed to fetch data from the kubernetes api"))]
KubeClientFetchError { source: KubeClientError },

#[snafu(display("no namespace set for custom resource '{crd_name}'"))]
CustomCrdNamespaceError { crd_name: String },

#[snafu(display("JSON error"))]
JsonError { source: serde_json::Error },
#[snafu(display("failed to deserialize cluster conditions from json"))]
DeserializeConditionsError { source: serde_json::Error },

#[snafu(display("service error"))]
ServiceError { source: ServiceError },
Expand All @@ -66,7 +69,7 @@ pub enum StackletError {
/// in the specified namespace are returned. The `options` allow further
/// customization of the returned information.
pub async fn list_stacklets(namespace: Option<&str>) -> Result<Vec<Stacklet>, StackletError> {
let kube_client = KubeClient::new().await?;
let kube_client = KubeClient::new().await.context(KubeClientCreateSnafu)?;

let mut stacklets = list_stackable_stacklets(&kube_client, namespace).await?;
stacklets.extend(grafana::list(&kube_client, namespace).await?);
Expand All @@ -82,12 +85,13 @@ pub async fn get_credentials_for_product(
object_name: &str,
product_name: &str,
) -> Result<Option<Credentials>, StackletError> {
let kube_client = KubeClient::new().await?;
let kube_client = KubeClient::new().await.context(KubeClientCreateSnafu)?;

let product_gvk = gvk_from_product_name(product_name);
let product_cluster = match kube_client
.get_namespaced_object(namespace, object_name, &product_gvk)
.await?
.await
.context(KubeClientFetchSnafu)?
{
Some(obj) => obj,
None => {
Expand All @@ -101,7 +105,9 @@ pub async fn get_credentials_for_product(
let credentials = match get_credentials(&kube_client, product_name, &product_cluster).await {
Ok(credentials) => credentials,
Err(CredentialsError::NoSecret) => None,
Err(CredentialsError::KubeError { source }) => return Err(source.into()),
Err(CredentialsError::KubeClientFetchError { source }) => {
return Err(StackletError::KubeClientFetchError { source })
}
};

Ok(credentials)
Expand All @@ -115,7 +121,11 @@ async fn list_stackable_stacklets(
let mut stacklets = Vec::new();

for (product_name, product_gvk) in product_list {
let objects = match kube_client.list_objects(&product_gvk, namespace).await? {
let objects = match kube_client
.list_objects(&product_gvk, namespace)
.await
.context(KubeClientFetchSnafu)?
{
Some(obj) => obj,
None => {
info!(
Expand All @@ -128,9 +138,8 @@ async fn list_stackable_stacklets(
for object in objects {
let conditions: Vec<ClusterCondition> = match object.data.pointer("/status/conditions")
{
Some(conditions) => {
serde_json::from_value(conditions.clone()).context(JsonSnafu)?
}
Some(conditions) => serde_json::from_value(conditions.clone())
.context(DeserializeConditionsSnafu)?,
None => vec![],
};

Expand Down
Loading

0 comments on commit 37751bb

Please sign in to comment.