diff --git a/tenants/acme/56-cronjob-resumo.yaml b/tenants/acme/56-cronjob-resumo.yaml new file mode 100644 index 0000000..b8c10e2 --- /dev/null +++ b/tenants/acme/56-cronjob-resumo.yaml @@ -0,0 +1,141 @@ +# ============================================================================= +# N16 — o resumo AGREGADO do tenant, entregue a plataforma uma vez por dia, para o +# comprador ver todas as escolinhas dele lado a lado no /minha-conta. +# +# AGUARDANDO GRAVACAO no molde (`moldes/consolidado/` do GitOps) e nos tenants que +# ja existem. Nada aqui foi aplicado. +# +# O desenho: o TENANT calcula (runtime-operacao, GET /api/resumo-do-tenant, so +# contadores e somas) e o TENANT envia (este CronJob). A plataforma nao entra no +# namespace nem no banco do tenant para isto. +# +# 1. token do client `operacao` no realm do tenant (o mesmo das tarefas do +# financeiro: Secret `operacao-credentials`); +# 2. GET http://runtime-operacao/api/resumo-do-tenant (lista de 1 ou 2 meses); +# 3. POST http://servico-captacao.plataforma-prod.svc/api/public/resumo-do-tenant/acme +# com o cabecalho `x-atm-resumo-token` — do Secret `resumo-credentials`, que +# so vale para ESTE tenant (HMAC do nome dele com a chave da plataforma). +# +# A saida para a plataforma e pela internet (443 publico), que a deny-cross-tenant +# ja libera: nao precisa de NetworkPolicy nova. +# +# `optional: true` no resumo-credentials: tenant sem o Secret (chave da plataforma +# ainda nao configurada) roda o job, que termina OK dizendo "sem token" — nao gera +# KubeJobFailed nem gasta a cota de e-mail dos alertas. +# ============================================================================= +apiVersion: v1 +kind: ConfigMap +metadata: + name: resumo-cron + namespace: acme-prod +data: + enviar.sh: | + #!/bin/sh + set -eu + + : "${KEYCLOAK_URL:?falta KEYCLOAK_URL}" + : "${CLIENT_SECRET:?falta CLIENT_SECRET}" + : "${ISSUER_HOST:?falta ISSUER_HOST}" + : "${DESTINO:?falta DESTINO}" + + if [ -z "${RESUMO_TOKEN:-}" ]; then + echo "sem o Secret resumo-credentials neste tenant: nada a enviar." + exit 0 + fi + + i=0 + until curl -sS -o /dev/null -m 5 "${KEYCLOAK_URL}" 2>/dev/null; do + i=$((i+1)) + if [ "$i" -ge 24 ]; then + echo "ERRO: ${KEYCLOAK_URL} inalcancavel depois de 2 min." >&2 + exit 1 + fi + sleep 5 + done + + RESP="$(printf 'grant_type=client_credentials&client_id=operacao&client_secret=%s' \ + "${CLIENT_SECRET}" \ + | curl -sS --fail-with-body -X POST "${KEYCLOAK_URL}" \ + -H 'Content-Type: application/x-www-form-urlencoded' \ + -H 'X-Forwarded-Proto: https' \ + -H 'X-Forwarded-Port: 443' \ + -H "X-Forwarded-Host: ${ISSUER_HOST}" -d @-)" + TOKEN="$(printf '%s' "${RESP}" | sed -n 's/.*"access_token":"\([^"]*\)".*/\1/p')" + if [ -z "${TOKEN}" ]; then + echo "ERRO: nao consegui token pelo client 'operacao'." >&2 + exit 1 + fi + + # Os tokens vao por `-K -` (stdin), e nao na linha de comando: argumento de + # processo aparece em `ps` para quem estiver no no. + CODIGO="$(printf 'header = "Authorization: Bearer %s"\n' "${TOKEN}" \ + | curl -sS -K - -o /tmp/resumo.json -w '%{http_code}' http://runtime-operacao/api/resumo-do-tenant)" + if [ "${CODIGO}" != "200" ]; then + echo "ERRO: o runtime-operacao respondeu ${CODIGO} ao resumo." >&2 + exit 1 + fi + echo "resumo calculado: $(wc -c < /tmp/resumo.json) bytes (so numeros)" + + CODIGO="$(printf 'header = "x-atm-resumo-token: %s"\n' "${RESUMO_TOKEN}" \ + | curl -sS -K - -X POST "${DESTINO}" -H 'Content-Type: application/json' \ + --data-binary @/tmp/resumo.json -o /tmp/saida.json -w '%{http_code}')" + echo "plataforma: ${CODIGO}" + cat /tmp/saida.json; echo + case "${CODIGO}" in + 2*) ;; + *) echo "ERRO: a plataforma respondeu ${CODIGO}." >&2; exit 1 ;; + esac +--- +apiVersion: batch/v1 +kind: CronJob +metadata: + name: resumo-para-a-plataforma + namespace: acme-prod +spec: + # Madrugada, depois da virada do dia em Sao Paulo e fora do horario das + # mensalidades (dia 1, 06h). + schedule: "30 4 * * *" + timeZone: America/Sao_Paulo + concurrencyPolicy: Forbid + successfulJobsHistoryLimit: 3 + failedJobsHistoryLimit: 5 + jobTemplate: + spec: + activeDeadlineSeconds: 600 + backoffLimit: 2 + template: + spec: + restartPolicy: OnFailure + containers: + - name: enviar + image: docker.io/curlimages/curl:8.9.1 + imagePullPolicy: Never + command: ["/bin/sh", "/script/enviar.sh"] + env: + - name: KEYCLOAK_URL + value: http://keycloak.plataforma-prod.svc:8080/realms/athleticmap-3/protocol/openid-connect/token + - name: ISSUER_HOST + value: auth.athleticmap.com + - name: DESTINO + value: http://servico-captacao.plataforma-prod.svc/api/public/resumo-do-tenant/acme + - name: CLIENT_SECRET + valueFrom: + secretKeyRef: + name: operacao-credentials + key: client-secret + - name: RESUMO_TOKEN + valueFrom: + secretKeyRef: + name: resumo-credentials + key: token + optional: true + resources: + requests: { cpu: 10m, memory: 32Mi } + limits: { cpu: 200m, memory: 64Mi } + volumeMounts: + - name: script + mountPath: /script + volumes: + - name: script + configMap: + name: resumo-cron diff --git a/tenants/demo/56-cronjob-resumo.yaml b/tenants/demo/56-cronjob-resumo.yaml new file mode 100644 index 0000000..9d3134d --- /dev/null +++ b/tenants/demo/56-cronjob-resumo.yaml @@ -0,0 +1,141 @@ +# ============================================================================= +# N16 — o resumo AGREGADO do tenant, entregue a plataforma uma vez por dia, para o +# comprador ver todas as escolinhas dele lado a lado no /minha-conta. +# +# AGUARDANDO GRAVACAO no molde (`moldes/consolidado/` do GitOps) e nos tenants que +# ja existem. Nada aqui foi aplicado. +# +# O desenho: o TENANT calcula (runtime-operacao, GET /api/resumo-do-tenant, so +# contadores e somas) e o TENANT envia (este CronJob). A plataforma nao entra no +# namespace nem no banco do tenant para isto. +# +# 1. token do client `operacao` no realm do tenant (o mesmo das tarefas do +# financeiro: Secret `operacao-credentials`); +# 2. GET http://runtime-operacao/api/resumo-do-tenant (lista de 1 ou 2 meses); +# 3. POST http://servico-captacao.plataforma-prod.svc/api/public/resumo-do-tenant/demo +# com o cabecalho `x-atm-resumo-token` — do Secret `resumo-credentials`, que +# so vale para ESTE tenant (HMAC do nome dele com a chave da plataforma). +# +# A saida para a plataforma e pela internet (443 publico), que a deny-cross-tenant +# ja libera: nao precisa de NetworkPolicy nova. +# +# `optional: true` no resumo-credentials: tenant sem o Secret (chave da plataforma +# ainda nao configurada) roda o job, que termina OK dizendo "sem token" — nao gera +# KubeJobFailed nem gasta a cota de e-mail dos alertas. +# ============================================================================= +apiVersion: v1 +kind: ConfigMap +metadata: + name: resumo-cron + namespace: demo-prod +data: + enviar.sh: | + #!/bin/sh + set -eu + + : "${KEYCLOAK_URL:?falta KEYCLOAK_URL}" + : "${CLIENT_SECRET:?falta CLIENT_SECRET}" + : "${ISSUER_HOST:?falta ISSUER_HOST}" + : "${DESTINO:?falta DESTINO}" + + if [ -z "${RESUMO_TOKEN:-}" ]; then + echo "sem o Secret resumo-credentials neste tenant: nada a enviar." + exit 0 + fi + + i=0 + until curl -sS -o /dev/null -m 5 "${KEYCLOAK_URL}" 2>/dev/null; do + i=$((i+1)) + if [ "$i" -ge 24 ]; then + echo "ERRO: ${KEYCLOAK_URL} inalcancavel depois de 2 min." >&2 + exit 1 + fi + sleep 5 + done + + RESP="$(printf 'grant_type=client_credentials&client_id=operacao&client_secret=%s' \ + "${CLIENT_SECRET}" \ + | curl -sS --fail-with-body -X POST "${KEYCLOAK_URL}" \ + -H 'Content-Type: application/x-www-form-urlencoded' \ + -H 'X-Forwarded-Proto: https' \ + -H 'X-Forwarded-Port: 443' \ + -H "X-Forwarded-Host: ${ISSUER_HOST}" -d @-)" + TOKEN="$(printf '%s' "${RESP}" | sed -n 's/.*"access_token":"\([^"]*\)".*/\1/p')" + if [ -z "${TOKEN}" ]; then + echo "ERRO: nao consegui token pelo client 'operacao'." >&2 + exit 1 + fi + + # Os tokens vao por `-K -` (stdin), e nao na linha de comando: argumento de + # processo aparece em `ps` para quem estiver no no. + CODIGO="$(printf 'header = "Authorization: Bearer %s"\n' "${TOKEN}" \ + | curl -sS -K - -o /tmp/resumo.json -w '%{http_code}' http://runtime-operacao/api/resumo-do-tenant)" + if [ "${CODIGO}" != "200" ]; then + echo "ERRO: o runtime-operacao respondeu ${CODIGO} ao resumo." >&2 + exit 1 + fi + echo "resumo calculado: $(wc -c < /tmp/resumo.json) bytes (so numeros)" + + CODIGO="$(printf 'header = "x-atm-resumo-token: %s"\n' "${RESUMO_TOKEN}" \ + | curl -sS -K - -X POST "${DESTINO}" -H 'Content-Type: application/json' \ + --data-binary @/tmp/resumo.json -o /tmp/saida.json -w '%{http_code}')" + echo "plataforma: ${CODIGO}" + cat /tmp/saida.json; echo + case "${CODIGO}" in + 2*) ;; + *) echo "ERRO: a plataforma respondeu ${CODIGO}." >&2; exit 1 ;; + esac +--- +apiVersion: batch/v1 +kind: CronJob +metadata: + name: resumo-para-a-plataforma + namespace: demo-prod +spec: + # Madrugada, depois da virada do dia em Sao Paulo e fora do horario das + # mensalidades (dia 1, 06h). + schedule: "30 4 * * *" + timeZone: America/Sao_Paulo + concurrencyPolicy: Forbid + successfulJobsHistoryLimit: 3 + failedJobsHistoryLimit: 5 + jobTemplate: + spec: + activeDeadlineSeconds: 600 + backoffLimit: 2 + template: + spec: + restartPolicy: OnFailure + containers: + - name: enviar + image: docker.io/curlimages/curl:8.9.1 + imagePullPolicy: Never + command: ["/bin/sh", "/script/enviar.sh"] + env: + - name: KEYCLOAK_URL + value: http://keycloak.plataforma-prod.svc:8080/realms/athleticmap-2/protocol/openid-connect/token + - name: ISSUER_HOST + value: auth.athleticmap.com + - name: DESTINO + value: http://servico-captacao.plataforma-prod.svc/api/public/resumo-do-tenant/demo + - name: CLIENT_SECRET + valueFrom: + secretKeyRef: + name: operacao-credentials + key: client-secret + - name: RESUMO_TOKEN + valueFrom: + secretKeyRef: + name: resumo-credentials + key: token + optional: true + resources: + requests: { cpu: 10m, memory: 32Mi } + limits: { cpu: 200m, memory: 64Mi } + volumeMounts: + - name: script + mountPath: /script + volumes: + - name: script + configMap: + name: resumo-cron diff --git a/tenants/escolinha/56-cronjob-resumo.yaml b/tenants/escolinha/56-cronjob-resumo.yaml new file mode 100644 index 0000000..02ec6fc --- /dev/null +++ b/tenants/escolinha/56-cronjob-resumo.yaml @@ -0,0 +1,141 @@ +# ============================================================================= +# N16 — o resumo AGREGADO do tenant, entregue a plataforma uma vez por dia, para o +# comprador ver todas as escolinhas dele lado a lado no /minha-conta. +# +# AGUARDANDO GRAVACAO no molde (`moldes/consolidado/` do GitOps) e nos tenants que +# ja existem. Nada aqui foi aplicado. +# +# O desenho: o TENANT calcula (runtime-operacao, GET /api/resumo-do-tenant, so +# contadores e somas) e o TENANT envia (este CronJob). A plataforma nao entra no +# namespace nem no banco do tenant para isto. +# +# 1. token do client `operacao` no realm do tenant (o mesmo das tarefas do +# financeiro: Secret `operacao-credentials`); +# 2. GET http://runtime-operacao/api/resumo-do-tenant (lista de 1 ou 2 meses); +# 3. POST http://servico-captacao.plataforma-prod.svc/api/public/resumo-do-tenant/escolinha +# com o cabecalho `x-atm-resumo-token` — do Secret `resumo-credentials`, que +# so vale para ESTE tenant (HMAC do nome dele com a chave da plataforma). +# +# A saida para a plataforma e pela internet (443 publico), que a deny-cross-tenant +# ja libera: nao precisa de NetworkPolicy nova. +# +# `optional: true` no resumo-credentials: tenant sem o Secret (chave da plataforma +# ainda nao configurada) roda o job, que termina OK dizendo "sem token" — nao gera +# KubeJobFailed nem gasta a cota de e-mail dos alertas. +# ============================================================================= +apiVersion: v1 +kind: ConfigMap +metadata: + name: resumo-cron + namespace: escolinha-prod +data: + enviar.sh: | + #!/bin/sh + set -eu + + : "${KEYCLOAK_URL:?falta KEYCLOAK_URL}" + : "${CLIENT_SECRET:?falta CLIENT_SECRET}" + : "${ISSUER_HOST:?falta ISSUER_HOST}" + : "${DESTINO:?falta DESTINO}" + + if [ -z "${RESUMO_TOKEN:-}" ]; then + echo "sem o Secret resumo-credentials neste tenant: nada a enviar." + exit 0 + fi + + i=0 + until curl -sS -o /dev/null -m 5 "${KEYCLOAK_URL}" 2>/dev/null; do + i=$((i+1)) + if [ "$i" -ge 24 ]; then + echo "ERRO: ${KEYCLOAK_URL} inalcancavel depois de 2 min." >&2 + exit 1 + fi + sleep 5 + done + + RESP="$(printf 'grant_type=client_credentials&client_id=operacao&client_secret=%s' \ + "${CLIENT_SECRET}" \ + | curl -sS --fail-with-body -X POST "${KEYCLOAK_URL}" \ + -H 'Content-Type: application/x-www-form-urlencoded' \ + -H 'X-Forwarded-Proto: https' \ + -H 'X-Forwarded-Port: 443' \ + -H "X-Forwarded-Host: ${ISSUER_HOST}" -d @-)" + TOKEN="$(printf '%s' "${RESP}" | sed -n 's/.*"access_token":"\([^"]*\)".*/\1/p')" + if [ -z "${TOKEN}" ]; then + echo "ERRO: nao consegui token pelo client 'operacao'." >&2 + exit 1 + fi + + # Os tokens vao por `-K -` (stdin), e nao na linha de comando: argumento de + # processo aparece em `ps` para quem estiver no no. + CODIGO="$(printf 'header = "Authorization: Bearer %s"\n' "${TOKEN}" \ + | curl -sS -K - -o /tmp/resumo.json -w '%{http_code}' http://runtime-operacao/api/resumo-do-tenant)" + if [ "${CODIGO}" != "200" ]; then + echo "ERRO: o runtime-operacao respondeu ${CODIGO} ao resumo." >&2 + exit 1 + fi + echo "resumo calculado: $(wc -c < /tmp/resumo.json) bytes (so numeros)" + + CODIGO="$(printf 'header = "x-atm-resumo-token: %s"\n' "${RESUMO_TOKEN}" \ + | curl -sS -K - -X POST "${DESTINO}" -H 'Content-Type: application/json' \ + --data-binary @/tmp/resumo.json -o /tmp/saida.json -w '%{http_code}')" + echo "plataforma: ${CODIGO}" + cat /tmp/saida.json; echo + case "${CODIGO}" in + 2*) ;; + *) echo "ERRO: a plataforma respondeu ${CODIGO}." >&2; exit 1 ;; + esac +--- +apiVersion: batch/v1 +kind: CronJob +metadata: + name: resumo-para-a-plataforma + namespace: escolinha-prod +spec: + # Madrugada, depois da virada do dia em Sao Paulo e fora do horario das + # mensalidades (dia 1, 06h). + schedule: "30 4 * * *" + timeZone: America/Sao_Paulo + concurrencyPolicy: Forbid + successfulJobsHistoryLimit: 3 + failedJobsHistoryLimit: 5 + jobTemplate: + spec: + activeDeadlineSeconds: 600 + backoffLimit: 2 + template: + spec: + restartPolicy: OnFailure + containers: + - name: enviar + image: docker.io/curlimages/curl:8.9.1 + imagePullPolicy: Never + command: ["/bin/sh", "/script/enviar.sh"] + env: + - name: KEYCLOAK_URL + value: http://keycloak.plataforma-prod.svc:8080/realms/athleticmap-4/protocol/openid-connect/token + - name: ISSUER_HOST + value: auth.athleticmap.com + - name: DESTINO + value: http://servico-captacao.plataforma-prod.svc/api/public/resumo-do-tenant/escolinha + - name: CLIENT_SECRET + valueFrom: + secretKeyRef: + name: operacao-credentials + key: client-secret + - name: RESUMO_TOKEN + valueFrom: + secretKeyRef: + name: resumo-credentials + key: token + optional: true + resources: + requests: { cpu: 10m, memory: 32Mi } + limits: { cpu: 200m, memory: 64Mi } + volumeMounts: + - name: script + mountPath: /script + volumes: + - name: script + configMap: + name: resumo-cron diff --git a/tenants/piloto/56-cronjob-resumo.yaml b/tenants/piloto/56-cronjob-resumo.yaml new file mode 100644 index 0000000..41ed9af --- /dev/null +++ b/tenants/piloto/56-cronjob-resumo.yaml @@ -0,0 +1,141 @@ +# ============================================================================= +# N16 — o resumo AGREGADO do tenant, entregue a plataforma uma vez por dia, para o +# comprador ver todas as escolinhas dele lado a lado no /minha-conta. +# +# AGUARDANDO GRAVACAO no molde (`moldes/consolidado/` do GitOps) e nos tenants que +# ja existem. Nada aqui foi aplicado. +# +# O desenho: o TENANT calcula (runtime-operacao, GET /api/resumo-do-tenant, so +# contadores e somas) e o TENANT envia (este CronJob). A plataforma nao entra no +# namespace nem no banco do tenant para isto. +# +# 1. token do client `operacao` no realm do tenant (o mesmo das tarefas do +# financeiro: Secret `operacao-credentials`); +# 2. GET http://runtime-operacao/api/resumo-do-tenant (lista de 1 ou 2 meses); +# 3. POST http://servico-captacao.plataforma-prod.svc/api/public/resumo-do-tenant/piloto +# com o cabecalho `x-atm-resumo-token` — do Secret `resumo-credentials`, que +# so vale para ESTE tenant (HMAC do nome dele com a chave da plataforma). +# +# A saida para a plataforma e pela internet (443 publico), que a deny-cross-tenant +# ja libera: nao precisa de NetworkPolicy nova. +# +# `optional: true` no resumo-credentials: tenant sem o Secret (chave da plataforma +# ainda nao configurada) roda o job, que termina OK dizendo "sem token" — nao gera +# KubeJobFailed nem gasta a cota de e-mail dos alertas. +# ============================================================================= +apiVersion: v1 +kind: ConfigMap +metadata: + name: resumo-cron + namespace: piloto-prod +data: + enviar.sh: | + #!/bin/sh + set -eu + + : "${KEYCLOAK_URL:?falta KEYCLOAK_URL}" + : "${CLIENT_SECRET:?falta CLIENT_SECRET}" + : "${ISSUER_HOST:?falta ISSUER_HOST}" + : "${DESTINO:?falta DESTINO}" + + if [ -z "${RESUMO_TOKEN:-}" ]; then + echo "sem o Secret resumo-credentials neste tenant: nada a enviar." + exit 0 + fi + + i=0 + until curl -sS -o /dev/null -m 5 "${KEYCLOAK_URL}" 2>/dev/null; do + i=$((i+1)) + if [ "$i" -ge 24 ]; then + echo "ERRO: ${KEYCLOAK_URL} inalcancavel depois de 2 min." >&2 + exit 1 + fi + sleep 5 + done + + RESP="$(printf 'grant_type=client_credentials&client_id=operacao&client_secret=%s' \ + "${CLIENT_SECRET}" \ + | curl -sS --fail-with-body -X POST "${KEYCLOAK_URL}" \ + -H 'Content-Type: application/x-www-form-urlencoded' \ + -H 'X-Forwarded-Proto: https' \ + -H 'X-Forwarded-Port: 443' \ + -H "X-Forwarded-Host: ${ISSUER_HOST}" -d @-)" + TOKEN="$(printf '%s' "${RESP}" | sed -n 's/.*"access_token":"\([^"]*\)".*/\1/p')" + if [ -z "${TOKEN}" ]; then + echo "ERRO: nao consegui token pelo client 'operacao'." >&2 + exit 1 + fi + + # Os tokens vao por `-K -` (stdin), e nao na linha de comando: argumento de + # processo aparece em `ps` para quem estiver no no. + CODIGO="$(printf 'header = "Authorization: Bearer %s"\n' "${TOKEN}" \ + | curl -sS -K - -o /tmp/resumo.json -w '%{http_code}' http://runtime-operacao/api/resumo-do-tenant)" + if [ "${CODIGO}" != "200" ]; then + echo "ERRO: o runtime-operacao respondeu ${CODIGO} ao resumo." >&2 + exit 1 + fi + echo "resumo calculado: $(wc -c < /tmp/resumo.json) bytes (so numeros)" + + CODIGO="$(printf 'header = "x-atm-resumo-token: %s"\n' "${RESUMO_TOKEN}" \ + | curl -sS -K - -X POST "${DESTINO}" -H 'Content-Type: application/json' \ + --data-binary @/tmp/resumo.json -o /tmp/saida.json -w '%{http_code}')" + echo "plataforma: ${CODIGO}" + cat /tmp/saida.json; echo + case "${CODIGO}" in + 2*) ;; + *) echo "ERRO: a plataforma respondeu ${CODIGO}." >&2; exit 1 ;; + esac +--- +apiVersion: batch/v1 +kind: CronJob +metadata: + name: resumo-para-a-plataforma + namespace: piloto-prod +spec: + # Madrugada, depois da virada do dia em Sao Paulo e fora do horario das + # mensalidades (dia 1, 06h). + schedule: "30 4 * * *" + timeZone: America/Sao_Paulo + concurrencyPolicy: Forbid + successfulJobsHistoryLimit: 3 + failedJobsHistoryLimit: 5 + jobTemplate: + spec: + activeDeadlineSeconds: 600 + backoffLimit: 2 + template: + spec: + restartPolicy: OnFailure + containers: + - name: enviar + image: docker.io/curlimages/curl:8.9.1 + imagePullPolicy: Never + command: ["/bin/sh", "/script/enviar.sh"] + env: + - name: KEYCLOAK_URL + value: http://keycloak.plataforma-prod.svc:8080/realms/athleticmap-1/protocol/openid-connect/token + - name: ISSUER_HOST + value: auth.athleticmap.com + - name: DESTINO + value: http://servico-captacao.plataforma-prod.svc/api/public/resumo-do-tenant/piloto + - name: CLIENT_SECRET + valueFrom: + secretKeyRef: + name: operacao-credentials + key: client-secret + - name: RESUMO_TOKEN + valueFrom: + secretKeyRef: + name: resumo-credentials + key: token + optional: true + resources: + requests: { cpu: 10m, memory: 32Mi } + limits: { cpu: 200m, memory: 64Mi } + volumeMounts: + - name: script + mountPath: /script + volumes: + - name: script + configMap: + name: resumo-cron