mirror of
https://github.com/holos-run/holos.git
synced 2026-03-19 08:44:58 +00:00
Compare commits
8 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
0a7001f868 | ||
|
|
2db7be671b | ||
|
|
b51870f7bf | ||
|
|
0227dfa7e5 | ||
|
|
05b59d9af0 | ||
|
|
04f9f3b3a8 | ||
|
|
b58be8b38c | ||
|
|
10493d754a |
6
Makefile
6
Makefile
@@ -4,7 +4,7 @@ PROJ=holos
|
||||
ORG_PATH=github.com/holos-run
|
||||
REPO_PATH=$(ORG_PATH)/$(PROJ)
|
||||
|
||||
VERSION := $(shell grep "const Version " pkg/version/version.go | sed -E 's/.*"(.+)"$$/\1/')
|
||||
VERSION := $(shell cat pkg/version/embedded/major pkg/version/embedded/minor pkg/version/embedded/patch | xargs printf "%s.%s.%s")
|
||||
BIN_NAME := holos
|
||||
|
||||
DOCKER_REPO=quay.io/openinfrastructure/holos
|
||||
@@ -39,6 +39,10 @@ bumpmajor: ## Bump the major version.
|
||||
scripts/bump minor 0
|
||||
scripts/bump patch 0
|
||||
|
||||
.PHONY: show-version
|
||||
show-version: ## Print the full version.
|
||||
@echo $(VERSION)
|
||||
|
||||
.PHONY: tidy
|
||||
tidy: ## Tidy go module.
|
||||
go mod tidy
|
||||
|
||||
@@ -306,19 +306,10 @@ import "strings"
|
||||
// "value"` for prefix-based match - `regex: "value"` for RE2
|
||||
// style regex-based match
|
||||
// (https://github.com/google/re2/wiki/Syntax).
|
||||
uri?: ({} | {
|
||||
exact: _
|
||||
} | {
|
||||
prefix: _
|
||||
} | {
|
||||
regex: _
|
||||
}) & {
|
||||
uri?: {
|
||||
exact?: string
|
||||
prefix?: string
|
||||
|
||||
// RE2 style regex-based match
|
||||
// (https://github.com/google/re2/wiki/Syntax).
|
||||
regex?: string
|
||||
regex?: string
|
||||
}
|
||||
|
||||
// withoutHeader has the same syntax with the header, but has
|
||||
|
||||
@@ -17,6 +17,8 @@ import "encoding/yaml"
|
||||
VirtualService?: [Name=_]: #VirtualService & {metadata: name: Name}
|
||||
Issuer?: [Name=_]: #Issuer & {metadata: name: Name}
|
||||
Gateway?: [Name=_]: #Gateway & {metadata: name: Name}
|
||||
ConfigMap?: [Name=_]: #ConfigMap & {metadata: name: Name}
|
||||
Deployment?: [_]: #Deployment
|
||||
}
|
||||
|
||||
// apiObjectMap holds the marshalled representation of apiObjects
|
||||
|
||||
@@ -1,7 +1,5 @@
|
||||
package holos
|
||||
|
||||
import "list"
|
||||
|
||||
spec: components: KubernetesObjectsList: [
|
||||
#KubernetesObjects & {
|
||||
metadata: name: "prod-secrets-namespaces"
|
||||
@@ -9,7 +7,7 @@ spec: components: KubernetesObjectsList: [
|
||||
apiObjects: {
|
||||
// #ManagedNamespaces is the set of all namespaces across all clusters in the platform.
|
||||
for k, ns in #ManagedNamespaces {
|
||||
if list.Contains(ns.clusterNames, #ClusterName) {
|
||||
if ns.clusters[#ClusterName] != _|_ {
|
||||
Namespace: "\(k)": #Namespace & ns.namespace
|
||||
}
|
||||
}
|
||||
|
||||
@@ -50,8 +50,8 @@ let OBJECTS = #APIObjects & {
|
||||
seccompProfile: type: "RuntimeDefault"
|
||||
allowPrivilegeEscalation: false
|
||||
runAsNonRoot: true
|
||||
runAsUser: 1337
|
||||
runAsGroup: 1337
|
||||
runAsUser: 8192
|
||||
runAsGroup: 8192
|
||||
capabilities: drop: ["ALL"]
|
||||
}}]
|
||||
}
|
||||
|
||||
@@ -1,11 +1,16 @@
|
||||
package holos
|
||||
|
||||
#Project: authProxyOrgDomain: "openinfrastructure.co"
|
||||
|
||||
_Projects: #Projects & {
|
||||
holos: {
|
||||
resourceId: 260446255245690199
|
||||
clusters: {
|
||||
k1: _
|
||||
k2: _
|
||||
}
|
||||
stages: dev: authProxyClientID: "260505543108527218@holos"
|
||||
stages: prod: authProxyClientID: "260506079325128023@holos"
|
||||
environments: {
|
||||
prod: stage: "prod"
|
||||
dev: stage: "dev"
|
||||
@@ -26,6 +31,8 @@ _Projects: #Projects & {
|
||||
// Manage namespaces for platform project environments.
|
||||
for project in _Projects {
|
||||
for ns in project.managedNamespaces {
|
||||
#ManagedNamespaces: (ns.namespace.metadata.name): ns
|
||||
if ns.clusters[#ClusterName] != _|_ {
|
||||
#ManagedNamespaces: (ns.namespace.metadata.name): ns
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,79 +1,93 @@
|
||||
package holos
|
||||
|
||||
import "strings"
|
||||
|
||||
// Platform level definition of a project.
|
||||
#Project: {
|
||||
name: string
|
||||
|
||||
// All projects have at least a prod environment and stage.
|
||||
stages: prod: stageSegments: []
|
||||
environments: prod: stage: "prod"
|
||||
environments: prod: dnsSegments: []
|
||||
stages: prod: _
|
||||
stages: dev: _
|
||||
// Short hostnames to construct fqdns.
|
||||
environments: prod: envSegments: []
|
||||
stages: dev: _
|
||||
environments: dev: stage: "dev"
|
||||
environments: dev: envSegments: []
|
||||
// Ensure at least the project name is a short hostname. Additional may be added.
|
||||
hosts: (name): _
|
||||
|
||||
// environments share the stage segments of their stage.
|
||||
environments: [_]: {
|
||||
stage: string
|
||||
stageSegments: stages[stage].stageSegments
|
||||
}
|
||||
}
|
||||
|
||||
#ProjectTemplate: {
|
||||
project: #Project
|
||||
let Project = project
|
||||
|
||||
// ExtAuthzHosts maps host names to the backend environment namespace for ExtAuthz.
|
||||
let ExtAuthzHosts = {
|
||||
// GatewayServers maps Gateway spec.servers #GatewayServer values indexed by stage then name.
|
||||
let GatewayServers = {
|
||||
// Initialize all stages, even if they have no environments.
|
||||
for stage in project.stages {
|
||||
(stage.name): {}
|
||||
}
|
||||
|
||||
// For each stage, construct entries for the Gateway spec.servers.hosts field.
|
||||
for env in project.environments {
|
||||
(env.stage): {
|
||||
for host in project.hosts {
|
||||
let NAME = "https-\(project.name)-\(env.name)-\(host.name)"
|
||||
let SEGMENTS = [host.name] + env.dnsSegments + [#Platform.org.domain]
|
||||
let HOST = strings.Join(SEGMENTS, ".")
|
||||
(NAME): #GatewayServer & {
|
||||
hosts: ["\(env.namespace)/\(HOST)"]
|
||||
// name must be unique across all servers in all gateways
|
||||
port: name: NAME
|
||||
port: number: 443
|
||||
port: protocol: "HTTPS"
|
||||
// TODO: Manage a certificate with each host in the dns alt names.
|
||||
tls: credentialName: HOST
|
||||
let Env = env
|
||||
let Stage = project.stages[env.stage]
|
||||
for host in (#EnvHosts & {project: Project, env: Env}).hosts {
|
||||
(host.name): #GatewayServer & {
|
||||
hosts: [
|
||||
"\(env.namespace)/\(host.name)",
|
||||
// Allow the authproxy VirtualService to match the project.authProxyPrefix path.
|
||||
"\(Stage.namespace)/\(host.name)",
|
||||
]
|
||||
port: host.port
|
||||
tls: credentialName: host.name
|
||||
tls: mode: "SIMPLE"
|
||||
}
|
||||
|
||||
for cluster in project.clusters {
|
||||
let NAME = "https-\(cluster.name)-\(project.name)-\(env.name)-\(host.name)"
|
||||
let SEGMENTS = [host.name] + env.dnsSegments + [cluster.name, #Platform.org.domain]
|
||||
let HOST = strings.Join(SEGMENTS, ".")
|
||||
(NAME): #GatewayServer & {
|
||||
hosts: ["\(env.namespace)/\(HOST)"]
|
||||
// name must be unique across all servers in all gateways
|
||||
port: name: NAME
|
||||
port: number: 443
|
||||
port: protocol: "HTTPS"
|
||||
// TODO: Manage a certificate with each host in the dns alt names.
|
||||
tls: credentialName: HOST
|
||||
tls: mode: "SIMPLE"
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
workload: resources: {
|
||||
for stage in project.stages {
|
||||
// Istio Gateway
|
||||
"\(stage.slug)-gateway": #KubernetesObjects & {
|
||||
apiObjectMap: (#APIObjects & {
|
||||
apiObjects: Gateway: (stage.slug): #Gateway & {
|
||||
spec: servers: [for host in ExtAuthzHosts[stage.name] {host}]
|
||||
}
|
||||
// Provide resources only if the project is managed on --cluster-name
|
||||
if project.clusters[#ClusterName] != _|_ {
|
||||
for stage in project.stages {
|
||||
let Stage = stage
|
||||
|
||||
for host in ExtAuthzHosts[stage.name] {
|
||||
apiObjects: ExternalSecret: (host.tls.credentialName): metadata: namespace: "istio-ingress"
|
||||
// Istio Gateway
|
||||
"\(stage.slug)-gateway": #KubernetesObjects & {
|
||||
apiObjectMap: (#APIObjects & {
|
||||
apiObjects: Gateway: (stage.slug): #Gateway & {
|
||||
spec: servers: [for server in GatewayServers[stage.name] {server}]
|
||||
}
|
||||
|
||||
for host in GatewayServers[stage.name] {
|
||||
apiObjects: ExternalSecret: (host.tls.credentialName): metadata: namespace: "istio-ingress"
|
||||
}
|
||||
}).apiObjectMap
|
||||
}
|
||||
|
||||
// Manage auth-proxy in each stage
|
||||
"\(stage.slug)-authproxy": #KubernetesObjects & {
|
||||
apiObjectMap: (#APIObjects & {
|
||||
apiObjects: (AUTHPROXY & {stage: Stage, project: Project, servers: GatewayServers[stage.name]}).apiObjects
|
||||
}).apiObjectMap
|
||||
}
|
||||
|
||||
// Manage httpbin in each environment
|
||||
for Env in project.environments if Env.stage == stage.name {
|
||||
"\(Env.slug)-httpbin": #KubernetesObjects & {
|
||||
apiObjectMap: (#APIObjects & {
|
||||
let Project = project
|
||||
apiObjects: (HTTPBIN & {env: Env, project: Project}).apiObjects
|
||||
}).apiObjectMap
|
||||
}
|
||||
}).apiObjectMap
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -82,7 +96,7 @@ import "strings"
|
||||
for stage in project.stages {
|
||||
"\(stage.slug)-certs": #KubernetesObjects & {
|
||||
apiObjectMap: (#APIObjects & {
|
||||
for host in ExtAuthzHosts[stage.name] {
|
||||
for host in GatewayServers[stage.name] {
|
||||
let CN = host.tls.credentialName
|
||||
apiObjects: Certificate: (CN): #Certificate & {
|
||||
metadata: name: CN
|
||||
@@ -98,83 +112,273 @@ import "strings"
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
}).apiObjectMap
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// #GatewayServer defines the value of the istio Gateway.spec.servers field.
|
||||
#GatewayServer: {
|
||||
// The ip or the Unix domain socket to which the listener should
|
||||
// be bound to.
|
||||
bind?: string
|
||||
defaultEndpoint?: string
|
||||
let HTTPBIN = {
|
||||
name: string | *"httpbin"
|
||||
project: #Project
|
||||
env: #Environment
|
||||
let Name = name
|
||||
|
||||
// One or more hosts exposed by this gateway.
|
||||
hosts: [...string]
|
||||
|
||||
// An optional name of the server, when set must be unique across
|
||||
// all servers.
|
||||
name?: string
|
||||
|
||||
// The Port on which the proxy should listen for incoming
|
||||
// connections.
|
||||
port: {
|
||||
// Label assigned to the port.
|
||||
name: string
|
||||
|
||||
// A valid non-negative integer port number.
|
||||
number: int
|
||||
|
||||
// The protocol exposed on the port.
|
||||
protocol: string
|
||||
targetPort?: int
|
||||
let Metadata = {
|
||||
name: Name
|
||||
namespace: env.namespace
|
||||
labels: app: name
|
||||
}
|
||||
|
||||
// Set of TLS related options that govern the server's behavior.
|
||||
tls?: {
|
||||
// REQUIRED if mode is `MUTUAL` or `OPTIONAL_MUTUAL`.
|
||||
caCertificates?: string
|
||||
apiObjects: {
|
||||
Deployment: (Name): #Deployment & {
|
||||
metadata: Metadata
|
||||
|
||||
// Optional: If specified, only support the specified cipher list.
|
||||
cipherSuites?: [...string]
|
||||
|
||||
// For gateways running on Kubernetes, the name of the secret that
|
||||
// holds the TLS certs including the CA certificates.
|
||||
credentialName?: string
|
||||
|
||||
// If set to true, the load balancer will send a 301 redirect for
|
||||
// all http connections, asking the clients to use HTTPS.
|
||||
httpsRedirect?: bool
|
||||
|
||||
// Optional: Maximum TLS protocol version.
|
||||
maxProtocolVersion?: "TLS_AUTO" | "TLSV1_0" | "TLSV1_1" | "TLSV1_2" | "TLSV1_3"
|
||||
|
||||
// Optional: Minimum TLS protocol version.
|
||||
minProtocolVersion?: "TLS_AUTO" | "TLSV1_0" | "TLSV1_1" | "TLSV1_2" | "TLSV1_3"
|
||||
|
||||
// Optional: Indicates whether connections to this port should be
|
||||
// secured using TLS.
|
||||
mode?: "PASSTHROUGH" | "SIMPLE" | "MUTUAL" | "AUTO_PASSTHROUGH" | "ISTIO_MUTUAL" | "OPTIONAL_MUTUAL"
|
||||
|
||||
// REQUIRED if mode is `SIMPLE` or `MUTUAL`.
|
||||
privateKey?: string
|
||||
|
||||
// REQUIRED if mode is `SIMPLE` or `MUTUAL`.
|
||||
serverCertificate?: string
|
||||
|
||||
// A list of alternate names to verify the subject identity in the
|
||||
// certificate presented by the client.
|
||||
subjectAltNames?: [...string]
|
||||
|
||||
// An optional list of hex-encoded SHA-256 hashes of the
|
||||
// authorized client certificates.
|
||||
verifyCertificateHash?: [...string]
|
||||
|
||||
// An optional list of base64-encoded SHA-256 hashes of the SPKIs
|
||||
// of authorized client certificates.
|
||||
verifyCertificateSpki?: [...string]
|
||||
spec: selector: matchLabels: Metadata.labels
|
||||
spec: template: {
|
||||
metadata: labels: Metadata.labels & #IstioSidecar
|
||||
spec: securityContext: seccompProfile: type: "RuntimeDefault"
|
||||
spec: containers: [{
|
||||
name: Name
|
||||
image: "quay.io/holos/mccutchen/go-httpbin"
|
||||
ports: [{containerPort: 8080}]
|
||||
securityContext: {
|
||||
seccompProfile: type: "RuntimeDefault"
|
||||
allowPrivilegeEscalation: false
|
||||
runAsNonRoot: true
|
||||
runAsUser: 8192
|
||||
runAsGroup: 8192
|
||||
capabilities: drop: ["ALL"]
|
||||
}}]
|
||||
}
|
||||
}
|
||||
Service: (Name): #Service & {
|
||||
metadata: Metadata
|
||||
spec: selector: Metadata.labels
|
||||
spec: ports: [
|
||||
{port: 80, targetPort: 8080, protocol: "TCP", name: "http"},
|
||||
]
|
||||
}
|
||||
VirtualService: (Name): #VirtualService & {
|
||||
metadata: Metadata
|
||||
let Project = project
|
||||
let Env = env
|
||||
spec: hosts: [for host in (#EnvHosts & {project: Project, env: Env}).hosts {host.name}]
|
||||
spec: gateways: ["istio-ingress/\(env.stageSlug)"]
|
||||
spec: http: [{route: [{destination: host: Name}]}]
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// AUTHPROXY configures one oauth2-proxy deployment for each host in each stage of a project. Multiple deployments per stage are used to narrow down the cookie domain.
|
||||
let AUTHPROXY = {
|
||||
name: string | *"authproxy"
|
||||
project: #Project
|
||||
stage: #Stage
|
||||
servers: {}
|
||||
let Name = name
|
||||
let Project = project
|
||||
let Stage = stage
|
||||
|
||||
let Metadata = {
|
||||
name: Name
|
||||
namespace: stage.namespace
|
||||
labels: {
|
||||
"app.kubernetes.io/name": name
|
||||
"app.kubernetes.io/instance": stage.name
|
||||
"app.kubernetes.io/part-of": stage.project
|
||||
}
|
||||
}
|
||||
|
||||
let RedisMetadata = {
|
||||
name: Name + "-redis"
|
||||
namespace: stage.namespace
|
||||
labels: {
|
||||
"app.kubernetes.io/name": name
|
||||
"app.kubernetes.io/instance": stage.name
|
||||
"app.kubernetes.io/part-of": stage.project
|
||||
}
|
||||
}
|
||||
|
||||
apiObjects: {
|
||||
// oauth2-proxy
|
||||
ExternalSecret: (Name): metadata: Metadata
|
||||
Deployment: (Name): #Deployment & {
|
||||
metadata: Metadata
|
||||
|
||||
// project.dev.example.com, project.dev.k1.example.com, project.dev.k2.example.com
|
||||
let StageDomains = {
|
||||
for host in (#StageDomains & {project: Project, stage: Stage}).hosts {
|
||||
(host.name): host
|
||||
}
|
||||
}
|
||||
|
||||
spec: {
|
||||
replicas: 1
|
||||
selector: matchLabels: Metadata.labels
|
||||
template: {
|
||||
metadata: labels: Metadata.labels
|
||||
metadata: labels: #IstioSidecar
|
||||
spec: securityContext: seccompProfile: type: "RuntimeDefault"
|
||||
spec: containers: [{
|
||||
image: "quay.io/oauth2-proxy/oauth2-proxy:v7.4.0"
|
||||
imagePullPolicy: "IfNotPresent"
|
||||
name: "oauth2-proxy"
|
||||
args: [
|
||||
// callback url is proxy prefix + /callback
|
||||
"--proxy-prefix=" + project.authProxyPrefix,
|
||||
"--email-domain=*",
|
||||
"--session-store-type=redis",
|
||||
"--redis-connection-url=redis://\(RedisMetadata.name):6379",
|
||||
"--cookie-refresh=12h",
|
||||
"--cookie-expire=2160h",
|
||||
"--cookie-secure=true",
|
||||
"--cookie-name=__Secure-\(Name)-\(stage.slug)",
|
||||
"--cookie-samesite=lax",
|
||||
for domain in StageDomains {"--cookie-domain=.\(domain.name)"},
|
||||
for domain in StageDomains {"--whitelist-domain=.\(domain.name)"},
|
||||
"--cookie-csrf-per-request=true",
|
||||
"--cookie-csrf-expire=120s",
|
||||
"--set-authorization-header=false",
|
||||
"--set-xauthrequest=true",
|
||||
"--pass-access-token=true",
|
||||
"--pass-authorization-header=true",
|
||||
"--upstream=static://200",
|
||||
"--reverse-proxy",
|
||||
"--real-client-ip-header=X-Forwarded-For",
|
||||
"--skip-provider-button=true",
|
||||
"--auth-logging",
|
||||
"--provider=oidc",
|
||||
"--scope=openid profile email groups offline_access urn:zitadel:iam:org:domain:primary:\(project.authProxyOrgDomain)",
|
||||
"--client-id=" + stage.authProxyClientID,
|
||||
"--client-secret-file=/dev/null",
|
||||
"--oidc-issuer-url=\(project.authProxyIssuer)",
|
||||
"--code-challenge-method=S256",
|
||||
"--http-address=0.0.0.0:4180",
|
||||
// "--allowed-group=\(project.resourceId):\(stage.name)-access",
|
||||
]
|
||||
env: [{
|
||||
name: "OAUTH2_PROXY_COOKIE_SECRET"
|
||||
// echo '{"cookiesecret":"'$(LC_ALL=C tr -dc "[:alpha:]" </dev/random | tr '[:upper:]' '[:lower:]' | head -c 32)'"}' | holos create secret -n dev-holos-system --append-hash=false --data-stdin authproxy
|
||||
valueFrom: secretKeyRef: {
|
||||
key: "cookiesecret"
|
||||
name: Name
|
||||
}
|
||||
}]
|
||||
ports: [{
|
||||
containerPort: 4180
|
||||
protocol: "TCP"
|
||||
}]
|
||||
securityContext: {
|
||||
seccompProfile: type: "RuntimeDefault"
|
||||
allowPrivilegeEscalation: false
|
||||
runAsNonRoot: true
|
||||
runAsUser: 8192
|
||||
runAsGroup: 8192
|
||||
capabilities: drop: ["ALL"]
|
||||
}
|
||||
}]
|
||||
}
|
||||
}
|
||||
}
|
||||
Service: (Name): #Service & {
|
||||
metadata: Metadata
|
||||
spec: selector: Metadata.labels
|
||||
spec: ports: [
|
||||
{port: 80, targetPort: 4180, protocol: "TCP", name: "http"},
|
||||
]
|
||||
}
|
||||
VirtualService: (Name): #VirtualService & {
|
||||
metadata: Metadata
|
||||
spec: hosts: [for host, v in servers {host}]
|
||||
spec: gateways: ["istio-ingress/\(stage.slug)"]
|
||||
spec: http: [{
|
||||
match: [{uri: prefix: project.authProxyPrefix}]
|
||||
route: [{
|
||||
destination: host: Name
|
||||
destination: port: number: 80
|
||||
}]
|
||||
}]
|
||||
}
|
||||
|
||||
// redis
|
||||
ConfigMap: (RedisMetadata.name): {
|
||||
metadata: RedisMetadata
|
||||
data: "redis.conf": """
|
||||
maxmemory 128mb
|
||||
maxmemory-policy allkeys-lru
|
||||
"""
|
||||
}
|
||||
Deployment: (RedisMetadata.name): {
|
||||
metadata: RedisMetadata
|
||||
spec: {
|
||||
selector: matchLabels: RedisMetadata.labels
|
||||
template: {
|
||||
metadata: labels: RedisMetadata.labels
|
||||
metadata: labels: #IstioSidecar
|
||||
spec: securityContext: seccompProfile: type: "RuntimeDefault"
|
||||
spec: {
|
||||
containers: [{
|
||||
command: [
|
||||
"redis-server",
|
||||
"/redis-master/redis.conf",
|
||||
]
|
||||
env: [{
|
||||
name: "MASTER"
|
||||
value: "true"
|
||||
}]
|
||||
image: "quay.io/holos/redis:7.2.4"
|
||||
livenessProbe: {
|
||||
initialDelaySeconds: 15
|
||||
tcpSocket: port: "redis"
|
||||
}
|
||||
name: "redis"
|
||||
ports: [{
|
||||
containerPort: 6379
|
||||
name: "redis"
|
||||
}]
|
||||
readinessProbe: {
|
||||
exec: command: [
|
||||
"redis-cli",
|
||||
"ping",
|
||||
]
|
||||
initialDelaySeconds: 5
|
||||
}
|
||||
resources: limits: cpu: "0.5"
|
||||
securityContext: {
|
||||
seccompProfile: type: "RuntimeDefault"
|
||||
allowPrivilegeEscalation: false
|
||||
capabilities: drop: ["ALL"]
|
||||
}
|
||||
volumeMounts: [{
|
||||
mountPath: "/redis-master-data"
|
||||
name: "data"
|
||||
}, {
|
||||
mountPath: "/redis-master"
|
||||
name: "config"
|
||||
}]
|
||||
}]
|
||||
volumes: [{
|
||||
emptyDir: {}
|
||||
name: "data"
|
||||
}, {
|
||||
configMap: name: RedisMetadata.name
|
||||
name: "config"
|
||||
}]
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
Service: (RedisMetadata.name): #Service & {
|
||||
metadata: RedisMetadata
|
||||
spec: selector: RedisMetadata.labels
|
||||
spec: type: "ClusterIP"
|
||||
spec: ports: [{
|
||||
name: "redis"
|
||||
port: 6379
|
||||
protocol: "TCP"
|
||||
targetPort: 6379
|
||||
}]
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -2,11 +2,15 @@ package holos
|
||||
|
||||
import h "github.com/holos-run/holos/api/v1alpha1"
|
||||
|
||||
import "strings"
|
||||
|
||||
// #Projects is a map of all the projects in the platform.
|
||||
#Projects: [Name=_]: #Project & {name: Name}
|
||||
|
||||
#Project: {
|
||||
name: string
|
||||
// resourceId is the zitadel project Resource ID
|
||||
resourceId: number
|
||||
let ProjectName = name
|
||||
description: string
|
||||
environments: [Name=string]: #Environment & {
|
||||
@@ -17,11 +21,21 @@ import h "github.com/holos-run/holos/api/v1alpha1"
|
||||
name: Name
|
||||
project: ProjectName
|
||||
}
|
||||
domain: string | *#Platform.org.domain
|
||||
|
||||
// authProxyPrefix is the path routed to the ext auth proxy.
|
||||
authProxyPrefix: string | *"/holos/oidc"
|
||||
// authProxyOrgDomain is the primary org domain for zitadel.
|
||||
authProxyOrgDomain: string | *#Platform.org.domain
|
||||
// authProxyIssuer is the issuer url
|
||||
authProxyIssuer: string | *"https://login.\(#Platform.org.domain)"
|
||||
|
||||
// hosts are short hostnames to configure for the project.
|
||||
// Each value is routed to every environment in the project as a dns prefix.
|
||||
hosts: [Name=string]: #Host & {name: Name}
|
||||
// clusters are the cluster names the project is configured on.
|
||||
clusters: [Name=string]: #Cluster & {name: Name}
|
||||
clusterNames: [for c in clusters {c.name}]
|
||||
|
||||
// managedNamespaces ensures project namespaces have SecretStores that can sync ExternalSecrets from the provisioner cluster.
|
||||
managedNamespaces: {
|
||||
@@ -61,16 +75,48 @@ import h "github.com/holos-run/holos/api/v1alpha1"
|
||||
stage: string | "dev" | "prod"
|
||||
slug: "\(name)-\(project)"
|
||||
namespace: "\(name)-\(project)"
|
||||
dnsSegments: [...string] | *[name]
|
||||
stageSlug: "\(stage)-\(project)"
|
||||
|
||||
// envSegments are the env portion of the dns segments
|
||||
envSegments: [...string] | *[name]
|
||||
// stageSegments are the stage portion of the dns segments
|
||||
stageSegments: [...string] | *[stage]
|
||||
|
||||
// #host provides a hostname
|
||||
// Refer to: https://github.com/holos-run/holos/issues/66#issuecomment-2027562626
|
||||
#host: {
|
||||
name: string
|
||||
cluster?: string
|
||||
clusterSegments: [...string]
|
||||
if cluster != _|_ {
|
||||
clusterSegments: [cluster]
|
||||
}
|
||||
let SEGMENTS = envSegments + [name] + stageSegments + clusterSegments + [#Platform.org.domain]
|
||||
let NAMESEGMENTS = ["https"] + SEGMENTS
|
||||
host: {
|
||||
name: strings.Join(SEGMENTS, ".")
|
||||
port: {
|
||||
name: strings.Replace(strings.Join(NAMESEGMENTS, "-"), ".", "-", -1)
|
||||
number: 443
|
||||
protocol: "HTTPS"
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#Stage: {
|
||||
name: string
|
||||
project: string
|
||||
slug: "\(name)-\(project)"
|
||||
// namespace is the system namespace for the project stage
|
||||
namespace: "\(name)-\(project)-system"
|
||||
// Manage a system namespace for each stage
|
||||
namespaces: [Name=_]: name: Name
|
||||
namespaces: "\(name)-\(project)-system": _
|
||||
namespaces: (namespace): _
|
||||
// stageSegments are the stage portion of the dns segments
|
||||
stageSegments: [...string] | *[name]
|
||||
// authProxyClientID is the ClientID registered with the oidc issuer.
|
||||
authProxyClientID: string
|
||||
}
|
||||
|
||||
#Feature: {
|
||||
@@ -92,3 +138,58 @@ import h "github.com/holos-run/holos/api/v1alpha1"
|
||||
metadata: name: Name
|
||||
}
|
||||
}
|
||||
|
||||
// #EnvHosts provides hostnames given a project and environment.
|
||||
// Refer to https://github.com/holos-run/holos/issues/66#issuecomment-2027562626
|
||||
#EnvHosts: {
|
||||
project: #Project & {name: env.project}
|
||||
env: #Environment
|
||||
|
||||
hosts: {
|
||||
for host in project.hosts {
|
||||
// globally scoped hostname
|
||||
let HOST = (env.#host & {name: host.name}).host
|
||||
(HOST.name): HOST
|
||||
|
||||
// cluster scoped hostname
|
||||
for Cluster in project.clusters {
|
||||
let HOST = (env.#host & {name: host.name, cluster: Cluster.name}).host
|
||||
(HOST.name): HOST
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// #StageDomains provides hostnames given a project and stage. Primarily for the
|
||||
// auth proxy.
|
||||
// Refer to https://github.com/holos-run/holos/issues/66#issuecomment-2027562626
|
||||
#StageDomains: {
|
||||
// names are the leading prefix names to create hostnames for.
|
||||
// this is a two level list to support strings.Join()
|
||||
prefixes: [...[...string]] | *[[]]
|
||||
stage: #Stage
|
||||
project: #Project & {
|
||||
name: stage.project
|
||||
}
|
||||
|
||||
// blank segment for the global domain plus each cluster in the project.
|
||||
let ClusterSegments = [[], for cluster in project.clusters {[cluster.name]}]
|
||||
|
||||
hosts: {
|
||||
for prefix in prefixes {
|
||||
for ClusterSegment in ClusterSegments {
|
||||
let SEGMENTS = prefix + [project.name] + stage.stageSegments + ClusterSegment + [project.domain]
|
||||
let NAMESEGMENTS = ["https"] + SEGMENTS
|
||||
let HOSTNAME = strings.Join(SEGMENTS, ".")
|
||||
(HOSTNAME): {
|
||||
name: HOSTNAME
|
||||
port: {
|
||||
name: strings.Replace(strings.Join(NAMESEGMENTS, "-"), ".", "-", -1)
|
||||
number: 443
|
||||
protocol: "HTTPS"
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -233,6 +233,9 @@ _apiVersion: "holos.run/v1alpha1"
|
||||
}
|
||||
// clusterNames represents the set of clusters the namespace is managed on. Usually all clusters.
|
||||
clusterNames: [...string]
|
||||
for cluster in clusterNames {
|
||||
clusters: (cluster): name: cluster
|
||||
}
|
||||
}
|
||||
|
||||
// #ManagedNamepsaces is the union of all namespaces across all cluster types and optional services.
|
||||
@@ -299,3 +302,77 @@ _apiVersion: "holos.run/v1alpha1"
|
||||
// #IsPrimaryCluster is true if the cluster being rendered is the primary cluster
|
||||
// Used by the iam project to determine where https://login.example.com is active.
|
||||
#IsPrimaryCluster: bool & #ClusterName == #Platform.primaryCluster.name
|
||||
|
||||
// #GatewayServer defines the value of the istio Gateway.spec.servers field.
|
||||
#GatewayServer: {
|
||||
// The ip or the Unix domain socket to which the listener should
|
||||
// be bound to.
|
||||
bind?: string
|
||||
defaultEndpoint?: string
|
||||
|
||||
// One or more hosts exposed by this gateway.
|
||||
hosts: [...string]
|
||||
|
||||
// An optional name of the server, when set must be unique across
|
||||
// all servers.
|
||||
name?: string
|
||||
|
||||
// The Port on which the proxy should listen for incoming
|
||||
// connections.
|
||||
port: {
|
||||
// Label assigned to the port.
|
||||
name: string
|
||||
|
||||
// A valid non-negative integer port number.
|
||||
number: int
|
||||
|
||||
// The protocol exposed on the port.
|
||||
protocol: string
|
||||
targetPort?: int
|
||||
}
|
||||
|
||||
// Set of TLS related options that govern the server's behavior.
|
||||
tls?: {
|
||||
// REQUIRED if mode is `MUTUAL` or `OPTIONAL_MUTUAL`.
|
||||
caCertificates?: string
|
||||
|
||||
// Optional: If specified, only support the specified cipher list.
|
||||
cipherSuites?: [...string]
|
||||
|
||||
// For gateways running on Kubernetes, the name of the secret that
|
||||
// holds the TLS certs including the CA certificates.
|
||||
credentialName?: string
|
||||
|
||||
// If set to true, the load balancer will send a 301 redirect for
|
||||
// all http connections, asking the clients to use HTTPS.
|
||||
httpsRedirect?: bool
|
||||
|
||||
// Optional: Maximum TLS protocol version.
|
||||
maxProtocolVersion?: "TLS_AUTO" | "TLSV1_0" | "TLSV1_1" | "TLSV1_2" | "TLSV1_3"
|
||||
|
||||
// Optional: Minimum TLS protocol version.
|
||||
minProtocolVersion?: "TLS_AUTO" | "TLSV1_0" | "TLSV1_1" | "TLSV1_2" | "TLSV1_3"
|
||||
|
||||
// Optional: Indicates whether connections to this port should be
|
||||
// secured using TLS.
|
||||
mode?: "PASSTHROUGH" | "SIMPLE" | "MUTUAL" | "AUTO_PASSTHROUGH" | "ISTIO_MUTUAL" | "OPTIONAL_MUTUAL"
|
||||
|
||||
// REQUIRED if mode is `SIMPLE` or `MUTUAL`.
|
||||
privateKey?: string
|
||||
|
||||
// REQUIRED if mode is `SIMPLE` or `MUTUAL`.
|
||||
serverCertificate?: string
|
||||
|
||||
// A list of alternate names to verify the subject identity in the
|
||||
// certificate presented by the client.
|
||||
subjectAltNames?: [...string]
|
||||
|
||||
// An optional list of hex-encoded SHA-256 hashes of the
|
||||
// authorized client certificates.
|
||||
verifyCertificateHash?: [...string]
|
||||
|
||||
// An optional list of base64-encoded SHA-256 hashes of the SPKIs
|
||||
// of authorized client certificates.
|
||||
verifyCertificateSpki?: [...string]
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1 +1 @@
|
||||
1
|
||||
3
|
||||
|
||||
Reference in New Issue
Block a user