Compare commits
54 Commits
Author | SHA1 | Date | |
---|---|---|---|
1af78da2a2 | |||
5ca9997068 | |||
8ae66f1469 | |||
84a2a07173 | |||
634dcda630 | |||
f5013373ec | |||
f08acd5bab | |||
2724e43237 | |||
82173faf0f | |||
eaa51611d9 | |||
fde0c09a1b | |||
4457b6fc02 | |||
38e5919ea0 | |||
42219228ed | |||
e19bea34c3 | |||
19390304d8 | |||
16a1b5eb50 | |||
e5db5d8a0f | |||
dfc4a4d0b6 | |||
35d3168ae2 | |||
ad9ca49f7c | |||
bb6331c5e5 | |||
36aea1c05c | |||
c08dcb049d | |||
284648fe79 | |||
77c6c96fb0 | |||
b8fde5644c | |||
8f806474d1 | |||
252d0852ae | |||
c2b13dab9f | |||
b24056354f | |||
f24f2706ae | |||
c609dc4dd5 | |||
57137daa4e | |||
2f74098afe | |||
616d23c550 | |||
4bbca20797 | |||
6bb28cbfe3 | |||
c4c71766d9 | |||
22efa0fbe6 | |||
984d1fd259 | |||
74569c3b15 | |||
22527aadc6 | |||
d9ce9748b4 | |||
c76601a695 | |||
cdae730c6b | |||
b980ac949e | |||
6e8e63e5ee | |||
3e06479cda | |||
52007c82e0 | |||
1aa8562509 | |||
545439923f | |||
843139591c | |||
af57ef6cc8 |
3559
Cargo.lock
generated
3559
Cargo.lock
generated
File diff suppressed because it is too large
Load Diff
@ -11,5 +11,7 @@ tracing-subscriber = { version = "0.3.18" }
|
|||||||
clap = { version = "4", features = ["derive", "env"] }
|
clap = { version = "4", features = ["derive", "env"] }
|
||||||
dotenv = { version = "0.15" }
|
dotenv = { version = "0.15" }
|
||||||
|
|
||||||
|
flux-releaser = { git = "https://git.front.kjuulh.io/kjuulh/flux-releaser", branch = "main" }
|
||||||
|
|
||||||
[workspace.package]
|
[workspace.package]
|
||||||
version = "0.1.1"
|
version = "0.1.1"
|
||||||
|
@ -17,9 +17,11 @@ uuid = { version = "1.7.0", features = ["v4"] }
|
|||||||
serde_yaml = "0.9.34"
|
serde_yaml = "0.9.34"
|
||||||
tokio-stream = { version = "0.1.15", features = ["full"] }
|
tokio-stream = { version = "0.1.15", features = ["full"] }
|
||||||
walkdir = "2.5.0"
|
walkdir = "2.5.0"
|
||||||
minijinja = "2.0.1"
|
minijinja = { version = "2.0.1", features = ["custom_syntax"] }
|
||||||
futures = "0.3.30"
|
futures = "0.3.30"
|
||||||
|
|
||||||
|
flux-releaser.workspace = true
|
||||||
|
|
||||||
[[test]]
|
[[test]]
|
||||||
name = "integration"
|
name = "integration"
|
||||||
path = "tests/tests.rs"
|
path = "tests/tests.rs"
|
||||||
|
@ -1,3 +1,5 @@
|
|||||||
pub mod cluster_vars;
|
pub mod cluster_vars;
|
||||||
|
pub mod crdb_database;
|
||||||
pub mod cuddle_vars;
|
pub mod cuddle_vars;
|
||||||
|
pub mod ingress;
|
||||||
pub mod vault_secret;
|
pub mod vault_secret;
|
||||||
|
131
crates/cuddle-clusters/src/catalog/crdb_database.rs
Normal file
131
crates/cuddle-clusters/src/catalog/crdb_database.rs
Normal file
@ -0,0 +1,131 @@
|
|||||||
|
use std::path::Path;
|
||||||
|
|
||||||
|
use minijinja::{value::Object, Value};
|
||||||
|
|
||||||
|
use crate::{catalog::cuddle_vars::CuddleVariable, Component};
|
||||||
|
|
||||||
|
use super::cuddle_vars::{load_cuddle_file, CuddleVariables};
|
||||||
|
|
||||||
|
pub struct CockroachDB {
|
||||||
|
variables: CuddleVariables,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl CockroachDB {
|
||||||
|
pub async fn new(path: &Path) -> anyhow::Result<Self> {
|
||||||
|
let variables = load_cuddle_file(path).await?;
|
||||||
|
|
||||||
|
Ok(Self { variables })
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Component for CockroachDB {
|
||||||
|
fn name(&self) -> String {
|
||||||
|
"cuddle/crdb".into()
|
||||||
|
}
|
||||||
|
|
||||||
|
fn render_value(
|
||||||
|
&self,
|
||||||
|
_environment: &str,
|
||||||
|
_value: &serde_yaml::Value,
|
||||||
|
) -> Option<anyhow::Result<minijinja::Value>> {
|
||||||
|
if let Some(true) = self
|
||||||
|
.variables
|
||||||
|
.0
|
||||||
|
.get("database")
|
||||||
|
.and_then(|v| match v {
|
||||||
|
CuddleVariable::Object(o) => Some(o),
|
||||||
|
_ => None,
|
||||||
|
})
|
||||||
|
.and_then(|o| o.0.get("crdb"))
|
||||||
|
.and_then(|o| match o {
|
||||||
|
CuddleVariable::String(o) => {
|
||||||
|
if o == "true" {
|
||||||
|
Some(true)
|
||||||
|
} else {
|
||||||
|
None
|
||||||
|
}
|
||||||
|
}
|
||||||
|
_ => None,
|
||||||
|
})
|
||||||
|
{
|
||||||
|
return Some(Ok(minijinja::Value::from_object(CockroachDBValues {
|
||||||
|
name: self.name(),
|
||||||
|
enabled: true,
|
||||||
|
})));
|
||||||
|
}
|
||||||
|
|
||||||
|
Some(Ok(minijinja::Value::from_object(CockroachDBValues {
|
||||||
|
name: self.name(),
|
||||||
|
enabled: false,
|
||||||
|
})))
|
||||||
|
}
|
||||||
|
|
||||||
|
fn render(
|
||||||
|
&self,
|
||||||
|
_environment: &str,
|
||||||
|
_value: &serde_yaml::Value,
|
||||||
|
) -> Option<anyhow::Result<(String, String)>> {
|
||||||
|
if let Some(true) = self
|
||||||
|
.variables
|
||||||
|
.0
|
||||||
|
.get("database")
|
||||||
|
.and_then(|v| match v {
|
||||||
|
CuddleVariable::Object(o) => Some(o),
|
||||||
|
_ => None,
|
||||||
|
})
|
||||||
|
.and_then(|o| o.0.get("crdb"))
|
||||||
|
.and_then(|o| match o {
|
||||||
|
CuddleVariable::String(o) => {
|
||||||
|
if o == "true" {
|
||||||
|
Some(true)
|
||||||
|
} else {
|
||||||
|
None
|
||||||
|
}
|
||||||
|
}
|
||||||
|
_ => None,
|
||||||
|
})
|
||||||
|
{
|
||||||
|
return Some(Ok((
|
||||||
|
format!("{}.yaml", self.name().replace("/", "-")),
|
||||||
|
r#"
|
||||||
|
apiVersion: v1
|
||||||
|
kind: ConfigMap
|
||||||
|
metadata:
|
||||||
|
name: {{ vars.cuddle_crdb.file_name(vars.cuddle_vars.service) }}
|
||||||
|
namespace: {{ vars.cluster_vars.namespace }}
|
||||||
|
data:
|
||||||
|
DATABASE_URL: postgresql://root@{{environment}}-cluster:26257/{{ vars.cuddle_vars.service | replace("-", "_") }}
|
||||||
|
"#
|
||||||
|
.into(),
|
||||||
|
)));
|
||||||
|
}
|
||||||
|
|
||||||
|
None
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Debug)]
|
||||||
|
struct CockroachDBValues {
|
||||||
|
name: String,
|
||||||
|
enabled: bool,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Object for CockroachDBValues {
|
||||||
|
fn get_value(self: &std::sync::Arc<Self>, key: &minijinja::Value) -> Option<minijinja::Value> {
|
||||||
|
let name = self.name.clone();
|
||||||
|
match key.as_str()? {
|
||||||
|
"has_values" => {
|
||||||
|
if self.enabled {
|
||||||
|
Some(minijinja::Value::from_serialize(true))
|
||||||
|
} else {
|
||||||
|
Some(minijinja::Value::from_serialize(false))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
"file_name" => Some(Value::from_function(move |file_name: String| {
|
||||||
|
format!("{}-{}", file_name, name.replace("/", "-"))
|
||||||
|
})),
|
||||||
|
"env" => Some(Value::from_serialize("DATABASE_URL")),
|
||||||
|
_ => None,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
@ -84,7 +84,7 @@ pub struct CuddleVars {
|
|||||||
pub variables: CuddleVariables,
|
pub variables: CuddleVariables,
|
||||||
}
|
}
|
||||||
|
|
||||||
const PARENT_PLAN_PREFIX: &str = ".cuddle/plan";
|
const PARENT_PLAN_PREFIX: &str = ".cuddle/base";
|
||||||
const CUDDLE_FILE: &str = "cuddle.yaml";
|
const CUDDLE_FILE: &str = "cuddle.yaml";
|
||||||
|
|
||||||
impl CuddleVars {
|
impl CuddleVars {
|
||||||
@ -95,7 +95,7 @@ impl CuddleVars {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn load_cuddle_file(path: &Path) -> BoxFuture<'static, anyhow::Result<CuddleVariables>> {
|
pub fn load_cuddle_file(path: &Path) -> BoxFuture<'static, anyhow::Result<CuddleVariables>> {
|
||||||
let path = path.to_path_buf();
|
let path = path.to_path_buf();
|
||||||
|
|
||||||
async move {
|
async move {
|
||||||
|
172
crates/cuddle-clusters/src/catalog/ingress.rs
Normal file
172
crates/cuddle-clusters/src/catalog/ingress.rs
Normal file
@ -0,0 +1,172 @@
|
|||||||
|
use std::path::Path;
|
||||||
|
|
||||||
|
use minijinja::{context, syntax::SyntaxConfig};
|
||||||
|
|
||||||
|
use crate::Component;
|
||||||
|
|
||||||
|
use super::cuddle_vars::{load_cuddle_file, CuddleVariable, CuddleVariables};
|
||||||
|
|
||||||
|
pub enum IngressType {
|
||||||
|
External,
|
||||||
|
Internal,
|
||||||
|
ExternalGrpc,
|
||||||
|
InternalGrpc,
|
||||||
|
}
|
||||||
|
|
||||||
|
pub struct Ingress {
|
||||||
|
variables: CuddleVariables,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Ingress {
|
||||||
|
pub async fn new(path: &Path) -> anyhow::Result<Self> {
|
||||||
|
let variables = load_cuddle_file(path).await?;
|
||||||
|
|
||||||
|
Ok(Self { variables })
|
||||||
|
}
|
||||||
|
|
||||||
|
fn render_ingress_types(
|
||||||
|
&self,
|
||||||
|
ingress_types: Vec<IngressType>,
|
||||||
|
) -> anyhow::Result<(String, String)> {
|
||||||
|
let mut templates = Vec::new();
|
||||||
|
|
||||||
|
let internal_template = r#"
|
||||||
|
{%- set service_name = vars.cuddle_vars.service %}
|
||||||
|
{%- set host_name = vars.cuddle_vars.service | replace("_", "-") | replace(".", "-") %}
|
||||||
|
<%- macro host() -%>
|
||||||
|
<% if connection_type is defined %><<connection_type>>.<% endif %>{{ host_name }}.{{ environment }}.<< base_host >>
|
||||||
|
<%- endmacro %>
|
||||||
|
|
||||||
|
<%- macro k8s_service() -%>
|
||||||
|
<%- if connection_type == "grpc" -%>
|
||||||
|
{{ service_name }}-grpc
|
||||||
|
<%- else -%>
|
||||||
|
{{ service_name }}
|
||||||
|
<%- endif -%>
|
||||||
|
<%- endmacro %>
|
||||||
|
|
||||||
|
---
|
||||||
|
apiVersion: networking.k8s.io/v1
|
||||||
|
kind: Ingress
|
||||||
|
metadata:
|
||||||
|
annotations:
|
||||||
|
cert-manager.io/issuer: << issuer >>
|
||||||
|
traefik.ingress.kubernetes.io/router.entrypoints: web
|
||||||
|
traefik.ingress.kubernetes.io/router.tls: "true"
|
||||||
|
labels:
|
||||||
|
app: {{ service_name }}
|
||||||
|
cluster: {{ vars.cluster_vars.name }}
|
||||||
|
name: {{ service_name }}-<< name >>
|
||||||
|
namespace: {{ vars.cluster_vars.namespace }}
|
||||||
|
spec:
|
||||||
|
rules:
|
||||||
|
- host: << host() >>
|
||||||
|
http:
|
||||||
|
paths:
|
||||||
|
- backend:
|
||||||
|
service:
|
||||||
|
name: << k8s_service() >>
|
||||||
|
port:
|
||||||
|
name: << name >>
|
||||||
|
path: /
|
||||||
|
pathType: Prefix
|
||||||
|
tls:
|
||||||
|
- hosts:
|
||||||
|
- << host() >>
|
||||||
|
secretName: tls-{{ service_name }}-<< issuer >>-<< name >>-ingress-dns
|
||||||
|
"#;
|
||||||
|
|
||||||
|
let get_template = |name, base_host, connection_type| {
|
||||||
|
let mut env = minijinja::Environment::new();
|
||||||
|
env.set_syntax(
|
||||||
|
SyntaxConfig::builder()
|
||||||
|
.block_delimiters("<%", "%>")
|
||||||
|
.variable_delimiters("<<", ">>")
|
||||||
|
.comment_delimiters("<#", "#>")
|
||||||
|
.build()
|
||||||
|
.expect("to be able to build minijinja syntax"),
|
||||||
|
);
|
||||||
|
|
||||||
|
env.add_global("name", name);
|
||||||
|
env.add_global("base_host", base_host);
|
||||||
|
if let Some(connection_type) = connection_type {
|
||||||
|
env.add_global("connection_type", connection_type);
|
||||||
|
}
|
||||||
|
|
||||||
|
env.add_global("issuer", "kjuulh-app");
|
||||||
|
|
||||||
|
env.render_named_str("ingress.yaml", internal_template, context! {})
|
||||||
|
};
|
||||||
|
|
||||||
|
for ingress_type in ingress_types {
|
||||||
|
match ingress_type {
|
||||||
|
IngressType::External => {
|
||||||
|
templates.push(get_template("external-http", "kjuulh.app", None)?)
|
||||||
|
}
|
||||||
|
IngressType::Internal => {
|
||||||
|
templates.push(get_template("internal-http", "internal.kjuulh.app", None)?)
|
||||||
|
}
|
||||||
|
IngressType::ExternalGrpc => {
|
||||||
|
templates.push(get_template("external-grpc", "kjuulh.app", Some("grpc"))?)
|
||||||
|
}
|
||||||
|
IngressType::InternalGrpc => templates.push(get_template(
|
||||||
|
"internal-grpc",
|
||||||
|
"internal.kjuulh.app",
|
||||||
|
Some("grpc"),
|
||||||
|
)?),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
Ok(("ingress.yaml".into(), templates.join("\n")))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Component for Ingress {
|
||||||
|
fn name(&self) -> String {
|
||||||
|
"cuddle/ingress".to_string()
|
||||||
|
}
|
||||||
|
|
||||||
|
fn render(
|
||||||
|
&self,
|
||||||
|
_environment: &str,
|
||||||
|
_value: &serde_yaml::Value,
|
||||||
|
) -> Option<anyhow::Result<(String, String)>> {
|
||||||
|
if let Some(ingress_types) = self
|
||||||
|
.variables
|
||||||
|
.0
|
||||||
|
.get("ingress")
|
||||||
|
.and_then(|v| match v {
|
||||||
|
CuddleVariable::Array(a) => Some(a),
|
||||||
|
_ => None,
|
||||||
|
})
|
||||||
|
.map(|o| {
|
||||||
|
let mut types = Vec::new();
|
||||||
|
|
||||||
|
for value in o {
|
||||||
|
match value {
|
||||||
|
CuddleVariable::Object(o) => {
|
||||||
|
if o.0.contains_key("external") {
|
||||||
|
types.push(IngressType::External)
|
||||||
|
} else if o.0.contains_key("internal") {
|
||||||
|
types.push(IngressType::Internal)
|
||||||
|
} else if o.0.contains_key("external_grpc") {
|
||||||
|
types.push(IngressType::ExternalGrpc)
|
||||||
|
} else if o.0.contains_key("internal_grpc") {
|
||||||
|
types.push(IngressType::InternalGrpc)
|
||||||
|
} else {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
_ => continue,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
types
|
||||||
|
})
|
||||||
|
{
|
||||||
|
return Some(self.render_ingress_types(ingress_types));
|
||||||
|
}
|
||||||
|
|
||||||
|
None
|
||||||
|
}
|
||||||
|
}
|
@ -68,15 +68,40 @@ impl Component for VaultSecret {
|
|||||||
return Some(Ok(minijinja::Value::from_object(vault_values)));
|
return Some(Ok(minijinja::Value::from_object(vault_values)));
|
||||||
}
|
}
|
||||||
|
|
||||||
None
|
Some(Ok(minijinja::Value::from_object(VaultSecretValues {
|
||||||
|
name: self.name().replace("/", "-"),
|
||||||
|
secrets: VaultSecretsLookup {
|
||||||
|
secrets: Vec::default(),
|
||||||
|
},
|
||||||
|
})))
|
||||||
}
|
}
|
||||||
|
|
||||||
fn render(
|
fn render(
|
||||||
&self,
|
&self,
|
||||||
_environment: &str,
|
_environment: &str,
|
||||||
_value: &serde_yaml::Value,
|
value: &serde_yaml::Value,
|
||||||
) -> Option<anyhow::Result<(String, String)>> {
|
) -> Option<anyhow::Result<(String, String)>> {
|
||||||
Some(Ok((
|
value
|
||||||
|
.as_mapping()
|
||||||
|
.and_then(|map| map.get("env"))
|
||||||
|
.and_then(|v| v.as_mapping())
|
||||||
|
.map(|v| {
|
||||||
|
v.iter()
|
||||||
|
.filter_map(|(k, v)| {
|
||||||
|
if v.as_mapping()
|
||||||
|
.map(|m| m.get("vault").filter(|v| v.as_bool() == Some(true)))
|
||||||
|
.is_some()
|
||||||
|
{
|
||||||
|
Some(k)
|
||||||
|
} else {
|
||||||
|
None
|
||||||
|
}
|
||||||
|
})
|
||||||
|
.filter_map(|k| k.as_str())
|
||||||
|
.collect::<Vec<_>>()
|
||||||
|
})
|
||||||
|
.map(|_| {
|
||||||
|
Ok((
|
||||||
format!("{}.yaml", self.name().replace("/", "_")),
|
format!("{}.yaml", self.name().replace("/", "_")),
|
||||||
r#"apiVersion: secrets.hashicorp.com/v1beta1
|
r#"apiVersion: secrets.hashicorp.com/v1beta1
|
||||||
kind: VaultStaticSecret
|
kind: VaultStaticSecret
|
||||||
@ -93,7 +118,8 @@ spec:
|
|||||||
type: kv-v2
|
type: kv-v2
|
||||||
"#
|
"#
|
||||||
.into(),
|
.into(),
|
||||||
)))
|
))
|
||||||
|
})
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1,4 +1,4 @@
|
|||||||
use std::rc::Rc;
|
use std::sync::Arc;
|
||||||
|
|
||||||
pub trait Component {
|
pub trait Component {
|
||||||
fn name(&self) -> String;
|
fn name(&self) -> String;
|
||||||
@ -27,11 +27,11 @@ pub trait Component {
|
|||||||
|
|
||||||
#[derive(Clone)]
|
#[derive(Clone)]
|
||||||
pub struct ConcreteComponent {
|
pub struct ConcreteComponent {
|
||||||
inner: Rc<dyn Component + 'static>,
|
inner: Arc<dyn Component + Sync + Send + 'static>,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl std::ops::Deref for ConcreteComponent {
|
impl std::ops::Deref for ConcreteComponent {
|
||||||
type Target = Rc<dyn Component + 'static>;
|
type Target = Arc<dyn Component + Sync + Send + 'static>;
|
||||||
|
|
||||||
fn deref(&self) -> &Self::Target {
|
fn deref(&self) -> &Self::Target {
|
||||||
&self.inner
|
&self.inner
|
||||||
@ -39,8 +39,8 @@ impl std::ops::Deref for ConcreteComponent {
|
|||||||
}
|
}
|
||||||
|
|
||||||
impl ConcreteComponent {
|
impl ConcreteComponent {
|
||||||
pub fn new<T: Component + 'static>(t: T) -> Self {
|
pub fn new<T: Component + Sync + Send + 'static>(t: T) -> Self {
|
||||||
Self { inner: Rc::new(t) }
|
Self { inner: Arc::new(t) }
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -54,7 +54,7 @@ impl IntoComponent for ConcreteComponent {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<T: Component + 'static> IntoComponent for T {
|
impl<T: Component + Sync + Send + 'static> IntoComponent for T {
|
||||||
fn into_component(self) -> ConcreteComponent {
|
fn into_component(self) -> ConcreteComponent {
|
||||||
ConcreteComponent::new(self)
|
ConcreteComponent::new(self)
|
||||||
}
|
}
|
||||||
|
@ -7,3 +7,5 @@ pub mod catalog;
|
|||||||
|
|
||||||
pub mod process;
|
pub mod process;
|
||||||
pub use process::{process, process_opts};
|
pub use process::{process, process_opts};
|
||||||
|
|
||||||
|
pub mod releaser;
|
||||||
|
@ -13,12 +13,32 @@ use tokio_stream::{wrappers::ReadDirStream, StreamExt};
|
|||||||
use crate::components::{ConcreteComponent, IntoComponent};
|
use crate::components::{ConcreteComponent, IntoComponent};
|
||||||
|
|
||||||
pub async fn process() -> anyhow::Result<()> {
|
pub async fn process() -> anyhow::Result<()> {
|
||||||
process_opts(Vec::<ConcreteComponent>::new(), ProcessOpts::default()).await
|
process_opts(
|
||||||
|
Vec::<ConcreteComponent>::new(),
|
||||||
|
ProcessOpts::default(),
|
||||||
|
None::<NoUploadStrategy>,
|
||||||
|
)
|
||||||
|
.await
|
||||||
|
}
|
||||||
|
|
||||||
|
pub trait UploadStrategy {
|
||||||
|
fn upload(&self, input_path: &Path) -> BoxFuture<'_, anyhow::Result<()>>;
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Default)]
|
||||||
|
pub struct NoUploadStrategy {}
|
||||||
|
|
||||||
|
impl UploadStrategy for NoUploadStrategy {
|
||||||
|
fn upload(&self, _input_path: &Path) -> BoxFuture<'_, anyhow::Result<()>> {
|
||||||
|
async move { Ok(()) }.boxed()
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub struct ProcessOpts {
|
pub struct ProcessOpts {
|
||||||
pub path: PathBuf,
|
pub path: PathBuf,
|
||||||
pub output: PathBuf,
|
pub output: PathBuf,
|
||||||
|
|
||||||
|
pub variables: HashMap<String, String>,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Default for ProcessOpts {
|
impl Default for ProcessOpts {
|
||||||
@ -29,16 +49,18 @@ impl Default for ProcessOpts {
|
|||||||
.expect("to be able to get current dir")
|
.expect("to be able to get current dir")
|
||||||
.join("cuddle-clusters")
|
.join("cuddle-clusters")
|
||||||
.join("k8s"),
|
.join("k8s"),
|
||||||
|
variables: HashMap::default(),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
const TEMPLATES_PATH_PREFIX: &str = "templates/clusters";
|
const TEMPLATES_PATH_PREFIX: &str = "templates/clusters";
|
||||||
const CUDDLE_PLAN_PATH_PREFIX: &str = ".cuddle/plan";
|
const CUDDLE_PLAN_PATH_PREFIX: &str = ".cuddle/base";
|
||||||
|
|
||||||
pub async fn process_opts(
|
pub async fn process_opts(
|
||||||
components: impl IntoIterator<Item = impl IntoComponent>,
|
components: impl IntoIterator<Item = impl IntoComponent>,
|
||||||
opts: ProcessOpts,
|
opts: ProcessOpts,
|
||||||
|
upload_strategy: Option<impl UploadStrategy>,
|
||||||
) -> anyhow::Result<()> {
|
) -> anyhow::Result<()> {
|
||||||
let components = components
|
let components = components
|
||||||
.into_iter()
|
.into_iter()
|
||||||
@ -61,10 +83,21 @@ pub async fn process_opts(
|
|||||||
let template_files = load_template_files(&path).await?;
|
let template_files = load_template_files(&path).await?;
|
||||||
tracing::debug!("found files: {:?}", template_files);
|
tracing::debug!("found files: {:?}", template_files);
|
||||||
|
|
||||||
tokio::fs::remove_dir_all(&opts.output).await?;
|
let _ = tokio::fs::remove_dir_all(&opts.output).await;
|
||||||
tokio::fs::create_dir_all(&opts.output).await?;
|
tokio::fs::create_dir_all(&opts.output).await?;
|
||||||
|
|
||||||
process_templates(&components, &clusters, &template_files, &opts.output).await?;
|
process_templates(
|
||||||
|
&components,
|
||||||
|
&clusters,
|
||||||
|
&template_files,
|
||||||
|
&opts.output,
|
||||||
|
&opts.variables,
|
||||||
|
)
|
||||||
|
.await?;
|
||||||
|
|
||||||
|
if let Some(upload_strategy) = upload_strategy {
|
||||||
|
upload_strategy.upload(&opts.output).await?;
|
||||||
|
}
|
||||||
|
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
@ -184,6 +217,7 @@ async fn process_templates(
|
|||||||
clusters: &CuddleClusters,
|
clusters: &CuddleClusters,
|
||||||
template_files: &TemplateFiles,
|
template_files: &TemplateFiles,
|
||||||
dest: &Path,
|
dest: &Path,
|
||||||
|
variables: &HashMap<String, String>,
|
||||||
) -> anyhow::Result<()> {
|
) -> anyhow::Result<()> {
|
||||||
for (environment, value) in clusters.iter() {
|
for (environment, value) in clusters.iter() {
|
||||||
process_cluster(
|
process_cluster(
|
||||||
@ -192,6 +226,7 @@ async fn process_templates(
|
|||||||
environment,
|
environment,
|
||||||
template_files,
|
template_files,
|
||||||
&dest.join(environment),
|
&dest.join(environment),
|
||||||
|
variables,
|
||||||
)
|
)
|
||||||
.await?;
|
.await?;
|
||||||
}
|
}
|
||||||
@ -205,9 +240,18 @@ async fn process_cluster(
|
|||||||
environment: &str,
|
environment: &str,
|
||||||
template_files: &TemplateFiles,
|
template_files: &TemplateFiles,
|
||||||
dest: &Path,
|
dest: &Path,
|
||||||
|
variables: &HashMap<String, String>,
|
||||||
) -> anyhow::Result<()> {
|
) -> anyhow::Result<()> {
|
||||||
for (_, template_file) in &template_files.templates {
|
for (_, template_file) in &template_files.templates {
|
||||||
process_template_file(components, value, environment, template_file, dest).await?;
|
process_template_file(
|
||||||
|
components,
|
||||||
|
value,
|
||||||
|
environment,
|
||||||
|
template_file,
|
||||||
|
dest,
|
||||||
|
variables,
|
||||||
|
)
|
||||||
|
.await?;
|
||||||
}
|
}
|
||||||
|
|
||||||
for (template_file_name, template_content) in components
|
for (template_file_name, template_content) in components
|
||||||
@ -228,6 +272,7 @@ async fn process_cluster(
|
|||||||
&template_file_name,
|
&template_file_name,
|
||||||
&template_content,
|
&template_content,
|
||||||
dest,
|
dest,
|
||||||
|
variables,
|
||||||
)
|
)
|
||||||
.await?;
|
.await?;
|
||||||
}
|
}
|
||||||
@ -245,6 +290,7 @@ async fn process_template_file(
|
|||||||
environment: &str,
|
environment: &str,
|
||||||
template_file: &PathBuf,
|
template_file: &PathBuf,
|
||||||
dest: &Path,
|
dest: &Path,
|
||||||
|
variables: &HashMap<String, String>,
|
||||||
) -> anyhow::Result<()> {
|
) -> anyhow::Result<()> {
|
||||||
let file = tokio::fs::read_to_string(template_file)
|
let file = tokio::fs::read_to_string(template_file)
|
||||||
.await
|
.await
|
||||||
@ -260,6 +306,7 @@ async fn process_template_file(
|
|||||||
&file_name.to_string_lossy(),
|
&file_name.to_string_lossy(),
|
||||||
&file,
|
&file,
|
||||||
dest,
|
dest,
|
||||||
|
variables,
|
||||||
)
|
)
|
||||||
.await?;
|
.await?;
|
||||||
|
|
||||||
@ -273,6 +320,7 @@ async fn process_render_template(
|
|||||||
file_name: &str,
|
file_name: &str,
|
||||||
file_content: &str,
|
file_content: &str,
|
||||||
dest: &Path,
|
dest: &Path,
|
||||||
|
user_vars: &HashMap<String, String>,
|
||||||
) -> anyhow::Result<()> {
|
) -> anyhow::Result<()> {
|
||||||
if !dest.exists() {
|
if !dest.exists() {
|
||||||
tokio::fs::create_dir_all(dest).await?;
|
tokio::fs::create_dir_all(dest).await?;
|
||||||
@ -286,6 +334,7 @@ async fn process_render_template(
|
|||||||
env.add_global("environment", environment);
|
env.add_global("environment", environment);
|
||||||
|
|
||||||
let mut variables = HashMap::new();
|
let mut variables = HashMap::new();
|
||||||
|
|
||||||
for component in components {
|
for component in components {
|
||||||
let name = component.name();
|
let name = component.name();
|
||||||
|
|
||||||
@ -295,6 +344,10 @@ async fn process_render_template(
|
|||||||
variables.insert(name.replace("/", "_"), value);
|
variables.insert(name.replace("/", "_"), value);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
variables.insert(
|
||||||
|
"user_vars".into(),
|
||||||
|
minijinja::Value::from_serialize(user_vars),
|
||||||
|
);
|
||||||
|
|
||||||
let tmpl = env.get_template(file_name)?;
|
let tmpl = env.get_template(file_name)?;
|
||||||
let rendered = tmpl.render(context! {
|
let rendered = tmpl.render(context! {
|
||||||
|
108
crates/cuddle-clusters/src/releaser.rs
Normal file
108
crates/cuddle-clusters/src/releaser.rs
Normal file
@ -0,0 +1,108 @@
|
|||||||
|
use std::{
|
||||||
|
path::{Path, PathBuf},
|
||||||
|
process::Command,
|
||||||
|
};
|
||||||
|
|
||||||
|
use anyhow::Context;
|
||||||
|
use flux_releaser::{
|
||||||
|
app::{LocalApp, SharedLocalApp},
|
||||||
|
services::flux_local_cluster::extensions::FluxLocalClusterManagerExt,
|
||||||
|
};
|
||||||
|
use futures::{future::BoxFuture, FutureExt};
|
||||||
|
|
||||||
|
use crate::process::UploadStrategy;
|
||||||
|
|
||||||
|
#[derive(Default, Clone)]
|
||||||
|
pub struct Releaser {
|
||||||
|
registry: String,
|
||||||
|
service: String,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Releaser {
|
||||||
|
pub fn with_registry(&mut self, registry: impl Into<String>) -> &mut Self {
|
||||||
|
self.registry = registry.into();
|
||||||
|
|
||||||
|
self
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn with_service(&mut self, service: impl Into<String>) -> &mut Self {
|
||||||
|
self.service = service.into();
|
||||||
|
|
||||||
|
self
|
||||||
|
}
|
||||||
|
|
||||||
|
pub async fn release(&self, input_path: impl Into<PathBuf>) -> anyhow::Result<()> {
|
||||||
|
let input_path = input_path.into();
|
||||||
|
let branch = self.get_branch()?.ok_or(anyhow::anyhow!(
|
||||||
|
"failed to find branch, required for triggering release"
|
||||||
|
))?;
|
||||||
|
|
||||||
|
tracing::trace!("triggering release for: {}", input_path.display());
|
||||||
|
|
||||||
|
let local_app =
|
||||||
|
SharedLocalApp::new(LocalApp::new(&self.registry).await?).flux_local_cluster_manager();
|
||||||
|
|
||||||
|
let upload_id = local_app
|
||||||
|
.package_clusters(input_path)
|
||||||
|
.await
|
||||||
|
.context("failed to package clusters")?;
|
||||||
|
|
||||||
|
local_app
|
||||||
|
.commit_artifact(&self.service, &branch, upload_id)
|
||||||
|
.await
|
||||||
|
.context("failed to commit artifact")?;
|
||||||
|
|
||||||
|
local_app
|
||||||
|
.trigger_release(&self.service, &branch)
|
||||||
|
.await
|
||||||
|
.context("failed to trigger release")?;
|
||||||
|
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn get_branch(&self) -> anyhow::Result<Option<String>> {
|
||||||
|
let output = Command::new("git")
|
||||||
|
.args(["rev-parse", "--abbrev-ref", "HEAD"])
|
||||||
|
.output()?;
|
||||||
|
|
||||||
|
if output.status.success() {
|
||||||
|
let branch = std::str::from_utf8(&output.stdout)?.trim().to_string();
|
||||||
|
Ok(Some(branch))
|
||||||
|
} else {
|
||||||
|
let err = std::str::from_utf8(&output.stderr)?;
|
||||||
|
anyhow::bail!("Failed to get branch name: {}", err)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl UploadStrategy for Releaser {
|
||||||
|
fn upload(&self, input_path: &Path) -> BoxFuture<'_, anyhow::Result<()>> {
|
||||||
|
let input_path = input_path.to_path_buf();
|
||||||
|
|
||||||
|
async move {
|
||||||
|
self.release(input_path).await?;
|
||||||
|
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
.boxed()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[cfg(test)]
|
||||||
|
mod test {
|
||||||
|
use crate::releaser::Releaser;
|
||||||
|
|
||||||
|
#[tokio::test]
|
||||||
|
async fn can_upload_test() -> anyhow::Result<()> {
|
||||||
|
return Ok(());
|
||||||
|
let releaser = Releaser::default();
|
||||||
|
|
||||||
|
releaser
|
||||||
|
.release(
|
||||||
|
"/home/kjuulh/git/git.front.kjuulh.io/kjuulh/cuddle-rust-service-plan/.cuddle/tmp/cuddle-clusters",
|
||||||
|
)
|
||||||
|
.await?;
|
||||||
|
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
}
|
@ -1,6 +1,9 @@
|
|||||||
use std::{cmp::Ordering, path::Path};
|
use std::{cmp::Ordering, collections::HashMap, path::Path};
|
||||||
|
|
||||||
use cuddle_clusters::{process::ProcessOpts, ConcreteComponent, IntoComponent};
|
use cuddle_clusters::{
|
||||||
|
process::{NoUploadStrategy, ProcessOpts},
|
||||||
|
ConcreteComponent, IntoComponent,
|
||||||
|
};
|
||||||
use walkdir::DirEntry;
|
use walkdir::DirEntry;
|
||||||
|
|
||||||
pub(crate) async fn run_test_with_components(
|
pub(crate) async fn run_test_with_components(
|
||||||
@ -27,7 +30,9 @@ pub(crate) async fn run_test_with_components(
|
|||||||
ProcessOpts {
|
ProcessOpts {
|
||||||
path: test_folder.clone(),
|
path: test_folder.clone(),
|
||||||
output: actual.clone(),
|
output: actual.clone(),
|
||||||
|
variables: HashMap::default(),
|
||||||
},
|
},
|
||||||
|
None::<NoUploadStrategy>,
|
||||||
)
|
)
|
||||||
.await?;
|
.await?;
|
||||||
|
|
||||||
@ -37,7 +42,9 @@ pub(crate) async fn run_test_with_components(
|
|||||||
ProcessOpts {
|
ProcessOpts {
|
||||||
path: test_folder,
|
path: test_folder,
|
||||||
output: expected.clone(),
|
output: expected.clone(),
|
||||||
|
variables: HashMap::default(),
|
||||||
},
|
},
|
||||||
|
None::<NoUploadStrategy>,
|
||||||
)
|
)
|
||||||
.await?;
|
.await?;
|
||||||
}
|
}
|
||||||
|
@ -4,7 +4,10 @@ mod can_run_for_env;
|
|||||||
mod cuddle_vars;
|
mod cuddle_vars;
|
||||||
|
|
||||||
use cuddle_clusters::{
|
use cuddle_clusters::{
|
||||||
catalog::{cluster_vars::ClusterVars, cuddle_vars::CuddleVars, vault_secret::VaultSecret},
|
catalog::{
|
||||||
|
cluster_vars::ClusterVars, crdb_database::CockroachDB, cuddle_vars::CuddleVars,
|
||||||
|
ingress::Ingress, vault_secret::VaultSecret,
|
||||||
|
},
|
||||||
IntoComponent,
|
IntoComponent,
|
||||||
};
|
};
|
||||||
|
|
||||||
@ -96,3 +99,38 @@ async fn with_vault_secrets() -> anyhow::Result<()> {
|
|||||||
|
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[tokio::test]
|
||||||
|
async fn with_crdb() -> anyhow::Result<()> {
|
||||||
|
let current_dir = std::env::current_dir()?.join("tests/with_crdb");
|
||||||
|
|
||||||
|
run_test_with_components(
|
||||||
|
"with_crdb",
|
||||||
|
vec![
|
||||||
|
CuddleVars::new(¤t_dir).await?.into_component(),
|
||||||
|
ClusterVars::default().into_component(),
|
||||||
|
VaultSecret::default().into_component(),
|
||||||
|
CockroachDB::new(¤t_dir).await?.into_component(),
|
||||||
|
],
|
||||||
|
)
|
||||||
|
.await?;
|
||||||
|
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|
||||||
|
#[tokio::test]
|
||||||
|
async fn with_ingress() -> anyhow::Result<()> {
|
||||||
|
let current_dir = std::env::current_dir()?.join("tests/with_ingress");
|
||||||
|
|
||||||
|
run_test_with_components(
|
||||||
|
"with_ingress",
|
||||||
|
vec![
|
||||||
|
CuddleVars::new(¤t_dir).await?.into_component(),
|
||||||
|
ClusterVars::default().into_component(),
|
||||||
|
Ingress::new(¤t_dir).await?.into_component(),
|
||||||
|
],
|
||||||
|
)
|
||||||
|
.await?;
|
||||||
|
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
@ -1,11 +0,0 @@
|
|||||||
apiVersion: v1
|
|
||||||
kind: ConfigMap
|
|
||||||
metadata:
|
|
||||||
name: {{ vars.cuddle_vars.service }}-config
|
|
||||||
data:
|
|
||||||
{%- if (vars.cluster_vars.env | items | length) > 0 %}
|
|
||||||
environment:
|
|
||||||
{%- for (name, value) in vars.cluster_vars.env | dictsort %}
|
|
||||||
{{name | upper | replace(".", "_") | replace("-", "_") }}: {{value}}
|
|
||||||
{%- endfor %}
|
|
||||||
{%- endif %}
|
|
7
crates/cuddle-clusters/tests/with_crdb/cuddle.yaml
Normal file
7
crates/cuddle-clusters/tests/with_crdb/cuddle.yaml
Normal file
@ -0,0 +1,7 @@
|
|||||||
|
vars:
|
||||||
|
service: service
|
||||||
|
database:
|
||||||
|
crdb: "true"
|
||||||
|
|
||||||
|
cuddle/clusters:
|
||||||
|
dev:
|
@ -0,0 +1,9 @@
|
|||||||
|
|
||||||
|
apiVersion: v1
|
||||||
|
kind: ConfigMap
|
||||||
|
metadata:
|
||||||
|
name: service-cuddle-crdb
|
||||||
|
namespace: dev
|
||||||
|
data:
|
||||||
|
DATABASE_URL: postgresql://root@dev-cluster:26257/service
|
||||||
|
|
@ -0,0 +1,39 @@
|
|||||||
|
apiVersion: apps/v1
|
||||||
|
kind: Deployment
|
||||||
|
metadata:
|
||||||
|
labels:
|
||||||
|
app: service
|
||||||
|
name: service
|
||||||
|
spec:
|
||||||
|
replicas: 3
|
||||||
|
selector:
|
||||||
|
matchLabels:
|
||||||
|
app: service
|
||||||
|
template:
|
||||||
|
metadata:
|
||||||
|
labels:
|
||||||
|
app: service
|
||||||
|
spec:
|
||||||
|
containers:
|
||||||
|
- args:
|
||||||
|
- serve
|
||||||
|
command:
|
||||||
|
- service
|
||||||
|
image: kasperhermansen/service:main-1715336504
|
||||||
|
name: service
|
||||||
|
envFrom:
|
||||||
|
- configMapRef:
|
||||||
|
name: service-config
|
||||||
|
env:
|
||||||
|
- name: DATABASE_URL
|
||||||
|
valueFrom:
|
||||||
|
secretKeyRef:
|
||||||
|
name: service-cuddle-crdb
|
||||||
|
key: DATABASE_URL
|
||||||
|
ports:
|
||||||
|
- containerPort: 3000
|
||||||
|
name: external-http
|
||||||
|
- containerPort: 3001
|
||||||
|
name: internal-http
|
||||||
|
- containerPort: 3002
|
||||||
|
name: internal-grpc
|
@ -0,0 +1,54 @@
|
|||||||
|
{%- set service_name = vars.cuddle_vars.service -%}
|
||||||
|
|
||||||
|
apiVersion: apps/v1
|
||||||
|
kind: Deployment
|
||||||
|
metadata:
|
||||||
|
labels:
|
||||||
|
app: {{ service_name }}
|
||||||
|
name: {{ service_name }}
|
||||||
|
spec:
|
||||||
|
replicas: 3
|
||||||
|
selector:
|
||||||
|
matchLabels:
|
||||||
|
app: {{ service_name }}
|
||||||
|
template:
|
||||||
|
metadata:
|
||||||
|
labels:
|
||||||
|
app: {{ service_name }}
|
||||||
|
spec:
|
||||||
|
containers:
|
||||||
|
- args:
|
||||||
|
- serve
|
||||||
|
command:
|
||||||
|
- {{ service_name }}
|
||||||
|
image: kasperhermansen/{{ service_name }}:main-1715336504
|
||||||
|
name: {{ service_name }}
|
||||||
|
envFrom:
|
||||||
|
- configMapRef:
|
||||||
|
name: {{service_name}}-config
|
||||||
|
{%- if vars.vault_secret.has_values or vars.cuddle_crdb.has_values %}
|
||||||
|
env:
|
||||||
|
{%- if vars.vault_secret.has_values %}
|
||||||
|
{%- for secret in vars.vault_secret.secrets %}
|
||||||
|
- name: {{secret | upper | replace(".", "_") | replace("-", "_") }}
|
||||||
|
valueFrom:
|
||||||
|
secretKeyRef:
|
||||||
|
name: {{ vars.vault_secret.file_name(service_name) }}
|
||||||
|
key: {{ secret }}
|
||||||
|
{%- endfor %}
|
||||||
|
{%- endif %}
|
||||||
|
{%- if vars.cuddle_crdb.has_values %}
|
||||||
|
- name: {{vars.cuddle_crdb.env }}
|
||||||
|
valueFrom:
|
||||||
|
secretKeyRef:
|
||||||
|
name: {{ vars.cuddle_crdb.file_name(service_name) }}
|
||||||
|
key: {{ vars.cuddle_crdb.env }}
|
||||||
|
{%- endif %}
|
||||||
|
{%- endif %}
|
||||||
|
ports:
|
||||||
|
- containerPort: 3000
|
||||||
|
name: external-http
|
||||||
|
- containerPort: 3001
|
||||||
|
name: internal-http
|
||||||
|
- containerPort: 3002
|
||||||
|
name: internal-grpc
|
11
crates/cuddle-clusters/tests/with_ingress/cuddle.yaml
Normal file
11
crates/cuddle-clusters/tests/with_ingress/cuddle.yaml
Normal file
@ -0,0 +1,11 @@
|
|||||||
|
vars:
|
||||||
|
service: service
|
||||||
|
ingress:
|
||||||
|
- external: "true"
|
||||||
|
- internal: "true"
|
||||||
|
- external_grpc: "true"
|
||||||
|
- internal_grpc: "true"
|
||||||
|
|
||||||
|
cuddle/clusters:
|
||||||
|
dev:
|
||||||
|
prod:
|
@ -0,0 +1,121 @@
|
|||||||
|
|
||||||
|
|
||||||
|
---
|
||||||
|
apiVersion: networking.k8s.io/v1
|
||||||
|
kind: Ingress
|
||||||
|
metadata:
|
||||||
|
annotations:
|
||||||
|
cert-manager.io/issuer: kjuulh-app
|
||||||
|
traefik.ingress.kubernetes.io/router.entrypoints: web
|
||||||
|
traefik.ingress.kubernetes.io/router.tls: "true"
|
||||||
|
labels:
|
||||||
|
app: service
|
||||||
|
cluster: dev
|
||||||
|
name: service-external-http
|
||||||
|
namespace: dev
|
||||||
|
spec:
|
||||||
|
rules:
|
||||||
|
- host: service.dev.kjuulh.app
|
||||||
|
http:
|
||||||
|
paths:
|
||||||
|
- backend:
|
||||||
|
service:
|
||||||
|
name: service
|
||||||
|
port:
|
||||||
|
name: external-http
|
||||||
|
path: /
|
||||||
|
pathType: Prefix
|
||||||
|
tls:
|
||||||
|
- hosts:
|
||||||
|
- service.dev.kjuulh.app
|
||||||
|
secretName: tls-service-kjuulh-app-external-http-ingress-dns
|
||||||
|
|
||||||
|
---
|
||||||
|
apiVersion: networking.k8s.io/v1
|
||||||
|
kind: Ingress
|
||||||
|
metadata:
|
||||||
|
annotations:
|
||||||
|
cert-manager.io/issuer: kjuulh-app
|
||||||
|
traefik.ingress.kubernetes.io/router.entrypoints: web
|
||||||
|
traefik.ingress.kubernetes.io/router.tls: "true"
|
||||||
|
labels:
|
||||||
|
app: service
|
||||||
|
cluster: dev
|
||||||
|
name: service-internal-http
|
||||||
|
namespace: dev
|
||||||
|
spec:
|
||||||
|
rules:
|
||||||
|
- host: service.dev.internal.kjuulh.app
|
||||||
|
http:
|
||||||
|
paths:
|
||||||
|
- backend:
|
||||||
|
service:
|
||||||
|
name: service
|
||||||
|
port:
|
||||||
|
name: internal-http
|
||||||
|
path: /
|
||||||
|
pathType: Prefix
|
||||||
|
tls:
|
||||||
|
- hosts:
|
||||||
|
- service.dev.internal.kjuulh.app
|
||||||
|
secretName: tls-service-kjuulh-app-internal-http-ingress-dns
|
||||||
|
|
||||||
|
---
|
||||||
|
apiVersion: networking.k8s.io/v1
|
||||||
|
kind: Ingress
|
||||||
|
metadata:
|
||||||
|
annotations:
|
||||||
|
cert-manager.io/issuer: kjuulh-app
|
||||||
|
traefik.ingress.kubernetes.io/router.entrypoints: web
|
||||||
|
traefik.ingress.kubernetes.io/router.tls: "true"
|
||||||
|
labels:
|
||||||
|
app: service
|
||||||
|
cluster: dev
|
||||||
|
name: service-external-grpc
|
||||||
|
namespace: dev
|
||||||
|
spec:
|
||||||
|
rules:
|
||||||
|
- host: grpc.service.dev.kjuulh.app
|
||||||
|
http:
|
||||||
|
paths:
|
||||||
|
- backend:
|
||||||
|
service:
|
||||||
|
name: service-grpc
|
||||||
|
port:
|
||||||
|
name: external-grpc
|
||||||
|
path: /
|
||||||
|
pathType: Prefix
|
||||||
|
tls:
|
||||||
|
- hosts:
|
||||||
|
- grpc.service.dev.kjuulh.app
|
||||||
|
secretName: tls-service-kjuulh-app-external-grpc-ingress-dns
|
||||||
|
|
||||||
|
---
|
||||||
|
apiVersion: networking.k8s.io/v1
|
||||||
|
kind: Ingress
|
||||||
|
metadata:
|
||||||
|
annotations:
|
||||||
|
cert-manager.io/issuer: kjuulh-app
|
||||||
|
traefik.ingress.kubernetes.io/router.entrypoints: web
|
||||||
|
traefik.ingress.kubernetes.io/router.tls: "true"
|
||||||
|
labels:
|
||||||
|
app: service
|
||||||
|
cluster: dev
|
||||||
|
name: service-internal-grpc
|
||||||
|
namespace: dev
|
||||||
|
spec:
|
||||||
|
rules:
|
||||||
|
- host: grpc.service.dev.internal.kjuulh.app
|
||||||
|
http:
|
||||||
|
paths:
|
||||||
|
- backend:
|
||||||
|
service:
|
||||||
|
name: service-grpc
|
||||||
|
port:
|
||||||
|
name: internal-grpc
|
||||||
|
path: /
|
||||||
|
pathType: Prefix
|
||||||
|
tls:
|
||||||
|
- hosts:
|
||||||
|
- grpc.service.dev.internal.kjuulh.app
|
||||||
|
secretName: tls-service-kjuulh-app-internal-grpc-ingress-dns
|
@ -0,0 +1,30 @@
|
|||||||
|
apiVersion: apps/v1
|
||||||
|
kind: Deployment
|
||||||
|
metadata:
|
||||||
|
labels:
|
||||||
|
app: service
|
||||||
|
name: service
|
||||||
|
spec:
|
||||||
|
replicas: 3
|
||||||
|
selector:
|
||||||
|
matchLabels:
|
||||||
|
app: service
|
||||||
|
template:
|
||||||
|
metadata:
|
||||||
|
labels:
|
||||||
|
app: service
|
||||||
|
spec:
|
||||||
|
containers:
|
||||||
|
- args:
|
||||||
|
- serve
|
||||||
|
command:
|
||||||
|
- service
|
||||||
|
image: kasperhermansen/service:main-1715336504
|
||||||
|
name: service
|
||||||
|
ports:
|
||||||
|
- containerPort: 3000
|
||||||
|
name: external-http
|
||||||
|
- containerPort: 3001
|
||||||
|
name: internal-http
|
||||||
|
- containerPort: 3002
|
||||||
|
name: internal-grpc
|
@ -0,0 +1,121 @@
|
|||||||
|
|
||||||
|
|
||||||
|
---
|
||||||
|
apiVersion: networking.k8s.io/v1
|
||||||
|
kind: Ingress
|
||||||
|
metadata:
|
||||||
|
annotations:
|
||||||
|
cert-manager.io/issuer: kjuulh-app
|
||||||
|
traefik.ingress.kubernetes.io/router.entrypoints: web
|
||||||
|
traefik.ingress.kubernetes.io/router.tls: "true"
|
||||||
|
labels:
|
||||||
|
app: service
|
||||||
|
cluster: prod
|
||||||
|
name: service-external-http
|
||||||
|
namespace: prod
|
||||||
|
spec:
|
||||||
|
rules:
|
||||||
|
- host: service.prod.kjuulh.app
|
||||||
|
http:
|
||||||
|
paths:
|
||||||
|
- backend:
|
||||||
|
service:
|
||||||
|
name: service
|
||||||
|
port:
|
||||||
|
name: external-http
|
||||||
|
path: /
|
||||||
|
pathType: Prefix
|
||||||
|
tls:
|
||||||
|
- hosts:
|
||||||
|
- service.prod.kjuulh.app
|
||||||
|
secretName: tls-service-kjuulh-app-external-http-ingress-dns
|
||||||
|
|
||||||
|
---
|
||||||
|
apiVersion: networking.k8s.io/v1
|
||||||
|
kind: Ingress
|
||||||
|
metadata:
|
||||||
|
annotations:
|
||||||
|
cert-manager.io/issuer: kjuulh-app
|
||||||
|
traefik.ingress.kubernetes.io/router.entrypoints: web
|
||||||
|
traefik.ingress.kubernetes.io/router.tls: "true"
|
||||||
|
labels:
|
||||||
|
app: service
|
||||||
|
cluster: prod
|
||||||
|
name: service-internal-http
|
||||||
|
namespace: prod
|
||||||
|
spec:
|
||||||
|
rules:
|
||||||
|
- host: service.prod.internal.kjuulh.app
|
||||||
|
http:
|
||||||
|
paths:
|
||||||
|
- backend:
|
||||||
|
service:
|
||||||
|
name: service
|
||||||
|
port:
|
||||||
|
name: internal-http
|
||||||
|
path: /
|
||||||
|
pathType: Prefix
|
||||||
|
tls:
|
||||||
|
- hosts:
|
||||||
|
- service.prod.internal.kjuulh.app
|
||||||
|
secretName: tls-service-kjuulh-app-internal-http-ingress-dns
|
||||||
|
|
||||||
|
---
|
||||||
|
apiVersion: networking.k8s.io/v1
|
||||||
|
kind: Ingress
|
||||||
|
metadata:
|
||||||
|
annotations:
|
||||||
|
cert-manager.io/issuer: kjuulh-app
|
||||||
|
traefik.ingress.kubernetes.io/router.entrypoints: web
|
||||||
|
traefik.ingress.kubernetes.io/router.tls: "true"
|
||||||
|
labels:
|
||||||
|
app: service
|
||||||
|
cluster: prod
|
||||||
|
name: service-external-grpc
|
||||||
|
namespace: prod
|
||||||
|
spec:
|
||||||
|
rules:
|
||||||
|
- host: grpc.service.prod.kjuulh.app
|
||||||
|
http:
|
||||||
|
paths:
|
||||||
|
- backend:
|
||||||
|
service:
|
||||||
|
name: service-grpc
|
||||||
|
port:
|
||||||
|
name: external-grpc
|
||||||
|
path: /
|
||||||
|
pathType: Prefix
|
||||||
|
tls:
|
||||||
|
- hosts:
|
||||||
|
- grpc.service.prod.kjuulh.app
|
||||||
|
secretName: tls-service-kjuulh-app-external-grpc-ingress-dns
|
||||||
|
|
||||||
|
---
|
||||||
|
apiVersion: networking.k8s.io/v1
|
||||||
|
kind: Ingress
|
||||||
|
metadata:
|
||||||
|
annotations:
|
||||||
|
cert-manager.io/issuer: kjuulh-app
|
||||||
|
traefik.ingress.kubernetes.io/router.entrypoints: web
|
||||||
|
traefik.ingress.kubernetes.io/router.tls: "true"
|
||||||
|
labels:
|
||||||
|
app: service
|
||||||
|
cluster: prod
|
||||||
|
name: service-internal-grpc
|
||||||
|
namespace: prod
|
||||||
|
spec:
|
||||||
|
rules:
|
||||||
|
- host: grpc.service.prod.internal.kjuulh.app
|
||||||
|
http:
|
||||||
|
paths:
|
||||||
|
- backend:
|
||||||
|
service:
|
||||||
|
name: service-grpc
|
||||||
|
port:
|
||||||
|
name: internal-grpc
|
||||||
|
path: /
|
||||||
|
pathType: Prefix
|
||||||
|
tls:
|
||||||
|
- hosts:
|
||||||
|
- grpc.service.prod.internal.kjuulh.app
|
||||||
|
secretName: tls-service-kjuulh-app-internal-grpc-ingress-dns
|
@ -0,0 +1,30 @@
|
|||||||
|
apiVersion: apps/v1
|
||||||
|
kind: Deployment
|
||||||
|
metadata:
|
||||||
|
labels:
|
||||||
|
app: service
|
||||||
|
name: service
|
||||||
|
spec:
|
||||||
|
replicas: 3
|
||||||
|
selector:
|
||||||
|
matchLabels:
|
||||||
|
app: service
|
||||||
|
template:
|
||||||
|
metadata:
|
||||||
|
labels:
|
||||||
|
app: service
|
||||||
|
spec:
|
||||||
|
containers:
|
||||||
|
- args:
|
||||||
|
- serve
|
||||||
|
command:
|
||||||
|
- service
|
||||||
|
image: kasperhermansen/service:main-1715336504
|
||||||
|
name: service
|
||||||
|
ports:
|
||||||
|
- containerPort: 3000
|
||||||
|
name: external-http
|
||||||
|
- containerPort: 3001
|
||||||
|
name: internal-http
|
||||||
|
- containerPort: 3002
|
||||||
|
name: internal-grpc
|
@ -1,26 +1,20 @@
|
|||||||
{%- set service_name = vars.cuddle_vars.service -%}
|
{%- set service_name = vars.cuddle_vars.service -%}
|
||||||
{%- set cluster_name = vars.cluster_vars.name -%}
|
|
||||||
{%- set cluster_namespace = vars.cluster_vars.namespace -%}
|
|
||||||
|
|
||||||
apiVersion: apps/v1
|
apiVersion: apps/v1
|
||||||
kind: Deployment
|
kind: Deployment
|
||||||
metadata:
|
metadata:
|
||||||
labels:
|
labels:
|
||||||
app: {{ service_name }}
|
app: {{ service_name }}
|
||||||
cluster: {{ cluster_name }}
|
|
||||||
name: {{ service_name }}
|
name: {{ service_name }}
|
||||||
namespace: {{ cluster_namespace }}
|
|
||||||
spec:
|
spec:
|
||||||
replicas: 3
|
replicas: 3
|
||||||
selector:
|
selector:
|
||||||
matchLabels:
|
matchLabels:
|
||||||
app: {{ service_name }}
|
app: {{ service_name }}
|
||||||
cluster: {{ cluster_name }}
|
|
||||||
template:
|
template:
|
||||||
metadata:
|
metadata:
|
||||||
labels:
|
labels:
|
||||||
app: {{ service_name }}
|
app: {{ service_name }}
|
||||||
cluster: {{ cluster_name }}
|
|
||||||
spec:
|
spec:
|
||||||
containers:
|
containers:
|
||||||
- args:
|
- args:
|
||||||
@ -29,9 +23,6 @@ spec:
|
|||||||
- {{ service_name }}
|
- {{ service_name }}
|
||||||
image: kasperhermansen/{{ service_name }}:main-1715336504
|
image: kasperhermansen/{{ service_name }}:main-1715336504
|
||||||
name: {{ service_name }}
|
name: {{ service_name }}
|
||||||
envFrom:
|
|
||||||
- configMapRef:
|
|
||||||
name: {{service_name}}-config
|
|
||||||
ports:
|
ports:
|
||||||
- containerPort: 3000
|
- containerPort: 3000
|
||||||
name: external-http
|
name: external-http
|
Loading…
Reference in New Issue
Block a user