diff --git a/.gitignore b/.gitignore index 7b1e9f6..f1dcd87 100644 --- a/.gitignore +++ b/.gitignore @@ -2,3 +2,7 @@ target private_repos log/ *.tgz +examples/rust/examples/rust/webapp/helm/ +examples/rust/examples/rust/webapp/Dockerfile.harmony +examples/rust/webapp/helm/harmony-example-rust-webapp-chart/ +.gitignore diff --git a/Cargo.lock b/Cargo.lock index 9481478..3b0347e 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -1726,6 +1726,7 @@ name = "harmony" version = "0.1.0" dependencies = [ "async-trait", + "bollard", "chrono", "cidr", "convert_case", diff --git a/Cargo.toml b/Cargo.toml index c30f10c..da7f713 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -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" diff --git a/examples/rust/src/main.rs b/examples/rust/src/main.rs index c44ce88..954ca8f 100644 --- a/examples/rust/src/main.rs +++ b/examples/rust/src/main.rs @@ -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, + 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(); } diff --git a/harmony/Cargo.toml b/harmony/Cargo.toml index ec2e2fa..2ffdd04 100644 --- a/harmony/Cargo.toml +++ b/harmony/Cargo.toml @@ -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 diff --git a/harmony/src/modules/application/features/argo_types.rs b/harmony/src/modules/application/features/argo_types.rs index 197742a..eb42579 100644 --- a/harmony/src/modules/application/features/argo_types.rs +++ b/harmony/src/modules/application/features/argo_types.rs @@ -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, 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 { - 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 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 { diff --git a/harmony/src/modules/application/features/continuous_delivery.rs b/harmony/src/modules/application/features/continuous_delivery.rs index 8396124..785e169 100644 --- a/harmony/src/modules/application/features/continuous_delivery.rs +++ b/harmony/src/modules/application/features/continuous_delivery.rs @@ -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, pub name: String, diff --git a/harmony/src/modules/application/features/helm_argocd_score.rs b/harmony/src/modules/application/features/helm_argocd_score.rs index d5dbd44..b87b463 100644 --- a/harmony/src/modules/application/features/helm_argocd_score.rs +++ b/harmony/src/modules/application/features/helm_argocd_score.rs @@ -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 diff --git a/harmony/src/modules/application/features/monitoring.rs b/harmony/src/modules/application/features/monitoring.rs index 0a8d421..5ca59c0 100644 --- a/harmony/src/modules/application/features/monitoring.rs +++ b/harmony/src/modules/application/features/monitoring.rs @@ -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, diff --git a/harmony/src/modules/application/rust.rs b/harmony/src/modules/application/rust.rs index 401c01f..ac684b8 100644 --- a/harmony/src/modules/application/rust.rs +++ b/harmony/src/modules/application/rust.rs @@ -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: 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 }}