diff --git a/tenants/escolinha/00-namespace-quota-netpol.yaml b/tenants/escolinha/00-namespace-quota-netpol.yaml new file mode 100644 index 0000000..7b30e8d --- /dev/null +++ b/tenants/escolinha/00-namespace-quota-netpol.yaml @@ -0,0 +1,64 @@ +# Silo "demo" — isolamento do tenant (namespace + quota + limites + rede) +apiVersion: v1 +kind: Namespace +metadata: + name: escolinha-prod + labels: + name: escolinha-prod + tenant: escolinha + athleticmap.io/tier: pilot +--- +apiVersion: v1 +kind: ResourceQuota +metadata: + name: tenant-quota + namespace: escolinha-prod +spec: + hard: + requests.cpu: "4" + requests.memory: 6Gi + limits.cpu: "20" + limits.memory: 16Gi + pods: "30" + persistentvolumeclaims: "4" +--- +apiVersion: v1 +kind: LimitRange +metadata: + name: defaults + namespace: escolinha-prod +spec: + limits: + - type: Container + default: + cpu: 500m + memory: 512Mi + defaultRequest: + cpu: 100m + memory: 128Mi +--- +apiVersion: networking.k8s.io/v1 +kind: NetworkPolicy +metadata: + name: deny-cross-tenant + namespace: escolinha-prod +spec: + podSelector: {} + policyTypes: [Ingress, Egress] + ingress: + - from: + - podSelector: {} + - namespaceSelector: + matchLabels: + kubernetes.io/metadata.name: kube-system + egress: + - to: # intra-namespace (pods) + ClusterIPs (VIP de service, pre-DNAT) + - podSelector: {} + - ipBlock: { cidr: 10.43.0.0/16 } + - to: # DNS (CoreDNS em kube-system) + - namespaceSelector: + matchLabels: + kubernetes.io/metadata.name: kube-system + ports: + - { protocol: UDP, port: 53 } + - { protocol: TCP, port: 53 } diff --git a/tenants/escolinha/05-sealed-db-credentials.yaml b/tenants/escolinha/05-sealed-db-credentials.yaml new file mode 100644 index 0000000..0185bd2 --- /dev/null +++ b/tenants/escolinha/05-sealed-db-credentials.yaml @@ -0,0 +1,15 @@ +--- +apiVersion: bitnami.com/v1alpha1 +kind: SealedSecret +metadata: + creationTimestamp: null + name: db-credentials + namespace: escolinha-prod +spec: + encryptedData: + password: AgB2QaOuIFLe3rxIFaZ/ppM9uV/6waytab4xqGJ81bKkoxDKZJ4ls4Wdw1nOoRemFC75uJMSfdC1S9on+kbN54mvEjpuvZY6jHEKT4LBWPGl+KiHnKZJyqNil90cSBrT7ZpaPJC6FvLkOuENJHipfnsvcyAnuX1Udqbbc6uIjm7Yb9DPg+RGRTjD4j2Wcid7JrA7AhK8cePUO3a5ZDDdCOwqVHS1Z3KYuSmZ5HW4F/NnAU86cgkYUrCRm7Uc+DzRfoBJErW6EIpg+lkDIW2yMVHP/zYhi69E7gDwYtiBN2tdi/9qjLK/oCQYEHCeqqClyUefmjcwu5K3olsRAMxf1TfH/1VRPM+Kk7BcNa7uf6UN/X6kcTN487HskHVMzVK0DFRlysODqoZIOGfstcEzga+FncyC6xXVMOaYczn8a4cQLHpnX5Dzq4tHeBUf0xxMeYDwZzr54Euzfz6t3Hp/zLz97PO5HF8rHKhFEjfr4AEyyvPW73+eG+PDg2P/hUpRXueBimntpXGBEEob1ZJCiDt44KUVLgVFDUV6YeBtN3kNJc58jv4hZmZdPdpRLhTmlFsTYOHU8HrMlegyJOiQstGWQF1AYeJAfe3WtwZ+LHBCb3rxCC4nyKh+nFgkxU2LmJaWmPCo8FCWKUzG1bBI4lzxwI2oKa/mlWbjhIdjcdcU114gcT0+h5MbqfKtZoJKxiwRYDEH679SZE2/tZe5cfi19TuzC4YUFDiBUXCj + template: + metadata: + creationTimestamp: null + name: db-credentials + namespace: escolinha-prod diff --git a/tenants/escolinha/06-sealed-keycloak-admin.yaml b/tenants/escolinha/06-sealed-keycloak-admin.yaml new file mode 100644 index 0000000..9a8a4f1 --- /dev/null +++ b/tenants/escolinha/06-sealed-keycloak-admin.yaml @@ -0,0 +1,15 @@ +--- +apiVersion: bitnami.com/v1alpha1 +kind: SealedSecret +metadata: + creationTimestamp: null + name: keycloak-admin + namespace: escolinha-prod +spec: + encryptedData: + password: AgCpLPi/GqPeAFKad4SOaYFbpPQwQdXYcwWlKcrOby5fyzXBiBwau0BOrIAcOjbThzNrrFUsg8FxnFPBfdvfosW+aRpwoQSWoiJQ4/qfLYINZD9NWzCWAzQDvcnK1wqyODw5RmD/ugQK3g7ZWhvKys7lrjfyQQGldowMK1+XIpoJuC5sAVLldyJ08kBxp7mtKH/Xfb5rWMalmqk4PwueCkZFI65FkCxI/gAlq8inrtYWVrtNYHe1nTGfmbZfRGmbllTa+wuctUjs6SMJ2zj7fislgh2uv+Ihl2e4TOAoqC8ZreCknRL/ym9H6Pqa5lOhQqpfT1GOFrRH7PEXiQql55nczz44jUq4iplXFY3EvVd6hK06itw3eLSlLGhXPeRrOJuUB0DJSFR6nYOqXguMMcMYB/lpuYpxRRocVCygE2snIadTK2DIWxQ0XJhKwxwBcvO2N8r2hVJxgABlVecnErzVodeS6D53JiosP921MefUHQOPZtrql86AFjihdc2gZJF6N54bNOXRt5bP11zyHDLGBzIAWbdXvSVTRZ+i5tUHib2gnvdsLfD2gdgvorzbkcXtgI6IKdd4SaO/yxuS4sgtMJ5FxSkbvyNUw5k/NWmuHsifENMbsZG0laWnkHpqW3Vxzq+dJsdUm4wIyS0vdDxQSbyPy+ZF4CRyYqgE9+2cw6EF5WDA9iaMjfjJVND5Xr7cZG09GF96o+MgAVrDW1KFaEgAkAncHXuwULt4 + template: + metadata: + creationTimestamp: null + name: keycloak-admin + namespace: escolinha-prod diff --git a/tenants/escolinha/10-postgres.yaml b/tenants/escolinha/10-postgres.yaml new file mode 100644 index 0000000..cbb3266 --- /dev/null +++ b/tenants/escolinha/10-postgres.yaml @@ -0,0 +1,84 @@ +# PostgreSQL dedicado do tenant demo (banco da aplicação + banco do Keycloak) +apiVersion: v1 +kind: ConfigMap +metadata: + name: pg-initdb + namespace: escolinha-prod +data: + 01-keycloak.sql: | + CREATE DATABASE keycloak; +--- +apiVersion: v1 +kind: PersistentVolumeClaim +metadata: + name: postgres-data + namespace: escolinha-prod +spec: + accessModes: [ReadWriteOnce] + storageClassName: local-path + resources: + requests: + storage: 5Gi +--- +apiVersion: apps/v1 +kind: Deployment +metadata: + name: postgres + namespace: escolinha-prod +spec: + replicas: 1 + selector: + matchLabels: + app: postgres + template: + metadata: + labels: + app: postgres + spec: + containers: + - name: postgres + image: postgres:16 + args: ["-c", "max_connections=300"] + ports: + - containerPort: 5432 + env: + - name: POSTGRES_DB + value: athleticmap + - name: POSTGRES_USER + value: atm + - name: POSTGRES_PASSWORD + valueFrom: + secretKeyRef: + name: db-credentials + key: password + - name: PGDATA + value: /var/lib/postgresql/data/pgdata + volumeMounts: + - name: data + mountPath: /var/lib/postgresql/data + - name: initdb + mountPath: /docker-entrypoint-initdb.d + readinessProbe: + exec: + command: ["pg_isready", "-U", "atm", "-d", "athleticmap"] + initialDelaySeconds: 10 + periodSeconds: 5 + volumes: + - name: data + persistentVolumeClaim: + claimName: postgres-data + - name: initdb + configMap: + name: pg-initdb +--- +apiVersion: v1 +kind: Service +metadata: + name: postgres + namespace: escolinha-prod +spec: + selector: + app: postgres + ports: + - port: 5432 + targetPort: 5432 diff --git a/tenants/escolinha/20-keycloak.yaml b/tenants/escolinha/20-keycloak.yaml new file mode 100644 index 0000000..6e3aa8a --- /dev/null +++ b/tenants/escolinha/20-keycloak.yaml @@ -0,0 +1,86 @@ +# Keycloak dedicado do tenant demo (IdP do silo) — modo dev, persistindo no Postgres +apiVersion: apps/v1 +kind: Deployment +metadata: + name: keycloak + namespace: escolinha-prod +spec: + replicas: 1 + selector: + matchLabels: + app: keycloak + template: + metadata: + labels: + app: keycloak + spec: + containers: + - name: keycloak + image: quay.io/keycloak/keycloak:26.0 + args: ["start-dev", "--import-realm"] + env: + - name: KC_DB + value: postgres + - name: KC_DB_URL + value: "jdbc:postgresql://postgres:5432/keycloak" + - name: KC_DB_USERNAME + value: atm + - name: KC_DB_PASSWORD + valueFrom: + secretKeyRef: + name: db-credentials + key: password + - name: KC_BOOTSTRAP_ADMIN_USERNAME + value: admin + - name: KC_BOOTSTRAP_ADMIN_PASSWORD + valueFrom: + secretKeyRef: + name: keycloak-admin + key: password + - name: KC_HEALTH_ENABLED + value: "true" + - name: KC_HTTP_ENABLED + value: "true" + - name: KC_PROXY_HEADERS + value: xforwarded + - name: KC_HOSTNAME + value: "auth-escolinha.athleticmap.influxdigital.com.br" + - name: KC_HOSTNAME_STRICT + value: "true" + ports: + - containerPort: 8080 + - containerPort: 9000 + resources: + requests: + cpu: 250m + memory: 512Mi + limits: + cpu: "1" + memory: 1Gi + readinessProbe: + httpGet: + path: /health/ready + port: 9000 + initialDelaySeconds: 30 + periodSeconds: 10 + failureThreshold: 40 + volumeMounts: + - name: realm-import + mountPath: /opt/keycloak/data/import + readOnly: true + volumes: + - name: realm-import + configMap: + name: kc-realm-import +--- +apiVersion: v1 +kind: Service +metadata: + name: keycloak + namespace: escolinha-prod +spec: + selector: + app: keycloak + ports: + - port: 8080 + targetPort: 8080 diff --git a/tenants/escolinha/30-apps-stubs.yaml b/tenants/escolinha/30-apps-stubs.yaml new file mode 100644 index 0000000..72aed38 --- /dev/null +++ b/tenants/escolinha/30-apps-stubs.yaml @@ -0,0 +1,75 @@ +# Apps do tenant escolinha: +# - backend: Spring Boot OAuth2 Resource Server (imagem athletic-map-backend:1.0, importada no k3s) +# - frontend: SPA OIDC Authorization Code + PKCE (keycloak-js) chamando /api/me +# - bff: stub (whoami) — sera substituido na evolucao +--- +apiVersion: apps/v1 +kind: Deployment +metadata: { name: backend, namespace: escolinha-prod, labels: { app: backend } } +spec: + replicas: 1 + selector: { matchLabels: { app: backend } } + template: + metadata: { labels: { app: backend } } + spec: + containers: + - name: backend + image: docker.io/library/athletic-map-backend:1.3 + imagePullPolicy: Never + env: + - { name: ATM_JWK_SET_URI, value: "http://keycloak:8080/realms/athleticmap/protocol/openid-connect/certs" } + - { name: ATM_ISSUER, value: "https://auth-escolinha.athleticmap.influxdigital.com.br/realms/athleticmap" } + - { name: ATM_TENANT, value: "escolinha" } + ports: [{ containerPort: 8083 }] + readinessProbe: + httpGet: { path: /api/public/health, port: 8083 } + initialDelaySeconds: 20 + periodSeconds: 10 + failureThreshold: 24 +--- +apiVersion: v1 +kind: Service +metadata: { name: backend, namespace: escolinha-prod, labels: { app: backend } } +spec: + selector: { app: backend } + ports: [{ name: http, port: 80, targetPort: 8083 }] +--- +apiVersion: networking.k8s.io/v1 +kind: Ingress +metadata: + name: escolinha + namespace: escolinha-prod + annotations: + cert-manager.io/cluster-issuer: letsencrypt-prod + traefik.ingress.kubernetes.io/router.middlewares: escolinha-prod-redirect-https@kubernetescrd +spec: + ingressClassName: traefik + tls: + - hosts: + - escolinha.athleticmap.influxdigital.com.br + - auth-escolinha.athleticmap.influxdigital.com.br + secretName: escolinha-tls + rules: + - host: escolinha.athleticmap.influxdigital.com.br + http: + paths: + - { path: /api/configuracao, pathType: Prefix, backend: { service: { name: servico-configuracao, port: { number: 80 } } } } + - { path: /api/cadastro, pathType: Prefix, backend: { service: { name: servico-cadastro, port: { number: 80 } } } } + - { path: /bff, pathType: Prefix, backend: { service: { name: servico-bff, port: { number: 80 } } } } + - { path: /api/organizacao, pathType: Prefix, backend: { service: { name: servico-organizacao, port: { number: 80 } } } } + - { path: /api/planejamento, pathType: Prefix, backend: { service: { name: servico-planejamento, port: { number: 80 } } } } + - { path: /api/campeonato, pathType: Prefix, backend: { service: { name: servico-campeonato, port: { number: 80 } } } } + - { path: /api/saude, pathType: Prefix, backend: { service: { name: servico-saude, port: { number: 80 } } } } + - { path: /api/financeiro, pathType: Prefix, backend: { service: { name: servico-financeiro, port: { number: 80 } } } } + - { path: /api/documentos, pathType: Prefix, backend: { service: { name: servico-documentos, port: { number: 80 } } } } + - { path: /api/administrativo, pathType: Prefix, backend: { service: { name: servico-administrativo, port: { number: 80 } } } } + - { path: /api/assistencia-social, pathType: Prefix, backend: { service: { name: servico-assistencia-social, port: { number: 80 } } } } + - { path: /api/pedagogico, pathType: Prefix, backend: { service: { name: servico-pedagogico, port: { number: 80 } } } } + - { path: /api/nutricao, pathType: Prefix, backend: { service: { name: servico-nutricao, port: { number: 80 } } } } + - { path: /api/agenda, pathType: Prefix, backend: { service: { name: servico-agenda, port: { number: 80 } } } } + - { path: /api, pathType: Prefix, backend: { service: { name: backend, port: { number: 80 } } } } + - { path: /, pathType: Prefix, backend: { service: { name: frontend-spa, port: { number: 80 } } } } + - host: auth-escolinha.athleticmap.influxdigital.com.br + http: + paths: + - { path: /, pathType: Prefix, backend: { service: { name: keycloak, port: { number: 8080 } } } } diff --git a/tenants/escolinha/35-realm-import-cm.yaml b/tenants/escolinha/35-realm-import-cm.yaml new file mode 100644 index 0000000..3a2e8e6 --- /dev/null +++ b/tenants/escolinha/35-realm-import-cm.yaml @@ -0,0 +1,39 @@ +apiVersion: v1 +data: + athleticmap-realm.json: | + { + "realm": "athleticmap", + "enabled": true, + "displayName": "Athletic Map", + "loginWithEmailAllowed": true, + "roles": { + "realm": [ + { "name": "admin" }, + { "name": "atm_athlete" }, + { "name": "atm_trainer" }, + { "name": "atm_team_admin" }, + { "name": "atm_fed_admin" } + ] + }, + "clients": [ + { + "clientId": "spa", + "name": "Athletic Map SPA", + "publicClient": true, + "protocol": "openid-connect", + "standardFlowEnabled": true, + "implicitFlowEnabled": false, + "directAccessGrantsEnabled": false, + "redirectUris": ["https://escolinha.athleticmap.influxdigital.com.br/*"], + "webOrigins": ["https://escolinha.athleticmap.influxdigital.com.br"], + "attributes": { + "pkce.code.challenge.method": "S256", + "post.logout.redirect.uris": "https://escolinha.athleticmap.influxdigital.com.br/*" + } + } + ] + } +kind: ConfigMap +metadata: + name: kc-realm-import + namespace: escolinha-prod diff --git a/tenants/escolinha/40-middleware.yaml b/tenants/escolinha/40-middleware.yaml new file mode 100644 index 0000000..6bd024c --- /dev/null +++ b/tenants/escolinha/40-middleware.yaml @@ -0,0 +1,10 @@ +# Redirect HTTP->HTTPS (Traefik). Aplicado por-ingress (nao afeta o desafio ACME do cert-manager). +apiVersion: traefik.io/v1alpha1 +kind: Middleware +metadata: + name: redirect-https + namespace: escolinha-prod +spec: + redirectScheme: + scheme: https + permanent: true diff --git a/tenants/escolinha/50-backup.yaml b/tenants/escolinha/50-backup.yaml new file mode 100644 index 0000000..7f05cec --- /dev/null +++ b/tenants/escolinha/50-backup.yaml @@ -0,0 +1,57 @@ +# Backup diario do Postgres do tenant demo (pg_dump -> PVC, retencao 7 dias) +apiVersion: v1 +kind: PersistentVolumeClaim +metadata: + name: pg-backups + namespace: escolinha-prod +spec: + accessModes: [ReadWriteOnce] + storageClassName: local-path + resources: + requests: + storage: 5Gi +--- +apiVersion: batch/v1 +kind: CronJob +metadata: + name: pg-backup + namespace: escolinha-prod +spec: + schedule: "0 2 * * *" + successfulJobsHistoryLimit: 3 + failedJobsHistoryLimit: 3 + concurrencyPolicy: Forbid + jobTemplate: + spec: + template: + spec: + restartPolicy: OnFailure + containers: + - name: pg-backup + image: postgres:16 + env: + - name: PGPASSWORD + valueFrom: + secretKeyRef: + name: db-credentials + key: password + command: ["/bin/sh", "-c"] + args: + - | + set -eo pipefail + echo "aguardando postgres ficar acessivel..." + for i in $(seq 1 30); do pg_isready -h postgres -U atm -t 3 >/dev/null 2>&1 && break; sleep 2; done + TS=$(date +%Y%m%d-%H%M%S) + for DB in athleticmap keycloak; do + echo "dump $DB ..." + pg_dump -h postgres -U atm -d "$DB" | gzip > "/backups/${DB}-${TS}.sql.gz" + done + find /backups -name '*.sql.gz' -mtime +7 -delete + echo "backups atuais:"; ls -lh /backups | tail -20 + volumeMounts: + - name: backups + mountPath: /backups + volumes: + - name: backups + persistentVolumeClaim: + claimName: pg-backups diff --git a/tenants/escolinha/60-monitoring-netpol.yaml b/tenants/escolinha/60-monitoring-netpol.yaml new file mode 100644 index 0000000..daf4911 --- /dev/null +++ b/tenants/escolinha/60-monitoring-netpol.yaml @@ -0,0 +1,19 @@ +# Permite o namespace 'monitoring' (Prometheus) raspar o backend (porta 8083). Additivo a deny-cross-tenant. +apiVersion: networking.k8s.io/v1 +kind: NetworkPolicy +metadata: + name: allow-monitoring + namespace: escolinha-prod +spec: + podSelector: + matchLabels: + app: backend + policyTypes: [Ingress] + ingress: + - from: + - namespaceSelector: + matchLabels: + kubernetes.io/metadata.name: monitoring + ports: + - protocol: TCP + port: 8083 diff --git a/tenants/escolinha/70-servico-configuracao.yaml b/tenants/escolinha/70-servico-configuracao.yaml new file mode 100644 index 0000000..609a964 --- /dev/null +++ b/tenants/escolinha/70-servico-configuracao.yaml @@ -0,0 +1,60 @@ +# Bounded context "configuracao" como app proprio (ADR-0002): Deployment/pod +# medivel = centro de custo. Schema proprio "configuracao" no Postgres do silo +# (criado pelo Flyway). Roteado em /api/configuracao no ingress do tenant. +--- +apiVersion: apps/v1 +kind: Deployment +metadata: + name: servico-configuracao + namespace: escolinha-prod + labels: + app: servico-configuracao + athleticmap.io/contexto: configuracao +spec: + replicas: 1 + selector: { matchLabels: { app: servico-configuracao } } + template: + metadata: + labels: + app: servico-configuracao + athleticmap.io/contexto: configuracao + spec: + containers: + - name: servico-configuracao + image: docker.io/library/servico-configuracao:1.2 + imagePullPolicy: Never + ports: [{ containerPort: 8083 }] + env: + - { name: SPRING_DATASOURCE_URL, value: "jdbc:postgresql://postgres:5432/athleticmap" } + - { name: SPRING_DATASOURCE_USERNAME, value: "atm" } + - { name: SPRING_DATASOURCE_HIKARI_MAXIMUM_POOL_SIZE, value: "5" } + - name: SPRING_DATASOURCE_PASSWORD + valueFrom: + secretKeyRef: { name: db-credentials, key: password } + - { name: ATM_JWK_SET_URI, value: "http://keycloak:8080/realms/athleticmap/protocol/openid-connect/certs" } + - { name: ATM_ISSUER, value: "https://auth-escolinha.athleticmap.influxdigital.com.br/realms/athleticmap" } + - { name: ATM_TENANT, value: "escolinha" } + resources: + requests: { cpu: 150m, memory: 320Mi } + limits: { cpu: "1", memory: 768Mi } + readinessProbe: + httpGet: { path: /api/configuracao/health, port: 8083 } + initialDelaySeconds: 25 + periodSeconds: 10 + failureThreshold: 30 + livenessProbe: + httpGet: { path: /actuator/health/liveness, port: 8083 } + initialDelaySeconds: 60 + periodSeconds: 20 + failureThreshold: 6 +--- +apiVersion: v1 +kind: Service +metadata: + name: servico-configuracao + namespace: escolinha-prod + labels: + app: servico-configuracao +spec: + selector: { app: servico-configuracao } + ports: [{ name: http, port: 80, targetPort: 8083 }] diff --git a/tenants/escolinha/72-servico-organizacao.yaml b/tenants/escolinha/72-servico-organizacao.yaml new file mode 100644 index 0000000..209d8cb --- /dev/null +++ b/tenants/escolinha/72-servico-organizacao.yaml @@ -0,0 +1,59 @@ +# Bounded context "organizacao" como app proprio (ADR-0002): Elenco e Turma. +# Schema proprio "organizacao" (Flyway). Roteado em /api/organizacao no ingress. +--- +apiVersion: apps/v1 +kind: Deployment +metadata: + name: servico-organizacao + namespace: escolinha-prod + labels: + app: servico-organizacao + athleticmap.io/contexto: organizacao +spec: + replicas: 1 + selector: { matchLabels: { app: servico-organizacao } } + template: + metadata: + labels: + app: servico-organizacao + athleticmap.io/contexto: organizacao + spec: + containers: + - name: servico-organizacao + image: docker.io/library/servico-organizacao:1.2 + imagePullPolicy: Never + ports: [{ containerPort: 8083 }] + env: + - { name: SPRING_DATASOURCE_URL, value: "jdbc:postgresql://postgres:5432/athleticmap" } + - { name: SPRING_DATASOURCE_USERNAME, value: "atm" } + - { name: SPRING_DATASOURCE_HIKARI_MAXIMUM_POOL_SIZE, value: "5" } + - name: SPRING_DATASOURCE_PASSWORD + valueFrom: + secretKeyRef: { name: db-credentials, key: password } + - { name: ATM_JWK_SET_URI, value: "http://keycloak:8080/realms/athleticmap/protocol/openid-connect/certs" } + - { name: ATM_ISSUER, value: "https://auth-escolinha.athleticmap.influxdigital.com.br/realms/athleticmap" } + - { name: ATM_TENANT, value: "escolinha" } + resources: + requests: { cpu: 150m, memory: 320Mi } + limits: { cpu: "1", memory: 768Mi } + readinessProbe: + httpGet: { path: /api/organizacao/health, port: 8083 } + initialDelaySeconds: 25 + periodSeconds: 10 + failureThreshold: 30 + livenessProbe: + httpGet: { path: /actuator/health/liveness, port: 8083 } + initialDelaySeconds: 60 + periodSeconds: 20 + failureThreshold: 6 +--- +apiVersion: v1 +kind: Service +metadata: + name: servico-organizacao + namespace: escolinha-prod + labels: + app: servico-organizacao +spec: + selector: { app: servico-organizacao } + ports: [{ name: http, port: 80, targetPort: 8083 }] diff --git a/tenants/escolinha/74-servico-planejamento.yaml b/tenants/escolinha/74-servico-planejamento.yaml new file mode 100644 index 0000000..84f48cb --- /dev/null +++ b/tenants/escolinha/74-servico-planejamento.yaml @@ -0,0 +1,59 @@ +# Bounded context "planejamento" como app proprio (ADR-0002): Treino e Plano de Aula. +# Schema proprio "planejamento" (Flyway). Roteado em /api/planejamento no ingress. +--- +apiVersion: apps/v1 +kind: Deployment +metadata: + name: servico-planejamento + namespace: escolinha-prod + labels: + app: servico-planejamento + athleticmap.io/contexto: planejamento +spec: + replicas: 1 + selector: { matchLabels: { app: servico-planejamento } } + template: + metadata: + labels: + app: servico-planejamento + athleticmap.io/contexto: planejamento + spec: + containers: + - name: servico-planejamento + image: docker.io/library/servico-planejamento:1.3 + imagePullPolicy: Never + ports: [{ containerPort: 8083 }] + env: + - { name: SPRING_DATASOURCE_URL, value: "jdbc:postgresql://postgres:5432/athleticmap" } + - { name: SPRING_DATASOURCE_USERNAME, value: "atm" } + - { name: SPRING_DATASOURCE_HIKARI_MAXIMUM_POOL_SIZE, value: "5" } + - name: SPRING_DATASOURCE_PASSWORD + valueFrom: + secretKeyRef: { name: db-credentials, key: password } + - { name: ATM_JWK_SET_URI, value: "http://keycloak:8080/realms/athleticmap/protocol/openid-connect/certs" } + - { name: ATM_ISSUER, value: "https://auth-escolinha.athleticmap.influxdigital.com.br/realms/athleticmap" } + - { name: ATM_TENANT, value: "escolinha" } + resources: + requests: { cpu: 150m, memory: 320Mi } + limits: { cpu: "1", memory: 768Mi } + readinessProbe: + httpGet: { path: /api/planejamento/health, port: 8083 } + initialDelaySeconds: 25 + periodSeconds: 10 + failureThreshold: 30 + livenessProbe: + httpGet: { path: /actuator/health/liveness, port: 8083 } + initialDelaySeconds: 60 + periodSeconds: 20 + failureThreshold: 6 +--- +apiVersion: v1 +kind: Service +metadata: + name: servico-planejamento + namespace: escolinha-prod + labels: + app: servico-planejamento +spec: + selector: { app: servico-planejamento } + ports: [{ name: http, port: 80, targetPort: 8083 }] diff --git a/tenants/escolinha/76-servico-campeonato.yaml b/tenants/escolinha/76-servico-campeonato.yaml new file mode 100644 index 0000000..d6af04f --- /dev/null +++ b/tenants/escolinha/76-servico-campeonato.yaml @@ -0,0 +1,59 @@ +# Bounded context "campeonato" (2a onda): Campeonato (estrutura) + Jogo (sumula). +# Schema proprio "campeonato" (Flyway). Roteado em /api/campeonato no ingress. +--- +apiVersion: apps/v1 +kind: Deployment +metadata: + name: servico-campeonato + namespace: escolinha-prod + labels: + app: servico-campeonato + athleticmap.io/contexto: campeonato +spec: + replicas: 1 + selector: { matchLabels: { app: servico-campeonato } } + template: + metadata: + labels: + app: servico-campeonato + athleticmap.io/contexto: campeonato + spec: + containers: + - name: servico-campeonato + image: docker.io/library/servico-campeonato:1.9 + imagePullPolicy: Never + ports: [{ containerPort: 8083 }] + env: + - { name: SPRING_DATASOURCE_URL, value: "jdbc:postgresql://postgres:5432/athleticmap" } + - { name: SPRING_DATASOURCE_USERNAME, value: "atm" } + - { name: SPRING_DATASOURCE_HIKARI_MAXIMUM_POOL_SIZE, value: "5" } + - name: SPRING_DATASOURCE_PASSWORD + valueFrom: + secretKeyRef: { name: db-credentials, key: password } + - { name: ATM_JWK_SET_URI, value: "http://keycloak:8080/realms/athleticmap/protocol/openid-connect/certs" } + - { name: ATM_ISSUER, value: "https://auth-escolinha.athleticmap.influxdigital.com.br/realms/athleticmap" } + - { name: ATM_TENANT, value: "escolinha" } + resources: + requests: { cpu: 150m, memory: 320Mi } + limits: { cpu: "1", memory: 768Mi } + readinessProbe: + httpGet: { path: /api/campeonato/health, port: 8083 } + initialDelaySeconds: 25 + periodSeconds: 10 + failureThreshold: 30 + livenessProbe: + httpGet: { path: /actuator/health/liveness, port: 8083 } + initialDelaySeconds: 60 + periodSeconds: 20 + failureThreshold: 6 +--- +apiVersion: v1 +kind: Service +metadata: + name: servico-campeonato + namespace: escolinha-prod + labels: + app: servico-campeonato +spec: + selector: { app: servico-campeonato } + ports: [{ name: http, port: 80, targetPort: 8083 }] diff --git a/tenants/escolinha/78-servico-saude.yaml b/tenants/escolinha/78-servico-saude.yaml new file mode 100644 index 0000000..9a345da --- /dev/null +++ b/tenants/escolinha/78-servico-saude.yaml @@ -0,0 +1,69 @@ +# Bounded context "saude" (2a onda): RegistroSaude (registros por especialidade). +# Schema proprio "saude" (Flyway). Roteado em /api/saude no ingress. +--- +apiVersion: apps/v1 +kind: Deployment +metadata: + name: servico-saude + namespace: escolinha-prod + labels: + app: servico-saude + athleticmap.io/contexto: saude +spec: + replicas: 1 + selector: { matchLabels: { app: servico-saude } } + template: + metadata: + labels: + app: servico-saude + athleticmap.io/contexto: saude + spec: + containers: + - name: servico-saude + image: docker.io/library/servico-saude:1.88-garmin + imagePullPolicy: Never + ports: [{ containerPort: 8083 }] + env: + - { name: SPRING_DATASOURCE_URL, value: "jdbc:postgresql://postgres:5432/athleticmap" } + - { name: SPRING_DATASOURCE_USERNAME, value: "atm" } + - { name: SPRING_DATASOURCE_HIKARI_MAXIMUM_POOL_SIZE, value: "5" } + - name: SPRING_DATASOURCE_PASSWORD + valueFrom: + secretKeyRef: { name: db-credentials, key: password } + - { name: ATM_JWK_SET_URI, value: "http://keycloak:8080/realms/athleticmap/protocol/openid-connect/certs" } + - { name: ATM_ISSUER, value: "https://auth-escolinha.athleticmap.influxdigital.com.br/realms/athleticmap" } + - { name: ATM_TENANT, value: "escolinha" } + # Garmin: DNS devolve IPv6 primeiro e a rota IPv6 do cluster falha; forcar IPv4. + - { name: JAVA_TOOL_OPTIONS, value: "-Djava.net.preferIPv4Stack=true" } + - { name: GARMIN_REDIRECT_URI, value: "https://escolinha.athleticmap.influxdigital.com.br/api/public/integrations/garmin/callback" } + - { name: GARMIN_FRONTEND_REDIRECT, value: "https://escolinha.athleticmap.influxdigital.com.br/integracoes/garmin" } + - name: GARMIN_CLIENT_ID + valueFrom: + secretKeyRef: { name: garmin-oauth, key: GARMIN_CLIENT_ID } + - name: GARMIN_CLIENT_SECRET + valueFrom: + secretKeyRef: { name: garmin-oauth, key: GARMIN_CLIENT_SECRET } + resources: + requests: { cpu: 150m, memory: 320Mi } + limits: { cpu: "1", memory: 768Mi } + readinessProbe: + httpGet: { path: /api/saude/health, port: 8083 } + initialDelaySeconds: 25 + periodSeconds: 10 + failureThreshold: 30 + livenessProbe: + httpGet: { path: /actuator/health/liveness, port: 8083 } + initialDelaySeconds: 60 + periodSeconds: 20 + failureThreshold: 6 +--- +apiVersion: v1 +kind: Service +metadata: + name: servico-saude + namespace: escolinha-prod + labels: + app: servico-saude +spec: + selector: { app: servico-saude } + ports: [{ name: http, port: 80, targetPort: 8083 }] diff --git a/tenants/escolinha/79-servico-financeiro.yaml b/tenants/escolinha/79-servico-financeiro.yaml new file mode 100644 index 0000000..7e6a1e7 --- /dev/null +++ b/tenants/escolinha/79-servico-financeiro.yaml @@ -0,0 +1,59 @@ +# Bounded context "financeiro" (2a onda): Cobranca + ApuracaoRateio (centros de custo). +# Schema proprio "financeiro" (Flyway). Roteado em /api/financeiro no ingress. +--- +apiVersion: apps/v1 +kind: Deployment +metadata: + name: servico-financeiro + namespace: escolinha-prod + labels: + app: servico-financeiro + athleticmap.io/contexto: financeiro +spec: + replicas: 1 + selector: { matchLabels: { app: servico-financeiro } } + template: + metadata: + labels: + app: servico-financeiro + athleticmap.io/contexto: financeiro + spec: + containers: + - name: servico-financeiro + image: docker.io/library/servico-financeiro:1.4 + imagePullPolicy: Never + ports: [{ containerPort: 8083 }] + env: + - { name: SPRING_DATASOURCE_URL, value: "jdbc:postgresql://postgres:5432/athleticmap" } + - { name: SPRING_DATASOURCE_USERNAME, value: "atm" } + - { name: SPRING_DATASOURCE_HIKARI_MAXIMUM_POOL_SIZE, value: "5" } + - name: SPRING_DATASOURCE_PASSWORD + valueFrom: + secretKeyRef: { name: db-credentials, key: password } + - { name: ATM_JWK_SET_URI, value: "http://keycloak:8080/realms/athleticmap/protocol/openid-connect/certs" } + - { name: ATM_ISSUER, value: "https://auth-escolinha.athleticmap.influxdigital.com.br/realms/athleticmap" } + - { name: ATM_TENANT, value: "escolinha" } + resources: + requests: { cpu: 150m, memory: 320Mi } + limits: { cpu: "1", memory: 768Mi } + readinessProbe: + httpGet: { path: /api/financeiro/health, port: 8083 } + initialDelaySeconds: 25 + periodSeconds: 10 + failureThreshold: 30 + livenessProbe: + httpGet: { path: /actuator/health/liveness, port: 8083 } + initialDelaySeconds: 60 + periodSeconds: 20 + failureThreshold: 6 +--- +apiVersion: v1 +kind: Service +metadata: + name: servico-financeiro + namespace: escolinha-prod + labels: + app: servico-financeiro +spec: + selector: { app: servico-financeiro } + ports: [{ name: http, port: 80, targetPort: 8083 }] diff --git a/tenants/escolinha/80-servico-cadastro.yaml b/tenants/escolinha/80-servico-cadastro.yaml new file mode 100644 index 0000000..6adee6e --- /dev/null +++ b/tenants/escolinha/80-servico-cadastro.yaml @@ -0,0 +1,64 @@ +# Bounded context "cadastro" como app proprio (ADR-0002/0003): Deployment/pod +# medivel = centro de custo. Schema proprio "cadastro" no Postgres do silo +# (criado pelo Flyway). Roteado em /api/cadastro no ingress do tenant. +--- +apiVersion: apps/v1 +kind: Deployment +metadata: + name: servico-cadastro + namespace: escolinha-prod + labels: + app: servico-cadastro + athleticmap.io/contexto: cadastro +spec: + replicas: 1 + selector: { matchLabels: { app: servico-cadastro } } + template: + metadata: + labels: + app: servico-cadastro + athleticmap.io/contexto: cadastro + spec: + containers: + - name: servico-cadastro + image: docker.io/library/servico-cadastro:1.6 + imagePullPolicy: Never + ports: [{ containerPort: 8083 }] + env: + - { name: SPRING_DATASOURCE_URL, value: "jdbc:postgresql://postgres:5432/athleticmap" } + - { name: SPRING_DATASOURCE_USERNAME, value: "atm" } + - { name: SPRING_DATASOURCE_HIKARI_MAXIMUM_POOL_SIZE, value: "5" } + - name: SPRING_DATASOURCE_PASSWORD + valueFrom: + secretKeyRef: { name: db-credentials, key: password } + - { name: ATM_JWK_SET_URI, value: "http://keycloak:8080/realms/athleticmap/protocol/openid-connect/certs" } + - { name: ATM_ISSUER, value: "https://auth-escolinha.athleticmap.influxdigital.com.br/realms/athleticmap" } + - { name: ATM_TENANT, value: "escolinha" } + - { name: ATM_KC_ADMIN_ENABLED, value: "true" } + - name: ATM_KC_CLIENT_SECRET + valueFrom: + secretKeyRef: { name: kc-admin-secret, key: client-secret } + resources: + requests: { cpu: 150m, memory: 320Mi } + limits: { cpu: "1", memory: 768Mi } + readinessProbe: + httpGet: { path: /api/cadastro/health, port: 8083 } + initialDelaySeconds: 25 + periodSeconds: 10 + failureThreshold: 30 + livenessProbe: + httpGet: { path: /actuator/health/liveness, port: 8083 } + initialDelaySeconds: 60 + periodSeconds: 20 + failureThreshold: 6 +--- +apiVersion: v1 +kind: Service +metadata: + name: servico-cadastro + namespace: escolinha-prod + labels: + app: servico-cadastro +spec: + selector: { app: servico-cadastro } + ports: [{ name: http, port: 80, targetPort: 8083 }] diff --git a/tenants/escolinha/81-servico-documentos.yaml b/tenants/escolinha/81-servico-documentos.yaml new file mode 100644 index 0000000..df5c261 --- /dev/null +++ b/tenants/escolinha/81-servico-documentos.yaml @@ -0,0 +1,54 @@ +# Bounded context "documentos" (servico de anexos centralizado, ADR-0002 + decisao 2026-06-18). +# Schema proprio "documentos"; roteado em /api/documentos no ingress do tenant. +--- +apiVersion: apps/v1 +kind: Deployment +metadata: + name: servico-documentos + namespace: escolinha-prod + labels: { app: servico-documentos, athleticmap.io/contexto: documentos } +spec: + replicas: 1 + selector: { matchLabels: { app: servico-documentos } } + template: + metadata: + labels: { app: servico-documentos, athleticmap.io/contexto: documentos } + spec: + containers: + - name: servico-documentos + image: docker.io/library/servico-documentos:1.0 + imagePullPolicy: Never + ports: [{ containerPort: 8083 }] + env: + - { name: SPRING_DATASOURCE_URL, value: "jdbc:postgresql://postgres:5432/athleticmap" } + - { name: SPRING_DATASOURCE_USERNAME, value: "atm" } + - { name: SPRING_DATASOURCE_HIKARI_MAXIMUM_POOL_SIZE, value: "5" } + - name: SPRING_DATASOURCE_PASSWORD + valueFrom: + secretKeyRef: { name: db-credentials, key: password } + - { name: ATM_JWK_SET_URI, value: "http://keycloak:8080/realms/athleticmap/protocol/openid-connect/certs" } + - { name: ATM_ISSUER, value: "https://auth-escolinha.athleticmap.influxdigital.com.br/realms/athleticmap" } + - { name: ATM_TENANT, value: "escolinha" } + resources: + requests: { cpu: 100m, memory: 300Mi } + limits: { cpu: "1", memory: 640Mi } + readinessProbe: + httpGet: { path: /api/documentos/health, port: 8083 } + initialDelaySeconds: 25 + periodSeconds: 10 + failureThreshold: 30 + livenessProbe: + httpGet: { path: /actuator/health/liveness, port: 8083 } + initialDelaySeconds: 60 + periodSeconds: 20 + failureThreshold: 6 +--- +apiVersion: v1 +kind: Service +metadata: + name: servico-documentos + namespace: escolinha-prod + labels: { app: servico-documentos } +spec: + selector: { app: servico-documentos } + ports: [{ name: http, port: 80, targetPort: 8083 }] diff --git a/tenants/escolinha/82-servico-administrativo.yaml b/tenants/escolinha/82-servico-administrativo.yaml new file mode 100644 index 0000000..757bb95 --- /dev/null +++ b/tenants/escolinha/82-servico-administrativo.yaml @@ -0,0 +1,54 @@ +# Bounded context "administrativo" (contratos e gestao administrativa, ADR-0002). +# Schema proprio "administrativo"; roteado em /api/administrativo no ingress do tenant. +--- +apiVersion: apps/v1 +kind: Deployment +metadata: + name: servico-administrativo + namespace: escolinha-prod + labels: { app: servico-administrativo, athleticmap.io/contexto: administrativo } +spec: + replicas: 1 + selector: { matchLabels: { app: servico-administrativo } } + template: + metadata: + labels: { app: servico-administrativo, athleticmap.io/contexto: administrativo } + spec: + containers: + - name: servico-administrativo + image: docker.io/library/servico-administrativo:1.6 + imagePullPolicy: Never + ports: [{ containerPort: 8083 }] + env: + - { name: SPRING_DATASOURCE_URL, value: "jdbc:postgresql://postgres:5432/athleticmap" } + - { name: SPRING_DATASOURCE_USERNAME, value: "atm" } + - { name: SPRING_DATASOURCE_HIKARI_MAXIMUM_POOL_SIZE, value: "5" } + - name: SPRING_DATASOURCE_PASSWORD + valueFrom: + secretKeyRef: { name: db-credentials, key: password } + - { name: ATM_JWK_SET_URI, value: "http://keycloak:8080/realms/athleticmap/protocol/openid-connect/certs" } + - { name: ATM_ISSUER, value: "https://auth-escolinha.athleticmap.influxdigital.com.br/realms/athleticmap" } + - { name: ATM_TENANT, value: "escolinha" } + resources: + requests: { cpu: 100m, memory: 300Mi } + limits: { cpu: "1", memory: 640Mi } + readinessProbe: + httpGet: { path: /api/administrativo/health, port: 8083 } + initialDelaySeconds: 25 + periodSeconds: 10 + failureThreshold: 30 + livenessProbe: + httpGet: { path: /actuator/health/liveness, port: 8083 } + initialDelaySeconds: 60 + periodSeconds: 20 + failureThreshold: 6 +--- +apiVersion: v1 +kind: Service +metadata: + name: servico-administrativo + namespace: escolinha-prod + labels: { app: servico-administrativo } +spec: + selector: { app: servico-administrativo } + ports: [{ name: http, port: 80, targetPort: 8083 }] diff --git a/tenants/escolinha/83-servico-assistencia-social.yaml b/tenants/escolinha/83-servico-assistencia-social.yaml new file mode 100644 index 0000000..e4609ed --- /dev/null +++ b/tenants/escolinha/83-servico-assistencia-social.yaml @@ -0,0 +1,54 @@ +# Bounded context "assistencia-social" (servico social, ADR-0002). +# Schema proprio "assistencia_social"; roteado em /api/assistencia-social no ingress. +--- +apiVersion: apps/v1 +kind: Deployment +metadata: + name: servico-assistencia-social + namespace: escolinha-prod + labels: { app: servico-assistencia-social, athleticmap.io/contexto: assistencia-social } +spec: + replicas: 1 + selector: { matchLabels: { app: servico-assistencia-social } } + template: + metadata: + labels: { app: servico-assistencia-social, athleticmap.io/contexto: assistencia-social } + spec: + containers: + - name: servico-assistencia-social + image: docker.io/library/servico-assistencia-social:1.0 + imagePullPolicy: Never + ports: [{ containerPort: 8083 }] + env: + - { name: SPRING_DATASOURCE_URL, value: "jdbc:postgresql://postgres:5432/athleticmap" } + - { name: SPRING_DATASOURCE_USERNAME, value: "atm" } + - { name: SPRING_DATASOURCE_HIKARI_MAXIMUM_POOL_SIZE, value: "5" } + - name: SPRING_DATASOURCE_PASSWORD + valueFrom: + secretKeyRef: { name: db-credentials, key: password } + - { name: ATM_JWK_SET_URI, value: "http://keycloak:8080/realms/athleticmap/protocol/openid-connect/certs" } + - { name: ATM_ISSUER, value: "https://auth-escolinha.athleticmap.influxdigital.com.br/realms/athleticmap" } + - { name: ATM_TENANT, value: "escolinha" } + resources: + requests: { cpu: 100m, memory: 300Mi } + limits: { cpu: "1", memory: 640Mi } + readinessProbe: + httpGet: { path: /api/assistencia-social/health, port: 8083 } + initialDelaySeconds: 25 + periodSeconds: 10 + failureThreshold: 30 + livenessProbe: + httpGet: { path: /actuator/health/liveness, port: 8083 } + initialDelaySeconds: 60 + periodSeconds: 20 + failureThreshold: 6 +--- +apiVersion: v1 +kind: Service +metadata: + name: servico-assistencia-social + namespace: escolinha-prod + labels: { app: servico-assistencia-social } +spec: + selector: { app: servico-assistencia-social } + ports: [{ name: http, port: 80, targetPort: 8083 }] diff --git a/tenants/escolinha/84-servico-pedagogico.yaml b/tenants/escolinha/84-servico-pedagogico.yaml new file mode 100644 index 0000000..ff7d956 --- /dev/null +++ b/tenants/escolinha/84-servico-pedagogico.yaml @@ -0,0 +1,54 @@ +# Bounded context "pedagogico" (boletim / acompanhamento academico, ADR-0002). +# Schema proprio "pedagogico"; roteado em /api/pedagogico no ingress. +--- +apiVersion: apps/v1 +kind: Deployment +metadata: + name: servico-pedagogico + namespace: escolinha-prod + labels: { app: servico-pedagogico, athleticmap.io/contexto: pedagogico } +spec: + replicas: 1 + selector: { matchLabels: { app: servico-pedagogico } } + template: + metadata: + labels: { app: servico-pedagogico, athleticmap.io/contexto: pedagogico } + spec: + containers: + - name: servico-pedagogico + image: docker.io/library/servico-pedagogico:1.0 + imagePullPolicy: Never + ports: [{ containerPort: 8083 }] + env: + - { name: SPRING_DATASOURCE_URL, value: "jdbc:postgresql://postgres:5432/athleticmap" } + - { name: SPRING_DATASOURCE_USERNAME, value: "atm" } + - { name: SPRING_DATASOURCE_HIKARI_MAXIMUM_POOL_SIZE, value: "5" } + - name: SPRING_DATASOURCE_PASSWORD + valueFrom: + secretKeyRef: { name: db-credentials, key: password } + - { name: ATM_JWK_SET_URI, value: "http://keycloak:8080/realms/athleticmap/protocol/openid-connect/certs" } + - { name: ATM_ISSUER, value: "https://auth-escolinha.athleticmap.influxdigital.com.br/realms/athleticmap" } + - { name: ATM_TENANT, value: "escolinha" } + resources: + requests: { cpu: 100m, memory: 300Mi } + limits: { cpu: "1", memory: 640Mi } + readinessProbe: + httpGet: { path: /api/pedagogico/health, port: 8083 } + initialDelaySeconds: 25 + periodSeconds: 10 + failureThreshold: 30 + livenessProbe: + httpGet: { path: /actuator/health/liveness, port: 8083 } + initialDelaySeconds: 60 + periodSeconds: 20 + failureThreshold: 6 +--- +apiVersion: v1 +kind: Service +metadata: + name: servico-pedagogico + namespace: escolinha-prod + labels: { app: servico-pedagogico } +spec: + selector: { app: servico-pedagogico } + ports: [{ name: http, port: 80, targetPort: 8083 }] diff --git a/tenants/escolinha/85-servico-nutricao.yaml b/tenants/escolinha/85-servico-nutricao.yaml new file mode 100644 index 0000000..80542fb --- /dev/null +++ b/tenants/escolinha/85-servico-nutricao.yaml @@ -0,0 +1,54 @@ +# Bounded context "nutricao" (planos alimentares + catalogo de alimentos, ADR-0002). +# Schema proprio "nutricao"; roteado em /api/nutricao no ingress. +--- +apiVersion: apps/v1 +kind: Deployment +metadata: + name: servico-nutricao + namespace: escolinha-prod + labels: { app: servico-nutricao, athleticmap.io/contexto: nutricao } +spec: + replicas: 1 + selector: { matchLabels: { app: servico-nutricao } } + template: + metadata: + labels: { app: servico-nutricao, athleticmap.io/contexto: nutricao } + spec: + containers: + - name: servico-nutricao + image: docker.io/library/servico-nutricao:1.9 + imagePullPolicy: Never + ports: [{ containerPort: 8083 }] + env: + - { name: SPRING_DATASOURCE_URL, value: "jdbc:postgresql://postgres:5432/athleticmap" } + - { name: SPRING_DATASOURCE_USERNAME, value: "atm" } + - { name: SPRING_DATASOURCE_HIKARI_MAXIMUM_POOL_SIZE, value: "5" } + - name: SPRING_DATASOURCE_PASSWORD + valueFrom: + secretKeyRef: { name: db-credentials, key: password } + - { name: ATM_JWK_SET_URI, value: "http://keycloak:8080/realms/athleticmap/protocol/openid-connect/certs" } + - { name: ATM_ISSUER, value: "https://auth-escolinha.athleticmap.influxdigital.com.br/realms/athleticmap" } + - { name: ATM_TENANT, value: "escolinha" } + resources: + requests: { cpu: 100m, memory: 300Mi } + limits: { cpu: "1", memory: 640Mi } + readinessProbe: + httpGet: { path: /api/nutricao/health, port: 8083 } + initialDelaySeconds: 25 + periodSeconds: 10 + failureThreshold: 30 + livenessProbe: + httpGet: { path: /actuator/health/liveness, port: 8083 } + initialDelaySeconds: 60 + periodSeconds: 20 + failureThreshold: 6 +--- +apiVersion: v1 +kind: Service +metadata: + name: servico-nutricao + namespace: escolinha-prod + labels: { app: servico-nutricao } +spec: + selector: { app: servico-nutricao } + ports: [{ name: http, port: 80, targetPort: 8083 }] diff --git a/tenants/escolinha/86-servico-agenda.yaml b/tenants/escolinha/86-servico-agenda.yaml new file mode 100644 index 0000000..11accc1 --- /dev/null +++ b/tenants/escolinha/86-servico-agenda.yaml @@ -0,0 +1,54 @@ +# Bounded context "agenda" (agendamento de compromissos: consulta/avaliacao/retorno/treino/reuniao, ADR-0002). +# Schema proprio "agenda"; roteado em /api/agenda no ingress. +--- +apiVersion: apps/v1 +kind: Deployment +metadata: + name: servico-agenda + namespace: escolinha-prod + labels: { app: servico-agenda, athleticmap.io/contexto: agenda } +spec: + replicas: 1 + selector: { matchLabels: { app: servico-agenda } } + template: + metadata: + labels: { app: servico-agenda, athleticmap.io/contexto: agenda } + spec: + containers: + - name: servico-agenda + image: docker.io/library/servico-agenda:1.1 + imagePullPolicy: Never + ports: [{ containerPort: 8083 }] + env: + - { name: SPRING_DATASOURCE_URL, value: "jdbc:postgresql://postgres:5432/athleticmap" } + - { name: SPRING_DATASOURCE_USERNAME, value: "atm" } + - { name: SPRING_DATASOURCE_HIKARI_MAXIMUM_POOL_SIZE, value: "5" } + - name: SPRING_DATASOURCE_PASSWORD + valueFrom: + secretKeyRef: { name: db-credentials, key: password } + - { name: ATM_JWK_SET_URI, value: "http://keycloak:8080/realms/athleticmap/protocol/openid-connect/certs" } + - { name: ATM_ISSUER, value: "https://auth-escolinha.athleticmap.influxdigital.com.br/realms/athleticmap" } + - { name: ATM_TENANT, value: "escolinha" } + resources: + requests: { cpu: 100m, memory: 300Mi } + limits: { cpu: "1", memory: 640Mi } + readinessProbe: + httpGet: { path: /api/agenda/health, port: 8083 } + initialDelaySeconds: 25 + periodSeconds: 10 + failureThreshold: 30 + livenessProbe: + httpGet: { path: /actuator/health/liveness, port: 8083 } + initialDelaySeconds: 60 + periodSeconds: 20 + failureThreshold: 6 +--- +apiVersion: v1 +kind: Service +metadata: + name: servico-agenda + namespace: escolinha-prod + labels: { app: servico-agenda } +spec: + selector: { app: servico-agenda } + ports: [{ name: http, port: 80, targetPort: 8083 }] diff --git a/tenants/escolinha/90-servico-bff.yaml b/tenants/escolinha/90-servico-bff.yaml new file mode 100644 index 0000000..50ae183 --- /dev/null +++ b/tenants/escolinha/90-servico-bff.yaml @@ -0,0 +1,67 @@ +# BFF (Backend-for-Frontend) — ponto unico da SPA; agrega configuracao + cadastro +# com token relay. Stateless (sem banco). Roteado em /bff no ingress do tenant. +--- +apiVersion: apps/v1 +kind: Deployment +metadata: + name: servico-bff + namespace: escolinha-prod + labels: + app: servico-bff + athleticmap.io/contexto: bff +spec: + replicas: 1 + selector: { matchLabels: { app: servico-bff } } + template: + metadata: + labels: + app: servico-bff + athleticmap.io/contexto: bff + spec: + containers: + - name: servico-bff + image: docker.io/library/servico-bff:1.87-garmin + imagePullPolicy: Never + ports: [{ containerPort: 8083 }] + env: + - { name: ATM_CONFIG_URL, value: "http://servico-configuracao" } + - { name: ATM_CADASTRO_URL, value: "http://servico-cadastro" } + - { name: ATM_ORGANIZACAO_URL, value: "http://servico-organizacao" } + - { name: ATM_PLANEJAMENTO_URL, value: "http://servico-planejamento" } + - { name: ATM_CAMPEONATO_URL, value: "http://servico-campeonato" } + - { name: ATM_SAUDE_URL, value: "http://servico-saude" } + - { name: ATM_FINANCEIRO_URL, value: "http://servico-financeiro" } + - { name: ATM_DOCUMENTOS_URL, value: "http://servico-documentos" } + - { name: ATM_ADMINISTRATIVO_URL, value: "http://servico-administrativo" } + - { name: ATM_ASSISTENCIA_SOCIAL_URL, value: "http://servico-assistencia-social" } + - { name: ATM_PEDAGOGICO_URL, value: "http://servico-pedagogico" } + - { name: ATM_NUTRICAO_URL, value: "http://servico-nutricao" } + - { name: ATM_AGENDA_URL, value: "http://servico-agenda" } + - { name: ATM_JWK_SET_URI, value: "http://keycloak:8080/realms/athleticmap/protocol/openid-connect/certs" } + - { name: ATM_ISSUER, value: "https://auth-escolinha.athleticmap.influxdigital.com.br/realms/athleticmap" } + - { name: ATM_TENANT, value: "escolinha" } + - { name: GARMIN_FRONTEND_REDIRECT, value: "https://escolinha.athleticmap.influxdigital.com.br/integracoes/garmin" } + resources: + requests: { cpu: 100m, memory: 256Mi } + limits: { cpu: 500m, memory: 512Mi } + readinessProbe: + httpGet: { path: /bff/health, port: 8083 } + initialDelaySeconds: 20 + periodSeconds: 10 + failureThreshold: 30 + livenessProbe: + httpGet: { path: /actuator/health/liveness, port: 8083 } + initialDelaySeconds: 50 + periodSeconds: 20 + failureThreshold: 6 +--- +apiVersion: v1 +kind: Service +metadata: + name: servico-bff + namespace: escolinha-prod + labels: + app: servico-bff +spec: + selector: { app: servico-bff } + ports: [{ name: http, port: 80, targetPort: 8083 }] diff --git a/tenants/escolinha/95-frontend-spa.yaml b/tenants/escolinha/95-frontend-spa.yaml new file mode 100644 index 0000000..9994bc4 --- /dev/null +++ b/tenants/escolinha/95-frontend-spa.yaml @@ -0,0 +1,48 @@ +# SPA Angular (shell OIDC + admin de catalogos + cadastro de Atleta) consumindo +# o BFF. Substitui o stub estatico nginx/configMap. Roteado em / no ingress. +--- +apiVersion: apps/v1 +kind: Deployment +metadata: + name: frontend-spa + namespace: escolinha-prod + labels: + app: frontend-spa + athleticmap.io/contexto: frontend +spec: + replicas: 1 + selector: { matchLabels: { app: frontend-spa } } + template: + metadata: + labels: + app: frontend-spa + athleticmap.io/contexto: frontend + spec: + containers: + - name: frontend-spa + image: docker.io/library/frontend-spa:1.90-garmin + imagePullPolicy: Never + ports: [{ containerPort: 80 }] + env: + - { name: ATM_KC_URL, value: "https://auth-escolinha.athleticmap.influxdigital.com.br" } + - { name: ATM_REALM, value: "athleticmap" } + - { name: ATM_CLIENT, value: "spa" } + - { name: ATM_TENANT, value: "escolinha" } + resources: + requests: { cpu: 50m, memory: 64Mi } + limits: { cpu: 200m, memory: 128Mi } + readinessProbe: + httpGet: { path: /, port: 80 } + initialDelaySeconds: 5 + periodSeconds: 10 +--- +apiVersion: v1 +kind: Service +metadata: + name: frontend-spa + namespace: escolinha-prod + labels: + app: frontend-spa +spec: + selector: { app: frontend-spa } + ports: [{ name: http, port: 80, targetPort: 80 }] diff --git a/tenants/escolinha/96-ingress-garmin-patch.yaml b/tenants/escolinha/96-ingress-garmin-patch.yaml new file mode 100644 index 0000000..2e1d97d --- /dev/null +++ b/tenants/escolinha/96-ingress-garmin-patch.yaml @@ -0,0 +1 @@ +# Patch manual: /api/public roteado ao BFF (Garmin OAuth callback) diff --git a/tenants/escolinha/96-ingress-garmin-public.yaml b/tenants/escolinha/96-ingress-garmin-public.yaml new file mode 100644 index 0000000..3b28d0b --- /dev/null +++ b/tenants/escolinha/96-ingress-garmin-public.yaml @@ -0,0 +1,25 @@ +# Rota publica Garmin OAuth callback (BFF) +apiVersion: networking.k8s.io/v1 +kind: Ingress +metadata: + name: escolinha-garmin-public + namespace: escolinha-prod + annotations: + cert-manager.io/cluster-issuer: letsencrypt-prod + traefik.ingress.kubernetes.io/router.middlewares: escolinha-prod-redirect-https@kubernetescrd +spec: + ingressClassName: traefik + tls: + - hosts: [escolinha.athleticmap.influxdigital.com.br] + secretName: escolinha-tls + rules: + - host: escolinha.athleticmap.influxdigital.com.br + http: + paths: + - path: /api/public + pathType: Prefix + backend: + service: + name: servico-bff + port: + number: 80 diff --git a/tenants/escolinha/realm/athleticmap-realm.json b/tenants/escolinha/realm/athleticmap-realm.json new file mode 100644 index 0000000..066c1b6 --- /dev/null +++ b/tenants/escolinha/realm/athleticmap-realm.json @@ -0,0 +1,54 @@ +{ + "realm": "athleticmap", + "enabled": true, + "displayName": "Athletic Map", + "loginWithEmailAllowed": true, + "roles": { + "realm": [ + { + "name": "admin" + }, + { + "name": "atm_athlete" + }, + { + "name": "atm_trainer" + }, + { + "name": "atm_team_admin" + }, + { + "name": "atm_fed_admin" + }, + { + "name": "atm_admin", + "description": "Administrador do tenant (gestao geral)" + }, + { + "name": "atm_cadastro", + "description": "Gestao de cadastros e catalogos" + } + ] + }, + "clients": [ + { + "clientId": "spa", + "name": "Athletic Map SPA", + "publicClient": true, + "protocol": "openid-connect", + "standardFlowEnabled": true, + "implicitFlowEnabled": false, + "directAccessGrantsEnabled": false, + "redirectUris": [ + "https://escolinha.athleticmap.influxdigital.com.br/*" + ], + "webOrigins": [ + "https://escolinha.athleticmap.influxdigital.com.br" + ], + "attributes": { + "pkce.code.challenge.method": "S256", + "post.logout.redirect.uris": "https://escolinha.athleticmap.influxdigital.com.br/*" + } + } + ] +}