Skip to content

Commit

Permalink
feat(upgrade): add upported upgrade version file upgrade
Browse files Browse the repository at this point in the history
Signed-off-by: sinhaashish <[email protected]>
  • Loading branch information
sinhaashish committed May 12, 2023
1 parent ae64967 commit a56391a
Show file tree
Hide file tree
Showing 16 changed files with 392 additions and 235 deletions.
2 changes: 2 additions & 0 deletions Cargo.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

5 changes: 3 additions & 2 deletions k8s/plugin/src/main.rs
Original file line number Diff line number Diff line change
Expand Up @@ -157,10 +157,11 @@ async fn execute(cli_args: CliArgs) {
resources.skip_single_replica_volume_validation,
resources.skip_replica_rebuild,
resources.skip_cordoned_node_validation,
resources.skip_upgrade_path_validation_for_unsupported_version,
)
.await
.map_err(|_e| {
std::process::exit(1);
.map_err(|error| {
std::process::exit(error.into());
});

if resources.dry_run {
Expand Down
7 changes: 0 additions & 7 deletions k8s/upgrade-job/src/common/constants.rs
Original file line number Diff line number Diff line change
Expand Up @@ -20,13 +20,6 @@ pub(crate) const CHART_VERSION_LABEL_KEY: &str = "openebs.io/version";
/// This is the label set on a storage API Node resource when a 'Node Drain' is issued.
pub(crate) const DRAIN_FOR_UPGRADE: &str = "mayastor-upgrade";

/// This is the allowed upgrade to-version/to-version-range for the Core chart.
pub(crate) const TO_CORE_SEMVER: &str = ">=2.1.0-rc.0, <=2.1.0";

/// This version range will be only allowed to upgrade to TO_CORE_SEMVER above. This range applies
/// to the Core chart.
pub(crate) const FROM_CORE_SEMVER: &str = ">=2.0.0, <=2.0.1";

/// This is the allowed upgrade to-version/to-version-range for the Umbrella chart.
pub(crate) const TO_UMBRELLA_SEMVER: &str = "3.6.0";

Expand Down
41 changes: 33 additions & 8 deletions k8s/upgrade-job/src/helm/upgrade.rs
Original file line number Diff line number Diff line change
Expand Up @@ -32,6 +32,8 @@ pub(crate) struct HelmUpgradeBuilder {
namespace: Option<String>,
umbrella_chart_dir: Option<PathBuf>,
core_chart_dir: Option<PathBuf>,
upgrade_path_file: PathBuf,
skip_upgrade_path_validation: bool,
}

impl HelmUpgradeBuilder {
Expand Down Expand Up @@ -69,6 +71,23 @@ impl HelmUpgradeBuilder {
self
}

/// This is a builder option to set the path for the unsupported version yaml.
#[must_use]
pub(crate) fn with_upgrade_path_file(mut self, file: PathBuf) -> Self {
self.upgrade_path_file = file;
self
}

/// This sets the flag to skip upgrade path validation.
#[must_use]
pub(crate) fn with_skip_upgrade_path_validation(
mut self,
skip_upgrade_path_validation: bool,
) -> Self {
self.skip_upgrade_path_validation = skip_upgrade_path_validation;
self
}

/// This adds buiilds the HelmUpgrade object.
pub(crate) fn build(self) -> Result<HelmUpgrade> {
ensure!(
Expand Down Expand Up @@ -133,19 +152,25 @@ impl HelmUpgradeBuilder {
return NotAKnownHelmChart { chart_name: chart }.fail();
}

// Validating upgrade path.
let mut chart_yaml_path = chart_dir.clone();
chart_yaml_path.push("Chart.yaml");
let to_version = upgrade::path::version_from_chart_yaml_file(chart_yaml_path)?;
let from_version = upgrade::path::version_from_release_chart(chart)?;
let upgrade_path_is_valid =
upgrade::path::is_valid(chart_variant.clone(), &from_version, &to_version)?;
let already_upgraded = to_version.eq(&from_version);
let invalid_upgrades = upgrade::path::invalid_upgrade_path(self.upgrade_path_file)?;

ensure!(
upgrade_path_is_valid || already_upgraded,
InvalidUpgradePath
);
// Check for already upgraded
let already_upgraded = to_version.eq(&from_version);
ensure!(!already_upgraded, InvalidUpgradePath);

if !self.skip_upgrade_path_validation {
let upgrade_path_is_valid = upgrade::path::is_valid(
chart_variant.clone(),
&from_version,
&to_version,
invalid_upgrades,
)?;
ensure!(upgrade_path_is_valid, InvalidUpgradePath);
}

// Generate args to pass to the `helm upgrade` command.
let mut values_yaml_path = chart_dir.clone();
Expand Down
22 changes: 22 additions & 0 deletions k8s/upgrade-job/src/opts.rs
Original file line number Diff line number Diff line change
Expand Up @@ -41,10 +41,22 @@ pub(crate) struct CliArgs {
)]
core_chart_dir: Option<PathBuf>,

/// This is the path to upgrade exception file.
#[arg(
long,
env = "UPGRADE_EXCEPTION_FILE_PATH",
default_value = "/k8s/upgrade/config/unsupported_versions.yaml"
)]
upgrade_exception_file: PathBuf,

/// If not set, this skips the Kubernetes Pod restarts for the io-engine DaemonSet.
#[arg(long, default_value_t = false)]
skip_data_plane_restart: bool,

/// If set then this skips the upgrade path validation.
#[arg(long, default_value_t = false)]
skip_upgrade_path_validation: bool,

/// The name of the Kubernetes Job Pod. The Job object will be used to post upgrade event.
#[arg(env = "POD_NAME")]
pod_name: String,
Expand Down Expand Up @@ -77,12 +89,22 @@ impl CliArgs {
self.core_chart_dir.clone()
}

/// This returns the path to find unsupported upgrade version yaml file.
pub(crate) fn upgrade_exception_file(&self) -> PathBuf {
self.upgrade_exception_file.clone()
}

/// This is a predicate to decide if <release-name>-io-engine Kubernetes DaemonSet Pods should
/// be restarted as a part of the data-plane upgrade.
pub(crate) fn skip_data_plane_restart(&self) -> bool {
self.skip_data_plane_restart
}

/// This decides to skip upgrade path validation or not.
pub(crate) fn skip_upgrade_path_validation(&self) -> bool {
self.skip_upgrade_path_validation
}

/// This returns the name of the Kubernetes Pod where this binary will be running.
pub(crate) fn pod_name(&self) -> String {
self.pod_name.clone()
Expand Down
2 changes: 2 additions & 0 deletions k8s/upgrade-job/src/upgrade.rs
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,8 @@ pub(crate) async fn upgrade(opts: &CliArgs) -> Result<()> {
.with_release_name(opts.release_name())
.with_umbrella_chart_dir(opts.umbrella_chart_dir())
.with_core_chart_dir(opts.core_chart_dir())
.with_upgrade_path_file(opts.upgrade_exception_file())
.with_skip_upgrade_path_validation(opts.skip_upgrade_path_validation())
.build()?;

let from_version = helm_upgrade.upgrade_from_version();
Expand Down
53 changes: 36 additions & 17 deletions k8s/upgrade-job/src/upgrade/path.rs
Original file line number Diff line number Diff line change
@@ -1,18 +1,23 @@
use crate::{
common::{
constants::{FROM_CORE_SEMVER, FROM_UMBRELLA_SEMVER, TO_CORE_SEMVER, TO_UMBRELLA_SEMVER},
constants::{FROM_UMBRELLA_SEMVER, TO_UMBRELLA_SEMVER},
error::{HelmChartNameSplit, OpeningFile, Result, SemverParse, YamlParseFromFile},
},
helm::{chart::Chart, upgrade::HelmChart},
};

use semver::{Version, VersionReq};

use serde::{Deserialize, Serialize};
use snafu::ResultExt;
use std::{fs::File, path::PathBuf};
use std::{collections::HashSet, fs::File, path::PathBuf};

/// Validates the upgrade path from 'from' Version to 'to' Version for 'chart_variant' helm chart.
pub(crate) fn is_valid(chart_variant: HelmChart, from: &Version, to: &Version) -> Result<bool> {
pub(crate) fn is_valid(
chart_variant: HelmChart,
from: &Version,
to: &Version,
invalid_upgrade_path: HashSet<Version>,
) -> Result<bool> {
match chart_variant {
HelmChart::Umbrella => {
let to_req = VersionReq::parse(TO_UMBRELLA_SEMVER).context(SemverParse {
Expand All @@ -27,19 +32,7 @@ pub(crate) fn is_valid(chart_variant: HelmChart, from: &Version, to: &Version) -
}
Ok(false)
}
HelmChart::Core => {
let to_req = VersionReq::parse(TO_CORE_SEMVER).context(SemverParse {
version_string: TO_CORE_SEMVER.to_string(),
})?;

if to_req.matches(to) {
let from_req = VersionReq::parse(FROM_CORE_SEMVER).context(SemverParse {
version_string: FROM_CORE_SEMVER.to_string(),
})?;
return Ok(from_req.matches(from));
}
Ok(false)
}
HelmChart::Core => Ok(!invalid_upgrade_path.contains(from)),
}
}

Expand Down Expand Up @@ -72,3 +65,29 @@ pub(crate) fn version_from_release_chart(chart_name: String) -> Result<Version>
version_string: version.to_string(),
})
}

/// Struct to deserialize the unsupported version yaml.
#[derive(Debug, Serialize, Deserialize)]
struct UnsupportedVersions {
unsupported_versions: Vec<String>,
}

/// Returns the HashSet of invalid source versions.
pub(crate) fn invalid_upgrade_path(path: PathBuf) -> Result<HashSet<Version>> {
let unsupported_versions_yaml = File::open(path.as_path()).context(OpeningFile {
filepath: path.clone(),
})?;

let unsupported: UnsupportedVersions = serde_yaml::from_reader(unsupported_versions_yaml)
.context(YamlParseFromFile { filepath: path })?;

let mut unsupported_versions_set: HashSet<Version> = HashSet::new();

for version in unsupported.unsupported_versions.iter() {
let unsupported_version = Version::parse(version.as_str()).context(SemverParse {
version_string: version.clone(),
})?;
unsupported_versions_set.insert(unsupported_version);
}
Ok(unsupported_versions_set)
}
4 changes: 3 additions & 1 deletion k8s/upgrade/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -26,5 +26,7 @@ async-trait = "0.1.64"
serde = "1.0.152"
serde_json = "1.0.93"
snafu = "0.7.4"
serde_yaml = "0.9.17"
semver = { version="1.0.17", features = ["serde"] }
# Tracing
tracing = "0.1.37"
tracing = "0.1.37"
3 changes: 3 additions & 0 deletions k8s/upgrade/config/unsupported_versions.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
unsupported_versions:
# add the list of unsupported versions as shown below
- 0.0.0
15 changes: 6 additions & 9 deletions k8s/upgrade/src/constant.rs
Original file line number Diff line number Diff line change
Expand Up @@ -15,15 +15,8 @@ macro_rules! upgrade_labels {
}

/// Append the release name to k8s objects.
pub(crate) fn upgrade_name_concat(
release_name: &str,
component_name: &str,
upgrade_to_branch: Option<&String>,
) -> String {
let version = match upgrade_to_branch {
Some(tag) => tag.to_string(),
None => upgrade_obj_suffix(),
};
pub(crate) fn upgrade_name_concat(release_name: &str, component_name: &str) -> String {
let version = upgrade_obj_suffix();
format!("{release_name}-{component_name}-{version}")
}

Expand Down Expand Up @@ -105,3 +98,7 @@ pub(crate) const AGENT_CORE_POD_LABEL: &str = "app=agent-core";
pub(crate) const API_REST_POD_LABEL: &str = "app=api-rest";
/// UPGRADE_EVENT_REASON is the reason field in upgrade job.
pub(crate) const UPGRADE_EVENT_REASON: &str = "MayastorUpgrade";
/// Installed release version.
pub(crate) const HELM_RELEASE_VERSION_LABEL: &str = "openebs.io/version";
/// File contating unsupported versions.
pub(crate) const UNSUPPORTED_VERSION_FILE: &str = "k8s/upgrade/config/unsupported_versions.yaml";
89 changes: 89 additions & 0 deletions k8s/upgrade/src/error.rs
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
use snafu::Snafu;
use std::path::PathBuf;

/// For use with multiple fallible operations which may fail for different reasons, but are
/// defined withing the same scope and must return to the outer scope (calling scope) using
Expand Down Expand Up @@ -189,7 +190,95 @@ pub enum Error {
/// Openapi configuration error.
#[snafu(display("openapi configuration Error: {}", source))]
OpenapiClientConfiguration { source: anyhow::Error },

/// Error when opening a file.
#[snafu(display("Failed to open file {}: {}", filepath.display(), source))]
OpeningFile {
source: std::io::Error,
filepath: PathBuf,
},

/// Error for when yaml could not be parsed from a file (Reader).
#[snafu(display("Failed to parse YAML at {}: {}", filepath.display(), source))]
YamlParseFromFile {
source: serde_yaml::Error,
filepath: PathBuf,
},

/// Error for failures in generating semver::Value from a &str input.
#[snafu(display("Failed to parse {} as a valid semver: {}", version_string, source))]
SemverParse {
source: semver::Error,
version_string: String,
},

#[snafu(display("Failed to get current directory: {}", source))]
GetCurrentDirectory { source: std::io::Error },

/// Source and target version are same.
#[snafu(display("Source and target version are same for upgrade."))]
SourceTargetVersionSame,

/// Error when source version is not a valid for upgrade.
#[snafu(display("Not a valid source version for upgrade."))]
NotAValidSourceForUpgrade,

/// Error for when the detected upgrade path for PRODUCT is not supported.
#[snafu(display("The upgrade path is invalid"))]
InvalidUpgradePath,
}

/// A wrapper type to remove repeated Result<T, Error> returns.
pub(crate) type Result<T, E = Error> = std::result::Result<T, E>;

impl From<Error> for i32 {
fn from(err: Error) -> Self {
match err {
Error::GetCurrentDirectory { .. } => 401,
Error::UpgradeEventNotPresent { .. } => 401,
Error::NoDeploymentPresent { .. } => 403,
Error::MessageInEventNotPresent { .. } => 404,
Error::NodesInCordonedState { .. } => 405,
Error::SingleReplicaVolumeErr { .. } => 406,
Error::VolumeRebuildInProgress { .. } => 407,
Error::K8sClient { .. } => 408,
Error::EventSerdeDeserialization { .. } => 409,
Error::ServiceAccountCreate { .. } => 410,
Error::ServiceAccountDelete { .. } => 411,
Error::ClusterRoleCreate { .. } => 412,
Error::ClusterRoleDelete { .. } => 413,
Error::ClusterRoleBindingDelete { .. } => 414,
Error::ClusterRoleBindingCreate { .. } => 415,
Error::UpgradeJobCreate { .. } => 416,
Error::UpgradeJobDelete { .. } => 417,
Error::ReferenceDeploymentInvalidImage { .. } => 418,
Error::ReferenceDeploymentNoImage { .. } => 419,
Error::ReferenceDeploymentNoSpec { .. } => 420,
Error::ReferenceDeploymentNoPodTemplateSpec { .. } => 421,
Error::ReferenceDeploymentNoContainers { .. } => 422,
Error::NodeSpecNotPresent { .. } => 423,
Error::PodNameNotPresent { .. } => 424,
Error::UpgradeJobStatusNotPresent { .. } => 425,
Error::UpgradeJobNotCompleted { .. } => 426,
Error::ListPodsWithLabel { .. } => 427,
Error::ListDeploymantsWithLabel { .. } => 428,
Error::ListEventsWithFieldSelector { .. } => 429,
Error::ListPVC { .. } => 430,
Error::ListVolumes { .. } => 431,
Error::GetUpgradeJob { .. } => 432,
Error::GetServiceAccount { .. } => 433,
Error::GetClusterRole { .. } => 434,
Error::GetClusterRoleBinding { .. } => 435,
Error::K8sClientGeneration { .. } => 436,
Error::RestClientConfiguration { .. } => 437,
Error::ListStorageNodes { .. } => 438,
Error::OpenapiClientConfiguration { .. } => 439,
Error::OpeningFile { .. } => 440,
Error::YamlParseFromFile { .. } => 441,
Error::SemverParse { .. } => 442,
Error::SourceTargetVersionSame { .. } => 443,
Error::NotAValidSourceForUpgrade { .. } => 444,
Error::InvalidUpgradePath { .. } => 445,
}
}
}
Loading

0 comments on commit a56391a

Please sign in to comment.