fix: unjank the demo #85

Merged
taha merged 19 commits from fix_demo into master 2025-07-11 14:32:22 +00:00
10 changed files with 73 additions and 50 deletions
Showing only changes of commit 6de889aa0f - Show all commits

4
.gitignore vendored
View File

@ -2,3 +2,7 @@ target
private_repos
log/
*.tgz
examples/rust/examples/rust/webapp/helm/
Review

These would be better in the examples's own gitignore file examples/rust/.gitignore. Eventually we may have hundreds of examples, we don't want thousands of lines in this file.

But thinking a bit further, this points towards a problem on the way we manage these generated files. They should probably end up in a harmony specific directory that we can then ignore in a single line for all examples. Maybe harmony_generated or harmony_build ?

These would be better in the examples's own gitignore file `examples/rust/.gitignore`. Eventually we may have hundreds of examples, we don't want thousands of lines in this file. But thinking a bit further, this points towards a problem on the way we manage these generated files. They should probably end up in a harmony specific directory that we can then ignore in a single line for all examples. Maybe `harmony_generated` or `harmony_build` ?
examples/rust/examples/rust/webapp/Dockerfile.harmony
examples/rust/webapp/helm/harmony-example-rust-webapp-chart/
.gitignore

1
Cargo.lock generated
View File

