Merge pull request 'feat: add mariadb helm deployment to lamp interpreter' (#26) from feat/lampDatabase into master
Reviewed-on: https://git.nationtech.io/NationTech/harmony/pulls/26 Reviewed-by: wjro <wrolleman@nationtech.io>
This commit is contained in:
commit
28978299c9
2
Cargo.lock
generated
2
Cargo.lock
generated
@ -1020,8 +1020,8 @@ dependencies = [
|
||||
"cidr",
|
||||
"env_logger",
|
||||
"harmony",
|
||||
"harmony_cli",
|
||||
"harmony_macros",
|
||||
"harmony_tui",
|
||||
"harmony_types",
|
||||
"log",
|
||||
"tokio",
|
||||
|
@ -8,7 +8,7 @@ publish = false
|
||||
|
||||
[dependencies]
|
||||
harmony = { path = "../../harmony" }
|
||||
harmony_tui = { path = "../../harmony_tui" }
|
||||
harmony_cli = { path = "../../harmony_cli" }
|
||||
harmony_types = { path = "../../harmony_types" }
|
||||
cidr = { workspace = true }
|
||||
tokio = { workspace = true }
|
||||
|
@ -26,5 +26,5 @@ async fn main() {
|
||||
.await
|
||||
.unwrap();
|
||||
maestro.register_all(vec![Box::new(lamp_stack)]);
|
||||
harmony_tui::init(maestro).await.unwrap();
|
||||
harmony_cli::init(maestro, None).await.unwrap();
|
||||
}
|
||||
|
@ -6,10 +6,12 @@ use crate::topology::{HelmCommand, Topology};
|
||||
use async_trait::async_trait;
|
||||
use helm_wrapper_rs;
|
||||
use helm_wrapper_rs::blocking::{DefaultHelmExecutor, HelmExecutor};
|
||||
use log::info;
|
||||
pub use non_blank_string_rs::NonBlankString;
|
||||
use serde::Serialize;
|
||||
use std::collections::HashMap;
|
||||
use std::path::Path;
|
||||
use std::str::FromStr;
|
||||
use temp_file::TempFile;
|
||||
|
||||
#[derive(Debug, Clone, Serialize)]
|
||||
@ -20,6 +22,10 @@ pub struct HelmChartScore {
|
||||
pub chart_version: Option<NonBlankString>,
|
||||
pub values_overrides: Option<HashMap<NonBlankString, String>>,
|
||||
pub values_yaml: Option<String>,
|
||||
pub create_namespace: bool,
|
||||
|
||||
/// Wether to run `helm upgrade --install` under the hood or only install when not present
|
||||
pub install_only: bool,
|
||||
}
|
||||
|
||||
impl<T: Topology + HelmCommand> Score<T> for HelmChartScore {
|
||||
@ -62,6 +68,47 @@ impl<T: Topology + HelmCommand> Interpret<T> for HelmChartInterpret {
|
||||
};
|
||||
|
||||
let helm_executor = DefaultHelmExecutor::new();
|
||||
|
||||
let mut helm_options = Vec::new();
|
||||
if self.score.create_namespace {
|
||||
helm_options.push(NonBlankString::from_str("--create-namespace").unwrap());
|
||||
}
|
||||
|
||||
if self.score.install_only {
|
||||
let chart_list = match helm_executor.list(Some(ns)) {
|
||||
Ok(charts) => charts,
|
||||
Err(e) => {
|
||||
return Err(InterpretError::new(format!(
|
||||
"Failed to list scores in namespace {:?} because of error : {}",
|
||||
self.score.namespace, e
|
||||
)));
|
||||
}
|
||||
};
|
||||
|
||||
if chart_list
|
||||
.iter()
|
||||
.any(|item| item.name == self.score.release_name.to_string())
|
||||
{
|
||||
info!(
|
||||
"Release '{}' already exists in namespace '{}'. Skipping installation as install_only is true.",
|
||||
self.score.release_name, ns
|
||||
);
|
||||
|
||||
return Ok(Outcome::new(
|
||||
InterpretStatus::SUCCESS,
|
||||
format!(
|
||||
"Helm Chart '{}' already installed to namespace {ns} and install_only=true",
|
||||
self.score.release_name
|
||||
),
|
||||
));
|
||||
} else {
|
||||
info!(
|
||||
"Release '{}' not found in namespace '{}'. Proceeding with installation.",
|
||||
self.score.release_name, ns
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
let res = helm_executor.install_or_upgrade(
|
||||
&ns,
|
||||
&self.score.release_name,
|
||||
@ -69,7 +116,7 @@ impl<T: Topology + HelmCommand> Interpret<T> for HelmChartInterpret {
|
||||
self.score.chart_version.as_ref(),
|
||||
self.score.values_overrides.as_ref(),
|
||||
yaml_path,
|
||||
None,
|
||||
Some(&helm_options),
|
||||
);
|
||||
|
||||
let status = match res {
|
||||
|
@ -1,2 +1,3 @@
|
||||
pub mod deployment;
|
||||
pub mod namespace;
|
||||
pub mod resource;
|
||||
|
46
harmony/src/modules/k8s/namespace.rs
Normal file
46
harmony/src/modules/k8s/namespace.rs
Normal file
@ -0,0 +1,46 @@
|
||||
use k8s_openapi::api::core::v1::Namespace;
|
||||
use non_blank_string_rs::NonBlankString;
|
||||
use serde::Serialize;
|
||||
use serde_json::json;
|
||||
|
||||
use crate::{
|
||||
interpret::Interpret,
|
||||
score::Score,
|
||||
topology::{K8sclient, Topology},
|
||||
};
|
||||
|
||||
#[derive(Debug, Clone, Serialize)]
|
||||
pub struct K8sNamespaceScore {
|
||||
pub name: Option<NonBlankString>,
|
||||
}
|
||||
|
||||
impl<T: Topology + K8sclient> Score<T> for K8sNamespaceScore {
|
||||
fn create_interpret(&self) -> Box<dyn Interpret<T>> {
|
||||
let name = match &self.name {
|
||||
Some(name) => name,
|
||||
None => todo!(
|
||||
"Return NoOp interpret when no namespace specified or something that makes sense"
|
||||
),
|
||||
};
|
||||
let _namespace: Namespace = serde_json::from_value(json!(
|
||||
{
|
||||
"apiVersion": "v1",
|
||||
"kind": "Namespace",
|
||||
"metadata": {
|
||||
"name": name,
|
||||
},
|
||||
}
|
||||
))
|
||||
.unwrap();
|
||||
todo!(
|
||||
"We currently only support namespaced ressources (see Scope = NamespaceResourceScope)"
|
||||
);
|
||||
// Box::new(K8sResourceInterpret {
|
||||
// score: K8sResourceScore::single(namespace.clone()),
|
||||
// })
|
||||
}
|
||||
|
||||
fn name(&self) -> String {
|
||||
"K8sNamespaceScore".to_string()
|
||||
}
|
||||
}
|
@ -1,9 +1,15 @@
|
||||
use dockerfile_builder::instruction::{CMD, COPY, ENV, EXPOSE, FROM, RUN, WORKDIR};
|
||||
use dockerfile_builder::{Dockerfile, instruction_builder::EnvBuilder};
|
||||
use non_blank_string_rs::NonBlankString;
|
||||
use std::fs;
|
||||
use std::path::{Path, PathBuf};
|
||||
use std::str::FromStr;
|
||||
|
||||
use async_trait::async_trait;
|
||||
use log::info;
|
||||
use serde::Serialize;
|
||||
|
||||
use crate::topology::HelmCommand;
|
||||
use crate::{
|
||||
data::{Id, Version},
|
||||
interpret::{Interpret, InterpretError, InterpretName, InterpretStatus, Outcome},
|
||||
@ -13,6 +19,8 @@ use crate::{
|
||||
topology::{K8sclient, Topology, Url},
|
||||
};
|
||||
|
||||
use super::helm::chart::HelmChartScore;
|
||||
|
||||
#[derive(Debug, Clone, Serialize)]
|
||||
pub struct LAMPScore {
|
||||
pub name: String,
|
||||
@ -36,10 +44,11 @@ impl Default for LAMPConfig {
|
||||
}
|
||||
}
|
||||
|
||||
impl<T: Topology + K8sclient> Score<T> for LAMPScore {
|
||||
impl<T: Topology + K8sclient + HelmCommand> Score<T> for LAMPScore {
|
||||
fn create_interpret(&self) -> Box<dyn Interpret<T>> {
|
||||
Box::new(LAMPInterpret {
|
||||
score: self.clone(),
|
||||
namespace: "harmony-lamp".to_string(),
|
||||
})
|
||||
}
|
||||
|
||||
@ -51,10 +60,11 @@ impl<T: Topology + K8sclient> Score<T> for LAMPScore {
|
||||
#[derive(Debug)]
|
||||
pub struct LAMPInterpret {
|
||||
score: LAMPScore,
|
||||
namespace: String,
|
||||
}
|
||||
|
||||
#[async_trait]
|
||||
impl<T: Topology + K8sclient> Interpret<T> for LAMPInterpret {
|
||||
impl<T: Topology + K8sclient + HelmCommand> Interpret<T> for LAMPInterpret {
|
||||
async fn execute(
|
||||
&self,
|
||||
inventory: &Inventory,
|
||||
@ -70,18 +80,23 @@ impl<T: Topology + K8sclient> Interpret<T> for LAMPInterpret {
|
||||
};
|
||||
info!("LAMP docker image built {image_name}");
|
||||
|
||||
info!("Deploying database");
|
||||
self.deploy_database(inventory, topology).await?;
|
||||
|
||||
let deployment_score = K8sDeploymentScore {
|
||||
name: <LAMPScore as Score<T>>::name(&self.score),
|
||||
image: image_name,
|
||||
};
|
||||
|
||||
info!("LAMP deployment_score {deployment_score:?}");
|
||||
todo!();
|
||||
deployment_score
|
||||
.create_interpret()
|
||||
.execute(inventory, topology)
|
||||
.await?;
|
||||
todo!()
|
||||
|
||||
info!("LAMP deployment_score {deployment_score:?}");
|
||||
todo!("1. Use HelmChartScore to deploy mariadb
|
||||
2. Use deploymentScore to deploy lamp docker container
|
||||
3. for remote clusters, push the image to some registry (use nationtech's for demos? push to the cluster's registry?)");
|
||||
}
|
||||
|
||||
fn get_name(&self) -> InterpretName {
|
||||
@ -101,15 +116,31 @@ impl<T: Topology + K8sclient> Interpret<T> for LAMPInterpret {
|
||||
}
|
||||
}
|
||||
|
||||
use dockerfile_builder::instruction::{CMD, COPY, ENV, EXPOSE, FROM, RUN, WORKDIR};
|
||||
use dockerfile_builder::{Dockerfile, instruction_builder::EnvBuilder};
|
||||
use std::fs;
|
||||
|
||||
impl LAMPInterpret {
|
||||
pub fn build_dockerfile(
|
||||
async fn deploy_database<T: Topology + K8sclient + HelmCommand>(
|
||||
&self,
|
||||
score: &LAMPScore,
|
||||
) -> Result<PathBuf, Box<dyn std::error::Error>> {
|
||||
inventory: &Inventory,
|
||||
topology: &T,
|
||||
) -> Result<Outcome, InterpretError> {
|
||||
let score = HelmChartScore {
|
||||
namespace: self.get_namespace(),
|
||||
release_name: NonBlankString::from_str(&format!("{}-database", self.score.name))
|
||||
.unwrap(),
|
||||
chart_name: NonBlankString::from_str(
|
||||
"oci://registry-1.docker.io/bitnamicharts/mariadb",
|
||||
)
|
||||
.unwrap(),
|
||||
chart_version: None,
|
||||
values_overrides: None,
|
||||
create_namespace: true,
|
||||
install_only: true,
|
||||
values_yaml: None,
|
||||
};
|
||||
|
||||
score.create_interpret().execute(inventory, topology).await
|
||||
}
|
||||
|
||||
fn build_dockerfile(&self, score: &LAMPScore) -> Result<PathBuf, Box<dyn std::error::Error>> {
|
||||
let mut dockerfile = Dockerfile::new();
|
||||
|
||||
// Use the PHP version from the score to determine the base image
|
||||
@ -260,4 +291,8 @@ opcache.fast_shutdown=1
|
||||
|
||||
Ok(image_name)
|
||||
}
|
||||
|
||||
fn get_namespace(&self) -> Option<NonBlankString> {
|
||||
Some(NonBlankString::from_str(&self.namespace).unwrap())
|
||||
}
|
||||
}
|
||||
|
@ -4,14 +4,14 @@ pub mod modules;
|
||||
|
||||
pub use config::Config;
|
||||
pub use error::Error;
|
||||
#[cfg(test)]
|
||||
mod test {
|
||||
|
||||
#[cfg(e2e_test)]
|
||||
mod e2e_test {
|
||||
use opnsense_config_xml::StaticMap;
|
||||
use std::net::Ipv4Addr;
|
||||
|
||||
use crate::Config;
|
||||
|
||||
#[cfg(opnsenseendtoend)]
|
||||
#[tokio::test]
|
||||
async fn test_public_sdk() {
|
||||
use pretty_assertions::assert_eq;
|
||||
|
Loading…
Reference in New Issue
Block a user