Compare commits
2 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| f242aafebb | |||
| 3e14ebd62c |
@@ -2,19 +2,19 @@ use std::str::FromStr;
|
|||||||
|
|
||||||
use harmony::{
|
use harmony::{
|
||||||
inventory::Inventory,
|
inventory::Inventory,
|
||||||
modules::{k8s::apps::OperatorHubCatalogSourceScore, tenant::TenantScore},
|
modules::{k8s::apps::OperatorHubCatalogSourceScore, postgresql::CloudNativePgOperatorScore},
|
||||||
topology::{K8sAnywhereTopology, tenant::TenantConfig},
|
topology::K8sAnywhereTopology,
|
||||||
};
|
};
|
||||||
use harmony_types::id::Id;
|
|
||||||
|
|
||||||
#[tokio::main]
|
#[tokio::main]
|
||||||
async fn main() {
|
async fn main() {
|
||||||
let operatorhub_catalog = OperatorHubCatalogSourceScore::default();
|
let operatorhub_catalog = OperatorHubCatalogSourceScore::default();
|
||||||
|
let cnpg_operator = CloudNativePgOperatorScore::default();
|
||||||
|
|
||||||
harmony_cli::run(
|
harmony_cli::run(
|
||||||
Inventory::autoload(),
|
Inventory::autoload(),
|
||||||
K8sAnywhereTopology::from_env(),
|
K8sAnywhereTopology::from_env(),
|
||||||
vec![Box::new(operatorhub_catalog)],
|
vec![Box::new(operatorhub_catalog), Box::new(cnpg_operator)],
|
||||||
None,
|
None,
|
||||||
)
|
)
|
||||||
.await
|
.await
|
||||||
|
|||||||
@@ -1,2 +1,4 @@
|
|||||||
mod catalogsources_operators_coreos_com;
|
mod catalogsources_operators_coreos_com;
|
||||||
pub use catalogsources_operators_coreos_com::*;
|
pub use catalogsources_operators_coreos_com::*;
|
||||||
|
mod subscriptions_operators_coreos_com;
|
||||||
|
pub use subscriptions_operators_coreos_com::*;
|
||||||
|
|||||||
@@ -0,0 +1,68 @@
|
|||||||
|
use k8s_openapi::apimachinery::pkg::apis::meta::v1::ObjectMeta;
|
||||||
|
use kube::CustomResource;
|
||||||
|
use serde::{Deserialize, Serialize};
|
||||||
|
|
||||||
|
#[derive(CustomResource, Deserialize, Serialize, Clone, Debug)]
|
||||||
|
#[kube(
|
||||||
|
group = "operators.coreos.com",
|
||||||
|
version = "v1alpha1",
|
||||||
|
kind = "Subscription",
|
||||||
|
plural = "subscriptions",
|
||||||
|
namespaced = true,
|
||||||
|
schema = "disabled"
|
||||||
|
)]
|
||||||
|
#[serde(rename_all = "camelCase")]
|
||||||
|
pub struct SubscriptionSpec {
|
||||||
|
#[serde(skip_serializing_if = "Option::is_none")]
|
||||||
|
pub channel: Option<String>,
|
||||||
|
|
||||||
|
#[serde(skip_serializing_if = "Option::is_none")]
|
||||||
|
pub config: Option<SubscriptionConfig>,
|
||||||
|
|
||||||
|
#[serde(skip_serializing_if = "Option::is_none")]
|
||||||
|
pub install_plan_approval: Option<String>,
|
||||||
|
|
||||||
|
pub name: String,
|
||||||
|
|
||||||
|
pub source: String,
|
||||||
|
|
||||||
|
pub source_namespace: String,
|
||||||
|
|
||||||
|
#[serde(skip_serializing_if = "Option::is_none")]
|
||||||
|
pub starting_csv: Option<String>,
|
||||||
|
}
|
||||||
|
#[derive(Deserialize, Serialize, Clone, Debug)]
|
||||||
|
#[serde(rename_all = "camelCase")]
|
||||||
|
pub struct SubscriptionConfig {
|
||||||
|
#[serde(skip_serializing_if = "Option::is_none")]
|
||||||
|
pub env: Option<Vec<k8s_openapi::api::core::v1::EnvVar>>,
|
||||||
|
|
||||||
|
#[serde(skip_serializing_if = "Option::is_none")]
|
||||||
|
pub node_selector: Option<std::collections::BTreeMap<String, String>>,
|
||||||
|
|
||||||
|
#[serde(skip_serializing_if = "Option::is_none")]
|
||||||
|
pub tolerations: Option<Vec<k8s_openapi::api::core::v1::Toleration>>,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Default for Subscription {
|
||||||
|
fn default() -> Self {
|
||||||
|
Subscription {
|
||||||
|
metadata: ObjectMeta::default(),
|
||||||
|
spec: SubscriptionSpec::default(),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Default for SubscriptionSpec {
|
||||||
|
fn default() -> SubscriptionSpec {
|
||||||
|
SubscriptionSpec {
|
||||||
|
name: String::new(),
|
||||||
|
source: String::new(),
|
||||||
|
source_namespace: String::new(),
|
||||||
|
channel: None,
|
||||||
|
config: None,
|
||||||
|
install_plan_approval: None,
|
||||||
|
starting_csv: None,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -2,3 +2,5 @@ pub mod capability;
|
|||||||
mod score;
|
mod score;
|
||||||
|
|
||||||
pub mod failover;
|
pub mod failover;
|
||||||
|
mod operator;
|
||||||
|
pub use operator::*;
|
||||||
|
|||||||
102
harmony/src/modules/postgresql/operator.rs
Normal file
102
harmony/src/modules/postgresql/operator.rs
Normal file
@@ -0,0 +1,102 @@
|
|||||||
|
use k8s_openapi::apimachinery::pkg::apis::meta::v1::ObjectMeta;
|
||||||
|
use serde::Serialize;
|
||||||
|
|
||||||
|
use crate::interpret::Interpret;
|
||||||
|
use crate::modules::k8s::apps::crd::{Subscription, SubscriptionSpec};
|
||||||
|
use crate::modules::k8s::resource::K8sResourceScore;
|
||||||
|
use crate::score::Score;
|
||||||
|
use crate::topology::{K8sclient, Topology};
|
||||||
|
|
||||||
|
/// Install the CloudNativePg (CNPG) Operator via an OperatorHub `Subscription`.
|
||||||
|
///
|
||||||
|
/// This Score creates a a `Subscription` Custom Resource in the specified namespace.
|
||||||
|
///
|
||||||
|
/// The default implementation pulls the `cloudnative-pg` operator from the
|
||||||
|
/// `operatorhubio-catalog` source.
|
||||||
|
///
|
||||||
|
/// # Goals
|
||||||
|
/// - Deploy the CNPG Operator to manage PostgreSQL clusters in OpenShift/OKD environments.
|
||||||
|
///
|
||||||
|
/// # Usage
|
||||||
|
/// ```
|
||||||
|
/// use harmony::modules::postgresql::CloudNativePgOperatorScore;
|
||||||
|
/// let score = CloudNativePgOperatorScore::default();
|
||||||
|
/// ```
|
||||||
|
///
|
||||||
|
/// Or, you can take control of most relevant fiedls this way :
|
||||||
|
///
|
||||||
|
/// ```
|
||||||
|
/// use harmony::modules::postgresql::CloudNativePgOperatorScore;
|
||||||
|
///
|
||||||
|
/// let score = CloudNativePgOperatorScore {
|
||||||
|
/// namespace: "custom-cnpg-namespace".to_string(),
|
||||||
|
/// channel: "unstable-i-want-bleedingedge-v498437".to_string(),
|
||||||
|
/// install_plan_approval: "Manual".to_string(),
|
||||||
|
/// source: "operatorhubio-catalog-but-different".to_string(),
|
||||||
|
/// source_namespace: "i-customize-everything-marketplace".to_string(),
|
||||||
|
/// };
|
||||||
|
/// ```
|
||||||
|
///
|
||||||
|
/// # Limitations
|
||||||
|
/// - **OperatorHub dependency**: Requires OperatorHub catalog sources (e.g., `operatorhubio-catalog` in `openshift-marketplace`).
|
||||||
|
/// - **OKD/OpenShift assumption**: Catalog/source names and namespaces are hardcoded for OKD-like setups; adjust for upstream OpenShift.
|
||||||
|
/// - **Hardcoded values in Default implementation**: Operator name (`cloudnative-pg`), channel (`stable-v1`), automatic install plan approval.
|
||||||
|
/// - **No config options**: Does not support custom `SubscriptionConfig` (env vars, node selectors, tolerations).
|
||||||
|
/// - **Single namespace**: Targets one namespace per score instance.
|
||||||
|
#[derive(Debug, Clone, Serialize)]
|
||||||
|
pub struct CloudNativePgOperatorScore {
|
||||||
|
pub namespace: String,
|
||||||
|
pub channel: String,
|
||||||
|
pub install_plan_approval: String,
|
||||||
|
pub source: String,
|
||||||
|
pub source_namespace: String,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Default for CloudNativePgOperatorScore {
|
||||||
|
fn default() -> Self {
|
||||||
|
Self {
|
||||||
|
namespace: "openshift-operators".to_string(),
|
||||||
|
channel: "stable-v1".to_string(),
|
||||||
|
install_plan_approval: "Automatic".to_string(),
|
||||||
|
source: "operatorhubio-catalog".to_string(),
|
||||||
|
source_namespace: "openshift-marketplace".to_string(),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl CloudNativePgOperatorScore {
|
||||||
|
pub fn new(namespace: &str) -> Self {
|
||||||
|
Self {
|
||||||
|
namespace: namespace.to_string(),
|
||||||
|
..Default::default()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<T: Topology + K8sclient> Score<T> for CloudNativePgOperatorScore {
|
||||||
|
fn create_interpret(&self) -> Box<dyn Interpret<T>> {
|
||||||
|
let metadata = ObjectMeta {
|
||||||
|
name: Some("cloudnative-pg".to_string()),
|
||||||
|
namespace: Some(self.namespace.clone()),
|
||||||
|
..ObjectMeta::default()
|
||||||
|
};
|
||||||
|
|
||||||
|
let spec = SubscriptionSpec {
|
||||||
|
channel: Some(self.channel.clone()),
|
||||||
|
config: None,
|
||||||
|
install_plan_approval: Some(self.install_plan_approval.clone()),
|
||||||
|
name: "cloudnative-pg".to_string(),
|
||||||
|
source: self.source.clone(),
|
||||||
|
source_namespace: self.source_namespace.clone(),
|
||||||
|
starting_csv: None,
|
||||||
|
};
|
||||||
|
|
||||||
|
let subscription = Subscription { metadata, spec };
|
||||||
|
|
||||||
|
K8sResourceScore::single(subscription, Some(self.namespace.clone())).create_interpret()
|
||||||
|
}
|
||||||
|
|
||||||
|
fn name(&self) -> String {
|
||||||
|
format!("CloudNativePgOperatorScore({})", self.namespace)
|
||||||
|
}
|
||||||
|
}
|
||||||
Reference in New Issue
Block a user