@ -1726,6 +1726,7 @@ name = "harmony"
version = "0.1.0"
dependencies = [
"async-trait",
"bollard",
"chrono",
"cidr",
"convert_case",

View File

@ -53,3 +53,4 @@ chrono = "0.4"
similar = "2"
uuid = { version = "1.11", features = ["v4", "fast-rng", "macro-diagnostics"] }
pretty_assertions = "1.4.1"
bollard = "0.19.1"

View File

@ -1,18 +1,41 @@
use std::{path::PathBuf, sync::Arc};
use harmony::{
data::Id,
inventory::Inventory,
maestro::Maestro,
modules::application::{
ApplicationScore, RustWebFramework, RustWebapp,
features::{ContinuousDelivery, Monitoring},
modules::{
application::{
ApplicationScore, RustWebFramework, RustWebapp,
features::{ContinuousDelivery, Monitoring},
},
tenant::TenantScore,
},
topology::{
K8sAnywhereTopology, Url,
tenant::{ResourceLimits, TenantConfig, TenantNetworkPolicy},
},
topology::{K8sAnywhereTopology, Url},
};
#[tokio::main]
async fn main() {
env_logger::init();
let tenant = TenantScore {
config: TenantConfig {
id: Id::from_string("1234".to_string()),
name: "harmonydemo-staging".to_string(),
resource_limits: ResourceLimits {
cpu_request_cores: 6.0,
cpu_limit_cores: 4.0,
memory_request_gb: 4.0,

The Tenant should not be visible at the applcation scope. An application will auto detect the tenant it is supposed to be in from the environment (such as the content of the loaded kubeconfig).

For example, a client cannot provision its own tenant as it may not have permission on the host cluster to do so.

There may be exceptions and more complex use cases coming up soon, but for the sake of this example we want to keep it 100% focused on the application and especially how simple it it to deploy a fully featured infrastructure around an application with as close to zero config as possible.

The Tenant should not be visible at the applcation scope. An application will auto detect the tenant it is supposed to be in from the environment (such as the content of the loaded kubeconfig). For example, a client cannot provision its own tenant as it may not have permission on the host cluster to do so. There may be exceptions and more complex use cases coming up soon, but for the sake of this example we want to keep it 100% focused on the application and especially how **simple** it it to deploy a fully featured infrastructure around an application with as close to zero config as possible.
memory_limit_gb: 4.0,
storage_total_gb: 10.0,
},
network_policy: TenantNetworkPolicy::default(),
},
};
let application = Arc::new(RustWebapp {
name: "harmony-example-rust-webapp".to_string(),
domain: Url::Url(url::Url::parse("https://rustapp.harmony.example.com").unwrap()),
@ -35,6 +58,6 @@ async fn main() {
let mut maestro = Maestro::initialize(Inventory::autoload(), topology)
.await
.unwrap();
maestro.register_all(vec![Box::new(app)]);
maestro.register_all(vec![Box::new(tenant), Box::new(app)]);
harmony_cli::init(maestro, None).await.unwrap();
}

View File

@ -59,6 +59,7 @@ tokio-util = "0.7.15"
strum = { version = "0.27.1", features = ["derive"] }
tempfile = "3.20.0"
serde_with = "3.14.0"
bollard.workspace = true
[dev-dependencies]
pretty_assertions.workspace = true

View File

@ -1,6 +1,3 @@
use std::{backtrace, collections::HashMap};
use k8s_openapi::{Metadata, NamespaceResourceScope, Resource};
use log::debug;
use serde::Serialize;
use serde_with::skip_serializing_none;
@ -34,10 +31,11 @@ pub struct Helm {
#[serde(rename_all = "camelCase")]
pub struct Source {
#[serde(rename = "repoURL")]
pub repo_url: Url,
pub repo_url: String,
pub target_revision: Option<String>,
pub chart: String,
pub helm: Helm,
pub path: String,
}
#[derive(Clone, Debug, Serialize)]
@ -90,7 +88,7 @@ impl Default for ArgoApplication {
namespace: Default::default(),
project: Default::default(),
source: Source {
Outdated
Review

I would write some documentation for why you replaced url with string

I would write some documentation for why you replaced url with string
repo_url: Url::parse("http://asdf").expect("Couldn't parse to URL"),
repo_url: "http://asdf".to_string(),
target_revision: None,
chart: "".to_string(),
helm: Helm {
@ -109,6 +107,7 @@ impl Default for ArgoApplication {
api_versions: vec![],
namespace: None,
},
path: "".to_string(),
},
sync_policy: SyncPolicy {
automated: Automated {
@ -138,10 +137,10 @@ impl From<CDApplicationConfig> for ArgoApplication {
namespace: Some(value.namespace),
project: "default".to_string(),
source: Source {
repo_url: Url::parse(value.helm_chart_repo_url.to_string().as_str())
.expect("couldn't convert to URL"),
repo_url: value.helm_chart_repo_url,
target_revision: Some(value.version.to_string()),
chart: value.helm_chart_name,
chart: value.helm_chart_name.clone(),
path: value.helm_chart_name,
helm: Helm {
pass_credentials: None,
parameters: vec![],
@ -218,7 +217,7 @@ spec:
let mut yaml_value: Value =
serde_yaml::from_str(yaml_str.as_str()).expect("couldn't parse string to YAML");
let mut spec = yaml_value
let spec = yaml_value
.get_mut("spec")
.expect("couldn't get spec from yaml")
.as_mapping_mut()
@ -271,7 +270,7 @@ mod tests {
namespace: Some("test-ns".to_string()),
project: "test-project".to_string(),
source: Source {
repo_url: Url::parse("http://test").unwrap(),
repo_url: "http://test".to_string(),
target_revision: None,
chart: "test-chart".to_string(),
helm: Helm {
@ -290,6 +289,7 @@ mod tests {
api_versions: vec![],
namespace: None,
},
path: "".to_string(),
},
sync_policy: SyncPolicy {
automated: Automated {

View File

@ -9,12 +9,9 @@ use crate::{
config::HARMONY_DATA_DIR,
data::Version,
inventory::Inventory,
modules::{
application::{
Application, ApplicationFeature, HelmPackage, OCICompliant,
features::{ArgoApplication, ArgoHelmScore},
},
helm::chart::HelmChartScore,
modules::application::{
Application, ApplicationFeature, HelmPackage, OCICompliant,
features::{ArgoApplication, ArgoHelmScore},
},
score::Score,
topology::{DeploymentTarget, HelmCommand, K8sclient, MultiTargetTopology, Topology, Url},
@ -162,7 +159,7 @@ impl<
info!("Pushed new helm chart {helm_chart}");
error!("TODO Make building image configurable/skippable");
let image = self.application.build_push_oci_image().await?;
// let image = self.application.build_push_oci_image().await?;
info!("Pushed new docker image {image}");
info!("Installing ContinuousDelivery feature");
@ -188,12 +185,12 @@ impl<
info!("Deploying to target {target:?}");
let score = ArgoHelmScore {
namespace: "harmonydemo-staging".to_string(),
openshift: true,
openshift: false,
domain: "argo.harmonydemo.apps.st.mcd".to_string(),
argo_apps: vec![ArgoApplication::from(CDApplicationConfig {
// helm pull oci://hub.nationtech.io/harmony/harmony-example-rust-webapp-chart/harmony-example-rust-webapp-chart --version 0.1.0
// helm pull oci://hub.nationtech.io/harmony/harmony-example-rust-webapp-chart --version 0.1.0
version: Version::from("0.1.0").unwrap(),
helm_chart_repo_url: Url::Url(url::Url::parse("oci://hub.nationtech.io/harmony/harmony-example-rust-webapp-chart/harmony-example-rust-webapp-chart").unwrap()),
helm_chart_repo_url: "hub.nationtech.io/harmony".to_string(),
helm_chart_name: "harmony-example-rust-webapp-chart".to_string(),
values_overrides: None,
name: "harmony-demo-rust-webapp".to_string(),
@ -225,7 +222,7 @@ impl<
/// more CD systems
pub struct CDApplicationConfig {
pub version: Version,
pub helm_chart_repo_url: Url,
pub helm_chart_repo_url: String,
pub helm_chart_name: String,
pub values_overrides: Option<Value>,
pub name: String,

View File

@ -1,5 +1,4 @@
use async_trait::async_trait;
use k8s_openapi::Resource;
use log::error;
use non_blank_string_rs::NonBlankString;
use serde::Serialize;
@ -647,7 +646,7 @@ server:
# Argo CD server ingress configuration
ingress:
# -- Enable an ingress resource for the Argo CD server
enabled: false
enabled: true
# -- Specific implementation for ingress controller. One of `generic`, `aws` or `gke`
## Additional configuration might be required in related configuration sections
controller: generic

View File

@ -4,13 +4,10 @@ use log::info;
use crate::{
inventory::Inventory,
modules::{
application::{Application, ApplicationFeature},
application::ApplicationFeature,
monitoring::{
application_monitoring::k8s_application_monitoring_score::ApplicationPrometheusMonitoringScore,
kube_prometheus::{
helm_prometheus_alert_score::HelmPrometheusAlertingScore,
types::{NamespaceSelector, ServiceMonitor},
},
kube_prometheus::types::{NamespaceSelector, ServiceMonitor},
},
},
score::Score,

View File

@ -416,7 +416,7 @@ ingress:
Expand the name of the chart.
*/}}
{{- define "chart.name" -}}
{{- default .Chart.Name .Values.nameOverride | trunc 63 | trimSuffix "-" }}
{{- default .Chart.Name $.Values.nameOverride | trunc 63 | trimSuffix "-" }}
{{- end }}
{{/*
@ -424,7 +424,7 @@ Create a default fully qualified app name.
We truncate at 63 chars because some Kubernetes name fields are limited to this (by the DNS naming spec).
*/}}
{{- define "chart.fullname" -}}
{{- $name := default .Chart.Name .Values.nameOverride }}
{{- $name := default .Chart.Name $.Values.nameOverride }}
{{- printf "%s-%s" .Release.Name $name | trunc 63 | trimSuffix "-" }}
{{- end }}
"#;
@ -437,12 +437,12 @@ kind: Service
metadata:
name: {{ include "chart.fullname" . }}
spec:
type: {{ .Values.service.type }}
type: {{ $.Values.service.type }}
ports:
- port: {{ .Values.service.port }}
targetPort: 3000
- name: main
port: {{ $.Values.service.port | default 3000 }}
targetPort: {{ $.Values.service.port | default 3000 }}
protocol: TCP
name: http
selector:
app: {{ include "chart.name" . }}
"#;
@ -455,7 +455,7 @@ kind: Deployment
metadata:
Review

Thanks for fixing my demo hardcoding crap 😅

Thanks for fixing my demo hardcoding crap 😅
name: {{ include "chart.fullname" . }}
spec:
replicas: {{ .Values.replicaCount }}
replicas: {{ $.Values.replicaCount }}
selector:
matchLabels:
app: {{ include "chart.name" . }}
@ -466,28 +466,28 @@ spec:
spec:
containers:
- name: {{ .Chart.Name }}
image: "{{ .Values.image.repository }}:{{ .Values.image.tag | default .Chart.AppVersion }}"
imagePullPolicy: {{ .Values.image.pullPolicy }}
image: "{{ $.Values.image.repository }}:{{ $.Values.image.tag | default .Chart.AppVersion }}"
imagePullPolicy: {{ $.Values.image.pullPolicy }}
ports:
- name: http
containerPort: 3000
- name: main
containerPort: {{ $.Values.service.port | default 3000 }}
protocol: TCP
"#;
fs::write(templates_dir.join("deployment.yaml"), deployment_yaml)?;
// Create templates/ingress.yaml
let ingress_yaml = r#"
{{- if .Values.ingress.enabled -}}
{{- if $.Values.ingress.enabled -}}
apiVersion: networking.k8s.io/v1
kind: Ingress
metadata:
name: {{ include "chart.fullname" . }}
annotations:
{{- toYaml .Values.ingress.annotations | nindent 4 }}
{{- toYaml $.Values.ingress.annotations | nindent 4 }}
spec:
{{- if .Values.ingress.tls }}
{{- if $.Values.ingress.tls }}
tls:
{{- range .Values.ingress.tls }}
{{- range $.Values.ingress.tls }}
- hosts:
{{- range .hosts }}
- {{ . | quote }}
@ -496,7 +496,7 @@ spec:
{{- end }}
{{- end }}
rules:
{{- range .Values.ingress.hosts }}
{{- range $.Values.ingress.hosts }}
- host: {{ .host | quote }}
http:
paths:
@ -507,7 +507,7 @@ spec:
service:
name: {{ include "chart.fullname" $ }}
port:
number: 3000
number: {{ $.Values.service.port | default 3000 }}
{{- end }}
{{- end }}
{{- end }}