Idioma: Português (BR)
IR-11 · observability
Pushkar Kumar 11 min de leitura
Datasource ${DS_PROMETHEUS} was not found: como corrigir
Por que dashboards provisionados no Grafana dizem Datasource ${DS_PROMETHEUS} was not found, como achar cada placeholder com jq e quatro correções para o CI.
- observability
- prometheus
Nesta página
- Por que o Grafana diz “Datasource $${DS_PROMETHEUS} was not found”?
- De onde vem o nome DS_PROMETHEUS?
- Como confirmar quais placeholders estão no arquivo?
- Qual é a sua causa?
- Como corrigir no provisioning e na API?
- Apagar o __inputs é a correção que não corrige?
- Como impedir que ele volte no CI?
- O que conferir antes de publicar um arquivo de dashboard?
Você exportou um dashboard do staging, fez commit do JSON ao lado da sua configuração de provisioning e subiu o Grafana. O dashboard aparece na pasta certa. Todos os painéis estão vazios, e cada um exibe o mesmo canto vermelho:
Datasource ${DS_PROMETHEUS} was not foundO seu datasource Prometheus existe e funciona no Explore. Esse erro de datasource não encontrado no Grafana, muitas vezes pesquisado como “DS_PROMETHEUS not found”, não quer dizer que o datasource sumiu. O Grafana está imprimindo, ao pé da letra, a chave de busca que recebeu, e essa chave é um placeholder que ninguém preencheu. Depois que você enxerga o problema assim, a correção é mecânica.
TL;DR
- A opção de exportar para compartilhamento (“Export for sharing externally” no Grafana 10 a 11) troca cada datasource por
${DS_<NAME>}e os lista em__inputs. Só o diálogo de Import e a API de import preenchem esses valores.- O provisioning por arquivo, o
POST /api/dashboards/dbe o Terraform guardam o arquivo tal como está, então o placeholder chega ao painel.- Para encontrá-los:
jq -r 'paths(type == "string" and startswith("${DS_")) | map(tostring) | join(".")' dashboard.json.- Corrija substituindo por um
{ type, uid }real, fixando esse uid no provisioning do datasource, adicionando uma variável de datasource chamadaDS_PROMETHEUSou enviando para/api/dashboards/importcominputs.
Por que o Grafana diz “Datasource ${DS_PROMETHEUS} was not found”?
A referência de datasource de um painel passa por DatasourceSrv.get(), que roda o uid pelo serviço de templates antes de procurá-lo. O serviço de templates devolve sem alteração qualquer ${...} para o qual não tem variável. Assim, a chave continua sendo a string literal ${DS_PROMETHEUS}, nenhum datasource tem esse uid ou nome, e o loadDatasource() rejeita com a mensagem que você está vendo.
O placeholder foi parar ali durante a exportação. Com a opção de exportação ligada, o Grafana escreve isto:
{
"__inputs": [
{
"name": "DS_PROMETHEUS",
"label": "Prometheus",
"description": "",
"type": "datasource",
"pluginId": "prometheus",
"pluginName": "Prometheus"
}
],
"panels": [
{
"type": "timeseries",
"title": "Request rate",
"datasource": { "type": "prometheus", "uid": "${DS_PROMETHEUS}" },
"targets": [
{
"datasource": { "type": "prometheus", "uid": "${DS_PROMETHEUS}" },
"expr": "sum(rate(http_requests_total{job=\"$job\"}[5m]))"
}
]
}
],
"templating": {
"list": [
{
"type": "query",
"name": "job",
"datasource": { "type": "prometheus", "uid": "${DS_PROMETHEUS}" },
"query": "label_values(up, job)"
}
]
}
}O __inputs é uma pergunta para quem importa: qual dos seus datasources deve substituir DS_PROMETHEUS? O diálogo de Import faz essa pergunta. Em seguida, o avaliador de templates do lado do servidor substitui cada ${name} declarado pelo valor que você escolheu e descarta o __inputs.
Nada mais faz essa pergunta. Um comentário no próprio código do Grafana, em export_inputs.go, diz que um template gravado via Terraform, provisioning ou POST direto “is stored verbatim” e que “DS_* inputs carry no value to substitute”. O pedido para suportar isso no provisioning, grafana/grafana#10786, está aberto desde 2018.
Importante: o Grafana 13.2.0 (#129213) passou a resolver inputs constantes
${VAR_*}em dashboards gravados via provisioning ou POST direto. Ele explicitamente não resolve inputsDS_*, então no 13.2.2 este erro se comporta exatamente como antes.
No Grafana 7.x e no início do 8.x, a mesma falha aparecia como Datasource named ${DS_PROMETHEUS} was not found, a redação que a maioria dos tópicos de fórum cita. Uma variável de query apontada para o placeholder aparece, em vez disso, como um aviso de templating, relatado nos tópicos como Error updating options: Datasource named ${DS_PROMETHEUS} was not found ou Failed to upgrade legacy queries Datasource ${DS_PROMETHEUS} was not found.
De onde vem o nome DS_PROMETHEUS?
Do nome de exibição do datasource, não do seu tipo. O exportador o monta como 'DS_' + ds.name.replace(' ', '_').toUpperCase(). Você só recebe DS_PROMETHEUS porque o nome padrão é “Prometheus”. Um datasource chamado “Thanos” é exportado como ${DS_THANOS}.
Esse replace do JavaScript com um padrão em string troca apenas o primeiro espaço. Um datasource chamado “Prometheus Prod EU” é exportado como ${DS_PROMETHEUS_PROD EU}, com espaço e tudo. Referências de library panels ganham o sufixo -FOR-LIBRARY-PANEL, e variáveis constantes viram inputs VAR_<NAME>.
A opção que produz tudo isso mudou de lugar entre versões:
- Grafana 10 a 11: Export for sharing externally.
- Do Grafana 11.2 ao 12.3, ela também aparece como Export the dashboard to use in another instance (do 12.1 ao 12.3, o caminho é Export → Export as code).
- Grafana 12.4 e 13: Export → Export as code, opção Share dashboard with another instance, com um modelo de recurso Classic ou V2 em Advanced options.
Se você exporta para o seu próprio repositório de provisioning, deixe a opção desligada. A documentação de compartilhamento cobre as opções de cada versão.
Como confirmar quais placeholders estão no arquivo?
Três verificações, nesta ordem. Primeiro, liste o que o arquivo espera que seja preenchido:
jq -c '.__inputs[] | {name, pluginId}' dashboard.json{"name":"DS_PROMETHEUS","pluginId":"prometheus"}Segundo, encontre cada string que ainda contém um placeholder, incluindo as das variáveis de query. Mantenha o filtro entre aspas simples para que o shell não expanda ${DS_...}:
jq -r 'paths(type == "string" and startswith("${DS_")) | map(tostring) | join(".")' dashboard.jsonpanels.0.datasource.uid
panels.0.targets.0.datasource.uid
templating.list.0.datasource.uidgrep -nF '${DS_' dashboard.json dá a mesma resposta, com números de linha. Terceiro, liste os uids que realmente existem na instância de destino. GET /api/datasources retorna id, uid, orgId, name e type, segundo a documentação da API de data sources:
curl -sS -H "Authorization: Bearer $GRAFANA_TOKEN" "$GRAFANA_URL/api/datasources" \
| jq -r '.[] | [.uid, .name, .type] | @tsv'Para pular o jq, cole o JSON do dashboard no Grafana Dashboard Validator. No arquivo exportado acima, ele reporta um erro, na regra unresolved-ds-input. Os avisos ao lado dizem respeito ao trecho recortado, que não tem uid, title, gridPos nem schemaVersion:
[error] unresolved-ds-input @ __inputs[0]
"__inputs" declares "DS_PROMETHEUS", an import placeholder that only the Grafana import dialog fills in.O limite dele: ele só lê o arquivo. Um dashboard com um uid de aparência real, como P1809F7CD0C75ACF3, passa quer esse uid exista na sua instância, quer não, então confirme-o em /api/datasources.
Qual é a sua causa?
| O que você vê | Causa mais provável |
|---|---|
__inputs presente, arquivo carregado por provisioning ou Terraform | Arquivo feito só para import usado como arquivo provisionado |
Sem __inputs, ${DS_...} ainda nos painéis | Placeholders que ficaram depois que o __inputs foi removido |
| O erro cita um uid de aparência real, não um placeholder | O uid difere entre instâncias |
A variável DS_PROMETHEUS existe, mas não é type: datasource | Variável com o tipo errado |
1. Um arquivo exportado foi provisionado. Sinal: o __inputs está presente e o dashboard entrou pelo file provider, pelo /api/dashboards/db ou pelo Terraform. Correção: substitua por um uid real ou carregue o arquivo pela API de import. Verificação: a contagem de placeholders abaixo dá 0.
2. Placeholders sem __inputs. Sinal: alguém apagou o __inputs, ou um chart empacotou o dashboard sem ele. Aí o diálogo de Import não tem nada a perguntar, e o erro sobrevive a um import pela UI. Correção: substitua, ou adicione a variável de datasource. Verificação: os painéis renderizam depois de recarregar.
3. O uid não existe aqui. Sinal: depois da substituição, o erro imprime um uid. O Grafana gera um uid automaticamente quando o provisioning não define um, então staging e produção não batem. Correção: fixe o uid no provisioning do datasource. Verificação: o /api/datasources o lista.
4. Uma variável com o nome certo, mas o tipo errado. Sinal: o templating.list tem DS_PROMETHEUS, mas como variável custom. Ela resolve para o próprio valor, não para o uid de um datasource. Correção: transforme-a em type: datasource. Verificação: a guarda de CI abaixo imprime 0 para o arquivo.
Se a mensagem tiver dois-pontos, Datasource: ${DS_PROMETHEUS} was not found, o que falhou foi o carregamento do plugin. Esse é outro problema.
Como corrigir no provisioning e na API?
Substitua por um uid real. Este walk troca cada objeto com placeholder, incluindo o da variável de query, e remove as chaves que só servem para import:
jq --arg uid P1809F7CD0C75ACF3 \
'walk(if type == "object" and .uid == "${DS_PROMETHEUS}"
then .uid = $uid else . end)
| del(.__inputs, .__requires)' \
dashboard.json > dashboard.provisioned.jsonTeste o filtro no seu próprio arquivo no jq playground, que roda o jq de verdade.
Fixe o uid para que ele bata em todo lugar. Defina uid no provisioning do datasource, e o valor substituído passa a ser verdadeiro em todas as instâncias:
apiVersion: 1
datasources:
- name: Prometheus
type: prometheus
uid: prometheus-main
access: proxy
url: http://prometheus:9090O provider de dashboards não precisa de nada especial, mas saiba que ele nunca lê o __inputs:
apiVersion: 1
providers:
- name: platform
orgId: 1
folder: Platform
type: file
disableDeletion: true
updateIntervalSeconds: 30
allowUiUpdates: false
options:
path: /var/lib/grafana/dashboardsOs dois formatos estão na documentação de provisioning.
Mantenha o placeholder e defina-o. Uma variável do tipo datasource com exatamente o nome do placeholder resolve ${DS_PROMETHEUS} para um uid real. A query dela é o id do plugin:
{
"templating": {
"list": [
{ "type": "datasource", "name": "DS_PROMETHEUS", "query": "prometheus" }
]
}
}Use a API de import. POST /api/dashboards/import é o endpoint para o qual o diálogo de Import envia. Ele exige dashboards:create e não consta da documentação atual da HTTP API, então o formato vem do código-fonte. Troque <folder-uid> pelo uid real da pasta de destino: o provider acima cria a pasta pelo título, então o uid dela não é necessariamente platform.
jq -n --slurpfile dash dashboard.json \
--arg uid prometheus-main --arg folder '<folder-uid>' \
'{dashboard: $dash[0], overwrite: true, folderUid: $folder,
inputs: [{name: "DS_PROMETHEUS", type: "datasource",
pluginId: "prometheus", value: $uid}]}' \
> payload.json
curl -sS -X POST \
-H "Authorization: Bearer $GRAFANA_TOKEN" \
-H "Content-Type: application/json" \
--data @payload.json "$GRAFANA_URL/api/dashboards/import"Deixe um input de fora e a chamada falha com HTTP 400, com a mensagem dentro de um corpo JSON:
{"message":"dashboard import failed: missing dashboard input variable DS_PROMETHEUS"}Apagar o __inputs é a correção que não corrige?
Sim. Apagar o __inputs faz o arquivo parecer um dashboard normal, mas cada ${DS_PROMETHEUS} continua lá. O erro deixa de ser “provisionado do jeito errado” e vira a causa 2, e agora nem o diálogo de Import consegue repará-lo, porque o avaliador só substitui nomes declarados no __inputs.
A segunda falsa correção é trocar o placeholder pelo nome do datasource, "Prometheus". O Grafana procura uma chave pelo uid e depois pelo nome, então isso funciona até alguém renomear o datasource ou outra instância dar a ele um nome diferente. É por isso que o validador sinaliza a forma de string simples como datasource-by-name.
Atenção: renomear o datasource para bater com o placeholder não muda nada. O painel está procurando um datasource cujo uid ou nome seja literalmente
${DS_PROMETHEUS}.
Como impedir que ele volte no CI?
Faça o pipeline falhar quando um dashboard commitado ainda tiver um placeholder que nenhuma variável de datasource define:
status=0
for f in dashboards/*.json; do
n=$(jq '[.templating.list[]? | select(.type == "datasource") | .name] as $vars
| [.. | strings | select(startswith("${DS_"))
| ltrimstr("${") | rtrimstr("}")
| select(IN($vars[]) | not)] | length' "$f")
if [ "$n" -ne 0 ]; then echo "$f: $n unresolved placeholder(s)"; status=1; fi
done
exit "$status"No arquivo exportado, ele imprime 3 para esse dashboard. Depois da substituição com walk, ou com a variável de datasource DS_PROMETHEUS no lugar, ele imprime 0.
Faça o loop arquivo por arquivo em vez de passar um glob para jq -e, que define o exit code apenas pela última saída. Rode a guarda no mesmo job que copia os dashboards para o caminho de provisioning, para que um arquivo com falha nunca chegue ao Grafana.
Combine-a com a verificação de uid, porque um arquivo limpo ainda pode citar um uid que falta em algum ambiente. A mesma disciplina vale para as queries dentro dele: Como ler uma consulta PromQL explica o que os painéis estão perguntando, e LogQL vs PromQL ajuda quando um painel do Loki fica ao lado deles.
O que conferir antes de publicar um arquivo de dashboard?
- Exporte com a opção de compartilhamento desligada quando o arquivo for para o seu próprio repositório de provisioning.
- Rode o filtro
paths(...)e leia cada caminho que ele imprimir. - Liste os uids com
GET /api/datasourcesem cada instância de destino. - Fixe esses uids no provisioning do datasource, para que todos os ambientes concordem.
- Substitua com
walk, ou adicione uma variável de datasource por placeholder. - Remova
__inputse__requiressó depois da substituição. - Use
/api/dashboards/importcominputsse você precisar manter o arquivo compartilhável. - Adicione a guarda de CI e torne-a obrigatória antes do deploy.
Antes que o próximo dashboard caia na sua pasta de provisioning, cole-o no Grafana Dashboard Validator e corrija o que ele apontar.
Você mantém os dashboards exportados para compartilhamento e os provisionados como arquivos separados, ou gera um a partir do outro no CI?
Posts relacionados
-
11 min de leitura
Por que o Prometheus descartou meu target? relabel_configs
Um target sumiu ou um label desapareceu após o relabeling. Depure relabel_configs vs metric_relabel_configs, ancoragem de regex e a lógica de keep/drop.
-
11 min de leitura
Actions de relabeling do Prometheus: keep, labelmap, hashmod
Referência das 11 actions de relabeling do Prometheus — replace, keep, drop, labelmap, labeldrop, hashmod, keepequal, lowercase — cada uma com um exemplo.
-
6 min de leitura
Como ler uma consulta PromQL
Uma consulta PromQL é lida de dentro para fora, não da esquerda para a direita. Aprenda as quatro camadas — seletores, intervalos, funções e agregações.