use serde::{Deserialize, Serialize};
use std::collections::HashMap;
use crate::{KubernetesCloudReference, KubernetesClusterOwnership};
#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq)]
#[cfg_attr(feature = "openapi", derive(utoipa::ToSchema))]
#[serde(rename_all = "camelCase")]
pub struct AwsManagementConfig {
pub managing_role_arn: String,
}
#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq)]
#[cfg_attr(feature = "openapi", derive(utoipa::ToSchema))]
#[serde(rename_all = "camelCase")]
pub struct GcpManagementConfig {
pub service_account_email: String,
}
#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq)]
#[cfg_attr(feature = "openapi", derive(utoipa::ToSchema))]
#[serde(rename_all = "camelCase")]
pub struct AzureManagementConfig {
pub managing_tenant_id: String,
pub oidc_issuer: String,
pub oidc_subject: String,
}
#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq)]
#[cfg_attr(feature = "openapi", derive(utoipa::ToSchema))]
#[serde(rename_all = "camelCase", tag = "platform")]
pub enum ManagementConfig {
Aws(AwsManagementConfig),
Gcp(GcpManagementConfig),
Azure(AzureManagementConfig),
Kubernetes,
}
#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq)]
#[cfg_attr(feature = "openapi", derive(utoipa::ToSchema))]
#[serde(rename_all = "camelCase", tag = "type")]
pub enum NetworkSettings {
#[serde(rename = "use-default")]
UseDefault,
#[serde(rename = "create")]
Create {
#[serde(skip_serializing_if = "Option::is_none")]
cidr: Option<String>,
#[serde(default = "default_availability_zones")]
availability_zones: u8,
},
#[serde(rename = "byo-vpc-aws")]
ByoVpcAws {
vpc_id: String,
public_subnet_ids: Vec<String>,
private_subnet_ids: Vec<String>,
#[serde(default)]
security_group_ids: Vec<String>,
},
#[serde(rename = "byo-vpc-gcp")]
ByoVpcGcp {
network_name: String,
subnet_name: String,
region: String,
},
#[serde(rename = "byo-vnet-azure")]
ByoVnetAzure {
vnet_resource_id: String,
public_subnet_name: String,
private_subnet_name: String,
#[serde(default, skip_serializing_if = "Option::is_none")]
application_gateway_subnet_name: Option<String>,
},
}
fn default_availability_zones() -> u8 {
2
}
#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq, Default)]
#[cfg_attr(feature = "openapi", derive(utoipa::ToSchema))]
#[serde(rename_all = "camelCase")]
pub struct ComputeSettings {
#[serde(default, skip_serializing_if = "HashMap::is_empty")]
pub pools: HashMap<String, ComputePoolSelection>,
}
#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq)]
#[cfg_attr(feature = "openapi", derive(utoipa::ToSchema))]
#[serde(rename_all = "camelCase", tag = "mode")]
pub enum ComputePoolSelection {
Fixed {
machines: u32,
#[serde(default, skip_serializing_if = "Option::is_none")]
machine: Option<String>,
},
Autoscale {
min: u32,
max: u32,
#[serde(default, skip_serializing_if = "Option::is_none")]
machine: Option<String>,
},
}
impl ComputePoolSelection {
pub fn machine(&self) -> Option<&str> {
match self {
Self::Fixed { machine, .. } | Self::Autoscale { machine, .. } => machine.as_deref(),
}
}
pub fn min_size(&self) -> u32 {
match self {
Self::Fixed { machines, .. } => *machines,
Self::Autoscale { min, .. } => *min,
}
}
pub fn max_size(&self) -> u32 {
match self {
Self::Fixed { machines, .. } => *machines,
Self::Autoscale { max, .. } => *max,
}
}
pub fn validate(&self) -> std::result::Result<(), String> {
match self {
Self::Fixed { machines, .. } => {
if *machines == 0 {
Err("fixed compute pools must select at least one machine".to_string())
} else {
Ok(())
}
}
Self::Autoscale { min, max, .. } => {
if min > max {
Err(format!(
"autoscaling compute pool minimum ({min}) cannot exceed maximum ({max})"
))
} else {
Ok(())
}
}
}
}
}
#[derive(Debug, Clone, Copy, Serialize, Deserialize, PartialEq, Eq, Default)]
#[cfg_attr(feature = "openapi", derive(utoipa::ToSchema))]
#[serde(rename_all = "camelCase")]
pub enum DeploymentModel {
#[default]
Push,
Pull,
}
#[derive(Debug, Clone, Copy, Serialize, Deserialize, PartialEq, Eq, Default)]
#[cfg_attr(feature = "openapi", derive(utoipa::ToSchema))]
#[serde(rename_all = "kebab-case")]
pub enum UpdatesMode {
#[default]
Auto,
ApprovalRequired,
}
#[derive(Debug, Clone, Copy, Serialize, Deserialize, PartialEq, Eq, Default)]
#[cfg_attr(feature = "openapi", derive(utoipa::ToSchema))]
#[serde(rename_all = "kebab-case")]
pub enum TelemetryMode {
Off,
#[default]
Auto,
ApprovalRequired,
}
impl TelemetryMode {
pub fn is_enabled(&self) -> bool {
!matches!(self, TelemetryMode::Off)
}
}
#[derive(Debug, Clone, Copy, Serialize, Deserialize, PartialEq, Eq, Default)]
#[cfg_attr(feature = "openapi", derive(utoipa::ToSchema))]
#[serde(rename_all = "kebab-case")]
pub enum HeartbeatsMode {
Off,
#[default]
On,
}
impl HeartbeatsMode {
pub fn is_enabled(&self) -> bool {
matches!(self, HeartbeatsMode::On)
}
}
#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq)]
#[cfg_attr(feature = "openapi", derive(utoipa::ToSchema))]
#[serde(rename_all = "camelCase")]
pub struct DomainSettings {
#[serde(default, skip_serializing_if = "Option::is_none")]
pub custom_domains: Option<HashMap<String, CustomDomainConfig>>,
}
#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq)]
#[cfg_attr(feature = "openapi", derive(utoipa::ToSchema))]
#[serde(rename_all = "camelCase")]
pub struct CustomDomainConfig {
pub domain: String,
pub certificate: CustomCertificateConfig,
}
#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq, Default)]
#[cfg_attr(feature = "openapi", derive(utoipa::ToSchema))]
#[serde(rename_all = "camelCase")]
pub struct CustomCertificateConfig {
#[serde(default, skip_serializing_if = "Option::is_none")]
pub aws: Option<AwsCustomCertificateConfig>,
#[serde(default, skip_serializing_if = "Option::is_none")]
pub gcp: Option<GcpCustomCertificateConfig>,
#[serde(default, skip_serializing_if = "Option::is_none")]
pub azure: Option<AzureCustomCertificateConfig>,
#[serde(default, skip_serializing_if = "Option::is_none")]
pub kubernetes: Option<KubernetesCustomCertificateConfig>,
}
#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq)]
#[cfg_attr(feature = "openapi", derive(utoipa::ToSchema))]
#[serde(rename_all = "camelCase")]
pub struct AwsCustomCertificateConfig {
pub certificate_arn: String,
}
#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq)]
#[cfg_attr(feature = "openapi", derive(utoipa::ToSchema))]
#[serde(rename_all = "camelCase")]
pub struct GcpCustomCertificateConfig {
pub certificate_name: String,
}
#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq)]
#[cfg_attr(feature = "openapi", derive(utoipa::ToSchema))]
#[serde(rename_all = "camelCase")]
pub struct AzureCustomCertificateConfig {
pub key_vault_certificate_id: String,
#[serde(default, skip_serializing_if = "Option::is_none")]
pub key_vault_resource_id: Option<String>,
}
#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq)]
#[cfg_attr(feature = "openapi", derive(utoipa::ToSchema))]
#[serde(rename_all = "camelCase")]
pub struct KubernetesCustomCertificateConfig {
pub tls_secret_ref: KubernetesTlsSecretRef,
}
#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq)]
#[cfg_attr(feature = "openapi", derive(utoipa::ToSchema))]
#[serde(rename_all = "camelCase")]
pub struct KubernetesSettings {
#[serde(default, skip_serializing_if = "Option::is_none")]
pub cluster: Option<KubernetesClusterSettings>,
#[serde(default, skip_serializing_if = "Option::is_none")]
pub exposure: Option<KubernetesExposureSettings>,
}
#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq)]
#[cfg_attr(feature = "openapi", derive(utoipa::ToSchema))]
#[serde(rename_all = "camelCase")]
pub struct KubernetesClusterSettings {
pub ownership: KubernetesClusterOwnership,
#[serde(default, skip_serializing_if = "Option::is_none")]
pub namespace: Option<String>,
#[serde(default, skip_serializing_if = "Option::is_none")]
pub cloud: Option<KubernetesCloudReference>,
}
#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq)]
#[cfg_attr(feature = "openapi", derive(utoipa::ToSchema))]
#[serde(rename_all = "camelCase", tag = "mode")]
pub enum KubernetesExposureSettings {
Disabled,
Generated {
route: KubernetesRouteProfile,
certificate: KubernetesCertificateMode,
},
Custom {
domain: String,
route: KubernetesRouteProfile,
certificate: KubernetesCertificateMode,
},
}
#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq)]
#[cfg_attr(feature = "openapi", derive(utoipa::ToSchema))]
#[serde(rename_all = "camelCase", tag = "routeApi")]
pub enum KubernetesRouteProfile {
Ingress(KubernetesIngressRouteProfile),
Gateway(KubernetesGatewayRouteProfile),
}
#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq, Default)]
#[cfg_attr(feature = "openapi", derive(utoipa::ToSchema))]
#[serde(rename_all = "camelCase")]
pub struct KubernetesIngressRouteProfile {
#[serde(default, skip_serializing_if = "Option::is_none")]
pub controller: Option<String>,
pub ingress_class_name: String,
#[serde(default, skip_serializing_if = "HashMap::is_empty")]
pub labels: HashMap<String, String>,
#[serde(default, skip_serializing_if = "HashMap::is_empty")]
pub annotations: HashMap<String, String>,
#[serde(default, skip_serializing_if = "Option::is_none")]
pub provider: Option<KubernetesRouteProviderOptions>,
}
#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq, Default)]
#[cfg_attr(feature = "openapi", derive(utoipa::ToSchema))]
#[serde(rename_all = "camelCase")]
pub struct KubernetesGatewayRouteProfile {
#[serde(default, skip_serializing_if = "Option::is_none")]
pub controller: Option<String>,
pub gateway_class_name: String,
pub listener_port: u16,
#[serde(default, skip_serializing_if = "HashMap::is_empty")]
pub labels: HashMap<String, String>,
#[serde(default, skip_serializing_if = "HashMap::is_empty")]
pub annotations: HashMap<String, String>,
#[serde(default, skip_serializing_if = "Option::is_none")]
pub provider: Option<KubernetesRouteProviderOptions>,
}
#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq)]
#[cfg_attr(feature = "openapi", derive(utoipa::ToSchema))]
#[serde(rename_all = "camelCase", tag = "provider")]
pub enum KubernetesRouteProviderOptions {
#[serde(rename_all = "camelCase")]
AwsAlb {
scheme: String,
target_type: String,
#[serde(default, skip_serializing_if = "Option::is_none")]
ip_address_type: Option<String>,
#[serde(default, skip_serializing_if = "Vec::is_empty")]
subnet_ids: Vec<String>,
},
#[serde(rename_all = "camelCase")]
GkeGateway {
#[serde(default, skip_serializing_if = "Option::is_none")]
static_address_name: Option<String>,
},
#[serde(rename_all = "camelCase")]
AzureApplicationGatewayForContainers {
#[serde(default, skip_serializing_if = "Option::is_none")]
alb_namespace: Option<String>,
#[serde(default, skip_serializing_if = "Option::is_none")]
alb_name: Option<String>,
frontend: String,
},
}
#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq)]
#[cfg_attr(feature = "openapi", derive(utoipa::ToSchema))]
#[serde(rename_all = "camelCase", tag = "mode")]
pub enum KubernetesCertificateMode {
#[serde(rename_all = "camelCase")]
ManagedAcmImport {
#[serde(default, skip_serializing_if = "Option::is_none")]
region: Option<String>,
#[serde(default, skip_serializing_if = "HashMap::is_empty")]
tags: HashMap<String, String>,
},
#[serde(rename_all = "camelCase")]
AwsAcmArn {
certificate_arn: String,
},
#[serde(rename_all = "camelCase")]
ManagedTlsSecret {
secret_name_template: String,
},
TlsSecretRef(KubernetesTlsSecretRef),
None,
}
#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq)]
#[cfg_attr(feature = "openapi", derive(utoipa::ToSchema))]
#[serde(rename_all = "camelCase")]
pub struct KubernetesTlsSecretRef {
pub secret_name: String,
#[serde(default, skip_serializing_if = "Option::is_none")]
pub namespace: Option<String>,
}
#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq, Default)]
#[cfg_attr(feature = "openapi", derive(utoipa::ToSchema))]
#[serde(rename_all = "camelCase")]
pub struct StackSettings {
#[serde(default, skip_serializing_if = "Option::is_none")]
pub network: Option<NetworkSettings>,
#[serde(default, skip_serializing_if = "Option::is_none")]
pub domains: Option<DomainSettings>,
#[serde(default, skip_serializing_if = "Option::is_none")]
pub kubernetes: Option<KubernetesSettings>,
#[serde(default, skip_serializing_if = "Option::is_none")]
pub compute: Option<ComputeSettings>,
#[serde(default, skip_serializing_if = "is_default_deployment_model")]
pub deployment_model: DeploymentModel,
#[serde(default, skip_serializing_if = "is_default_updates_mode")]
pub updates: UpdatesMode,
#[serde(default, skip_serializing_if = "is_default_telemetry_mode")]
pub telemetry: TelemetryMode,
#[serde(default, skip_serializing_if = "is_default_heartbeats_mode")]
pub heartbeats: HeartbeatsMode,
#[serde(default, skip_serializing_if = "Option::is_none")]
#[cfg_attr(feature = "openapi", schema(value_type = Option<Object>))]
pub external_bindings: Option<crate::ExternalBindings>,
}
fn is_default_deployment_model(model: &DeploymentModel) -> bool {
*model == DeploymentModel::default()
}
fn is_default_updates_mode(mode: &UpdatesMode) -> bool {
*mode == UpdatesMode::default()
}
fn is_default_telemetry_mode(mode: &TelemetryMode) -> bool {
*mode == TelemetryMode::default()
}
fn is_default_heartbeats_mode(mode: &HeartbeatsMode) -> bool {
*mode == HeartbeatsMode::default()
}