From 8bc31a4da506f59e992d6ca7a99cc419822c3170 Mon Sep 17 00:00:00 2001 From: Ashley Harrison Date: Wed, 8 Oct 2025 11:13:01 +0100 Subject: [PATCH 001/112] Modal: Sticky position buttons (#111991) sticky position modal buttons --- .../grafana-ui/src/components/Modal/getModalStyles.ts | 10 ++++++++-- .../ValueMappingsEditor/ValueMappingsEditorModal.tsx | 3 --- 2 files changed, 8 insertions(+), 5 deletions(-) diff --git a/packages/grafana-ui/src/components/Modal/getModalStyles.ts b/packages/grafana-ui/src/components/Modal/getModalStyles.ts index d6ef33245b6..8defbe310a6 100644 --- a/packages/grafana-ui/src/components/Modal/getModalStyles.ts +++ b/packages/grafana-ui/src/components/Modal/getModalStyles.ts @@ -75,11 +75,17 @@ export const getModalStyles = (theme: GrafanaTheme2) => { }), modalContent: css({ overflow: 'auto', - padding: theme.spacing(3), + padding: theme.spacing(3, 3, 0, 3), + marginBottom: theme.spacing(3), + scrollbarWidth: 'thin', width: '100%', }), modalButtonRow: css({ - paddingTop: theme.spacing(3), + background: theme.colors.background.primary, + position: 'sticky', + bottom: 0, + paddingTop: theme.spacing(2), + zIndex: 1, }), }; }; diff --git a/public/app/features/dimensions/editors/ValueMappingsEditor/ValueMappingsEditorModal.tsx b/public/app/features/dimensions/editors/ValueMappingsEditor/ValueMappingsEditorModal.tsx index 3bd9d7e7916..1ef25b5b2be 100644 --- a/public/app/features/dimensions/editors/ValueMappingsEditor/ValueMappingsEditorModal.tsx +++ b/public/app/features/dimensions/editors/ValueMappingsEditor/ValueMappingsEditorModal.tsx @@ -186,9 +186,6 @@ export function ValueMappingsEditorModal({ value, onChange, onClose, showIconPic export const getStyles = (theme: GrafanaTheme2) => ({ tableWrap: css({ - // TODO remove maxHeight/overflow when Modal.ButtonRow is sticky - maxHeight: 'calc(80vh - 170px)', - overflow: 'auto', minHeight: '40px', }), From e22841ef0c53cfdbeaee2272c63c4d90608ef0f8 Mon Sep 17 00:00:00 2001 From: "grafana-pr-automation[bot]" <140550294+grafana-pr-automation[bot]@users.noreply.github.com> Date: Wed, 8 Oct 2025 10:19:54 +0000 Subject: [PATCH 002/112] I18n: Download translations from Crowdin (#112142) New Crowdin translations by GitHub Action Co-authored-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com> --- public/locales/cs-CZ/grafana.json | 25 ++++++++++++++++++++++--- public/locales/de-DE/grafana.json | 25 ++++++++++++++++++++++--- public/locales/es-ES/grafana.json | 25 ++++++++++++++++++++++--- public/locales/fr-FR/grafana.json | 25 ++++++++++++++++++++++--- public/locales/hu-HU/grafana.json | 25 ++++++++++++++++++++++--- public/locales/id-ID/grafana.json | 25 ++++++++++++++++++++++--- public/locales/it-IT/grafana.json | 25 ++++++++++++++++++++++--- public/locales/ja-JP/grafana.json | 25 ++++++++++++++++++++++--- public/locales/ko-KR/grafana.json | 25 ++++++++++++++++++++++--- public/locales/nl-NL/grafana.json | 25 ++++++++++++++++++++++--- public/locales/pl-PL/grafana.json | 25 ++++++++++++++++++++++--- public/locales/pt-BR/grafana.json | 25 ++++++++++++++++++++++--- public/locales/pt-PT/grafana.json | 25 ++++++++++++++++++++++--- public/locales/ru-RU/grafana.json | 25 ++++++++++++++++++++++--- public/locales/sv-SE/grafana.json | 25 ++++++++++++++++++++++--- public/locales/tr-TR/grafana.json | 25 ++++++++++++++++++++++--- public/locales/zh-Hans/grafana.json | 25 ++++++++++++++++++++++--- public/locales/zh-Hant/grafana.json | 25 ++++++++++++++++++++++--- 18 files changed, 396 insertions(+), 54 deletions(-) diff --git a/public/locales/cs-CZ/grafana.json b/public/locales/cs-CZ/grafana.json index 6957193c08d..de43cb09f04 100644 --- a/public/locales/cs-CZ/grafana.json +++ b/public/locales/cs-CZ/grafana.json @@ -1545,6 +1545,9 @@ "group-status": { "content-the-group-is-being-deleted": "Probíhá odstranění skupiny" }, + "group-wrapper": { + "toggle": "" + }, "header": { "tooltip-remove": "Odebrat výraz „{{refId}}“" }, @@ -1720,6 +1723,9 @@ "placeholder-key": "klíč", "placeholder-value": "hodnota" }, + "left-column": { + "label-instances": "" + }, "link-to-contact-points": { "aria-label-view-or-create-contact-points": "Zobrazit nebo vytvořit kontaktní body", "view-or-create-contact-points": "Zobrazit nebo vytvořit kontaktní body" @@ -2037,6 +2043,12 @@ "body-selected-alertmanager-not-found": "Vybraný správce výstrah již neexistuje nebo nemáte potřebná oprávnění. Vyberte jiného správce výstrah z rozevíracího seznamu.", "title-selected-alertmanager-not-found": "Vybraný správce výstrah nebyl nalezen." }, + "pages": { + "triage": { + "subtitle": "", + "title": "" + } + }, "panel-alert-tab-content": { "alert": { "title-errors-loading-rules": "Chyby při načítání pravidel" @@ -2940,6 +2952,13 @@ "title-warning": "Varování" } }, + "triage": { + "alert-instances": "", + "firing-instances-count": "", + "no-instances-found": "", + "no-labels": "", + "pending-instances-count": "" + }, "type-selector-button": { "add-expression": "Přidat výraz" }, @@ -9502,6 +9521,7 @@ "description-enable-infinite-scrolling": "Experimentální. Požádejte o další výsledky posunutím do dolní části seznamu protokolů.", "description-enable-logs-highlighting": "", "description-show-controls": "Zobrazit ovládací prvky pro přechod na poslední nebo první řádek protokolu a filtry podle úrovně protokolu", + "description-show-log-attributes": "", "fields": { "type": { "loki": { @@ -9558,9 +9578,6 @@ "collapse": "Sbalit štítky", "expand": "Rozbalit štítky" }, - "log-labels-list": { - "log-line": "řádek protokolu" - }, "log-line": { "has-error": "Obsahuje chyby", "is-sampled": "Je vzorkováno", @@ -9600,6 +9617,7 @@ "inline-mode": "Zobrazit v řádku", "link-value-tooltip": "", "links-section": "Odkazy", + "log-attributes-field": "", "log-line-field": "Řádek protokolu", "log-line-section": "Řádek protokolu", "move-displayed-field-down": "Přesunout dolů", @@ -9806,6 +9824,7 @@ "line-contains": "Přidat jako řádek obsahuje filtr", "line-contains-not": "Přidat jako řádek neobsahuje filtr" }, + "show-log-attributes": "", "timestamp-format": "Rozlišení časového razítka", "un-themed-log-details": { "aria-label-data-links": "Propojení dat", diff --git a/public/locales/de-DE/grafana.json b/public/locales/de-DE/grafana.json index 156c4f9e851..f510375db44 100644 --- a/public/locales/de-DE/grafana.json +++ b/public/locales/de-DE/grafana.json @@ -1535,6 +1535,9 @@ "group-status": { "content-the-group-is-being-deleted": "Die Gruppe wird gelöscht" }, + "group-wrapper": { + "toggle": "" + }, "header": { "tooltip-remove": "Ausdruck „{{refId}}“ entfernen" }, @@ -1710,6 +1713,9 @@ "placeholder-key": "Key", "placeholder-value": "Wert" }, + "left-column": { + "label-instances": "" + }, "link-to-contact-points": { "aria-label-view-or-create-contact-points": "Kontaktpunkte anzeigen oder erstellen", "view-or-create-contact-points": "Kontaktpunkte anzeigen oder erstellen" @@ -2025,6 +2031,12 @@ "body-selected-alertmanager-not-found": "Der ausgewählte Alertmanager existiert nicht mehr oder Sie haben keine Berechtigung, darauf zuzugreifen. Sie können einen anderen Alertmanager aus der Dropdown-Liste auswählen.", "title-selected-alertmanager-not-found": "Der ausgewählte Alertmanager wurde nicht gefunden." }, + "pages": { + "triage": { + "subtitle": "", + "title": "" + } + }, "panel-alert-tab-content": { "alert": { "title-errors-loading-rules": "Fehler beim Laden der Regeln" @@ -2916,6 +2928,13 @@ "title-warning": "Warnung" } }, + "triage": { + "alert-instances": "", + "firing-instances-count": "", + "no-instances-found": "", + "no-labels": "", + "pending-instances-count": "" + }, "type-selector-button": { "add-expression": "Ausdruck hinzufügen" }, @@ -9446,6 +9465,7 @@ "description-enable-infinite-scrolling": "Experimentell. Fordern Sie weitere Ergebnisse an, indem Sie zum Ende der Logs-Liste scrollen.", "description-enable-logs-highlighting": "", "description-show-controls": "Anzeige von Steuerelementen für den Sprung zur letzten oder ersten Log-Linie und Filtern nach Log-Ebene", + "description-show-log-attributes": "", "fields": { "type": { "loki": { @@ -9496,9 +9516,6 @@ "collapse": "Labels einklappen", "expand": "Labels ausklappen" }, - "log-labels-list": { - "log-line": "Log-Zeile" - }, "log-line": { "has-error": "Hat Fehler", "is-sampled": "Ist gesampelt", @@ -9538,6 +9555,7 @@ "inline-mode": "Inline anzeigen", "link-value-tooltip": "", "links-section": "Links", + "log-attributes-field": "", "log-line-field": "Log-Zeile", "log-line-section": "Log-Linie", "move-displayed-field-down": "Nach unten", @@ -9744,6 +9762,7 @@ "line-contains": "„Als Zeile hinzufügen“ enthält Filter", "line-contains-not": "„Als Zeile hinzufügen“ enthält keinen Filter" }, + "show-log-attributes": "", "timestamp-format": "Zeitstempelauflösung", "un-themed-log-details": { "aria-label-data-links": "Datenlinks", diff --git a/public/locales/es-ES/grafana.json b/public/locales/es-ES/grafana.json index d7f61d79ae0..a62115bb4f3 100644 --- a/public/locales/es-ES/grafana.json +++ b/public/locales/es-ES/grafana.json @@ -1535,6 +1535,9 @@ "group-status": { "content-the-group-is-being-deleted": "El grupo se está eliminando" }, + "group-wrapper": { + "toggle": "" + }, "header": { "tooltip-remove": "Quitar la expresión «{{refId}}»" }, @@ -1710,6 +1713,9 @@ "placeholder-key": "clave", "placeholder-value": "valor" }, + "left-column": { + "label-instances": "" + }, "link-to-contact-points": { "aria-label-view-or-create-contact-points": "Ver o crear puntos de contacto", "view-or-create-contact-points": "Ver o crear puntos de contacto" @@ -2025,6 +2031,12 @@ "body-selected-alertmanager-not-found": "El Alertmanager seleccionado ya no existe o es posible que no tengas permiso para acceder a él. Puedes seleccionar otro Alertmanager en el menú desplegable.", "title-selected-alertmanager-not-found": "No se ha encontrado el Alertmanager seleccionado." }, + "pages": { + "triage": { + "subtitle": "", + "title": "" + } + }, "panel-alert-tab-content": { "alert": { "title-errors-loading-rules": "Errores al cargar las reglas" @@ -2916,6 +2928,13 @@ "title-warning": "Advertencia" } }, + "triage": { + "alert-instances": "", + "firing-instances-count": "", + "no-instances-found": "", + "no-labels": "", + "pending-instances-count": "" + }, "type-selector-button": { "add-expression": "Añadir expresión" }, @@ -9446,6 +9465,7 @@ "description-enable-infinite-scrolling": "Experimental. Obtenga más resultados desplazándose hasta la parte inferior de la lista de logs.", "description-enable-logs-highlighting": "", "description-show-controls": "Mostrar controles para saltar a la última o primera línea de log y filtra por nivel de log", + "description-show-log-attributes": "", "fields": { "type": { "loki": { @@ -9496,9 +9516,6 @@ "collapse": "Contraer etiquetas", "expand": "Expandir etiquetas" }, - "log-labels-list": { - "log-line": "línea de log" - }, "log-line": { "has-error": "Contiene errores", "is-sampled": "Está muestreado", @@ -9538,6 +9555,7 @@ "inline-mode": "Mostrar en línea", "link-value-tooltip": "", "links-section": "Enlaces", + "log-attributes-field": "", "log-line-field": "Línea de log", "log-line-section": "Línea de log", "move-displayed-field-down": "Bajar", @@ -9744,6 +9762,7 @@ "line-contains": "Añadir como filtro «la línea contiene»", "line-contains-not": "Añadir como filtro «la línea no contiene»" }, + "show-log-attributes": "", "timestamp-format": "Resolución de marca de tiempo", "un-themed-log-details": { "aria-label-data-links": "Enlaces de datos", diff --git a/public/locales/fr-FR/grafana.json b/public/locales/fr-FR/grafana.json index fdfb71b1176..ce4d28c25c2 100644 --- a/public/locales/fr-FR/grafana.json +++ b/public/locales/fr-FR/grafana.json @@ -1535,6 +1535,9 @@ "group-status": { "content-the-group-is-being-deleted": "Le groupe est en cours de suppression" }, + "group-wrapper": { + "toggle": "" + }, "header": { "tooltip-remove": "Supprimer l’expression « {{refId}} »" }, @@ -1710,6 +1713,9 @@ "placeholder-key": "clé", "placeholder-value": "valeur" }, + "left-column": { + "label-instances": "" + }, "link-to-contact-points": { "aria-label-view-or-create-contact-points": "Afficher ou créer des points de contact", "view-or-create-contact-points": "Afficher ou créer des points de contact" @@ -2025,6 +2031,12 @@ "body-selected-alertmanager-not-found": "L’Alertmanager sélectionné n’existe plus ou vous n’avez peut-être pas l’autorisation d’y accéder. Vous pouvez sélectionner un autre Alertmanager dans la liste déroulante.", "title-selected-alertmanager-not-found": "Alertmanager sélectionné introuvable." }, + "pages": { + "triage": { + "subtitle": "", + "title": "" + } + }, "panel-alert-tab-content": { "alert": { "title-errors-loading-rules": "Erreurs lors du chargement des règles" @@ -2916,6 +2928,13 @@ "title-warning": "Avertissement" } }, + "triage": { + "alert-instances": "", + "firing-instances-count": "", + "no-instances-found": "", + "no-labels": "", + "pending-instances-count": "" + }, "type-selector-button": { "add-expression": "Ajouter une expression" }, @@ -9446,6 +9465,7 @@ "description-enable-infinite-scrolling": "Expérimental. Obtenez plus de résultats en faisant défiler jusqu’en bas de la liste des logs.", "description-enable-logs-highlighting": "", "description-show-controls": "Afficher les contrôles pour accéder à la première ou à la dernière ligne du log et filtrer par niveau de log", + "description-show-log-attributes": "", "fields": { "type": { "loki": { @@ -9496,9 +9516,6 @@ "collapse": "Réduire les étiquettes", "expand": "Développer les étiquettes" }, - "log-labels-list": { - "log-line": "ligne de journal" - }, "log-line": { "has-error": "Comporte des erreurs", "is-sampled": "Est échantillonné", @@ -9538,6 +9555,7 @@ "inline-mode": "Afficher en ligne", "link-value-tooltip": "", "links-section": "Liens", + "log-attributes-field": "", "log-line-field": "Ligne de journal", "log-line-section": "Ligne de log", "move-displayed-field-down": "Déplacer vers le bas", @@ -9744,6 +9762,7 @@ "line-contains": "Ajouter car la ligne contient un filtre", "line-contains-not": "Ajouter car la ligne ne contient pas de filtre" }, + "show-log-attributes": "", "timestamp-format": "Résolution des horodatages", "un-themed-log-details": { "aria-label-data-links": "Données de liaison", diff --git a/public/locales/hu-HU/grafana.json b/public/locales/hu-HU/grafana.json index 281e10eb0b2..8a6068343c4 100644 --- a/public/locales/hu-HU/grafana.json +++ b/public/locales/hu-HU/grafana.json @@ -1535,6 +1535,9 @@ "group-status": { "content-the-group-is-being-deleted": "A csoport törlése folyamatban van" }, + "group-wrapper": { + "toggle": "" + }, "header": { "tooltip-remove": "„{{refId}}” kifejezés eltávolítása" }, @@ -1710,6 +1713,9 @@ "placeholder-key": "kulcs", "placeholder-value": "érték" }, + "left-column": { + "label-instances": "" + }, "link-to-contact-points": { "aria-label-view-or-create-contact-points": "Kapcsolattartási pontok megtekintése vagy létrehozása", "view-or-create-contact-points": "Kapcsolattartási pontok megtekintése vagy létrehozása" @@ -2025,6 +2031,12 @@ "body-selected-alertmanager-not-found": "A kijelölt riasztáskezelő már nem létezik, vagy előfordulhat, hogy nincs hozzáférési joga. A legördülő menüből választhat másik riasztáskezelőt.", "title-selected-alertmanager-not-found": "A kiválasztott riasztáskezelő nem található." }, + "pages": { + "triage": { + "subtitle": "", + "title": "" + } + }, "panel-alert-tab-content": { "alert": { "title-errors-loading-rules": "Hiba történt a szabályok betöltésekor" @@ -2916,6 +2928,13 @@ "title-warning": "Figyelmeztetés" } }, + "triage": { + "alert-instances": "", + "firing-instances-count": "", + "no-instances-found": "", + "no-labels": "", + "pending-instances-count": "" + }, "type-selector-button": { "add-expression": "Kifejezés hozzáadása" }, @@ -9446,6 +9465,7 @@ "description-enable-infinite-scrolling": "Kísérleti. További eredmények kéréséhez görgessen a naplók listájának aljára.", "description-enable-logs-highlighting": "", "description-show-controls": "Vezérlők megjelenítése az utolsó vagy az első naplósorra ugráshoz és szűrés naplószint szerint", + "description-show-log-attributes": "", "fields": { "type": { "loki": { @@ -9496,9 +9516,6 @@ "collapse": "Címkék összecsukása", "expand": "Címkék kibontása" }, - "log-labels-list": { - "log-line": "naplósor" - }, "log-line": { "has-error": "Hibákat tartalmaz", "is-sampled": "Mintavételezett", @@ -9538,6 +9555,7 @@ "inline-mode": "Megjelenítés sorban", "link-value-tooltip": "", "links-section": "Hivatkozások", + "log-attributes-field": "", "log-line-field": "Naplósor", "log-line-section": "Naplósor", "move-displayed-field-down": "Mozgatás lefelé", @@ -9744,6 +9762,7 @@ "line-contains": "A hozzáadás sorként szűrőt tartalmaz", "line-contains-not": "A hozzáadás sorként nem tartalmaz szűrőt" }, + "show-log-attributes": "", "timestamp-format": "Időbélyeg felbontása", "un-themed-log-details": { "aria-label-data-links": "Adathivatkozások", diff --git a/public/locales/id-ID/grafana.json b/public/locales/id-ID/grafana.json index 5c8c7499ca6..bded24306e8 100644 --- a/public/locales/id-ID/grafana.json +++ b/public/locales/id-ID/grafana.json @@ -1530,6 +1530,9 @@ "group-status": { "content-the-group-is-being-deleted": "Grup sedang dihapus" }, + "group-wrapper": { + "toggle": "" + }, "header": { "tooltip-remove": "Hapus ekspresi \"{{refId}}\"" }, @@ -1705,6 +1708,9 @@ "placeholder-key": "kunci", "placeholder-value": "nilai" }, + "left-column": { + "label-instances": "" + }, "link-to-contact-points": { "aria-label-view-or-create-contact-points": "Lihat atau buat titik kontak", "view-or-create-contact-points": "Lihat atau buat titik kontak" @@ -2019,6 +2025,12 @@ "body-selected-alertmanager-not-found": "Alertmanager yang dipilih tidak ada lagi atau Anda mungkin tidak memiliki izin untuk mengaksesnya. Anda dapat memilih Alertmanager lain dari menu dropdown.", "title-selected-alertmanager-not-found": "Alertmanager yang dipilih tidak ditemukan." }, + "pages": { + "triage": { + "subtitle": "", + "title": "" + } + }, "panel-alert-tab-content": { "alert": { "title-errors-loading-rules": "Kesalahan saat memuat aturan" @@ -2904,6 +2916,13 @@ "title-warning": "PERINGATAN" } }, + "triage": { + "alert-instances": "", + "firing-instances-count": "", + "no-instances-found": "", + "no-labels": "", + "pending-instances-count": "" + }, "type-selector-button": { "add-expression": "Tambah pola" }, @@ -9418,6 +9437,7 @@ "description-enable-infinite-scrolling": "Eksperimental. Minta lebih banyak hasil dengan menggulir ke bagian bawah daftar log.", "description-enable-logs-highlighting": "", "description-show-controls": "Tampilkan kontrol untuk melompat ke baris log terakhir atau pertama, dan filter berdasarkan level log", + "description-show-log-attributes": "", "fields": { "type": { "loki": { @@ -9465,9 +9485,6 @@ "collapse": "Ciutkan label", "expand": "Perbesar label" }, - "log-labels-list": { - "log-line": "baris log" - }, "log-line": { "has-error": "Memiliki kesalahan", "is-sampled": "Diambil sampelnya", @@ -9507,6 +9524,7 @@ "inline-mode": "Tampilkan sebaris", "link-value-tooltip": "", "links-section": "Tautan", + "log-attributes-field": "", "log-line-field": "Baris log", "log-line-section": "Baris log", "move-displayed-field-down": "Pindahkan ke bawah", @@ -9713,6 +9731,7 @@ "line-contains": "Tambahkan sebagai baris berisi filter", "line-contains-not": "Tambahkan sebagai baris tidak berisi filter" }, + "show-log-attributes": "", "timestamp-format": "Resolusi stempel waktu", "un-themed-log-details": { "aria-label-data-links": "Tautan data", diff --git a/public/locales/it-IT/grafana.json b/public/locales/it-IT/grafana.json index c7cff216116..f650c7eb4f1 100644 --- a/public/locales/it-IT/grafana.json +++ b/public/locales/it-IT/grafana.json @@ -1535,6 +1535,9 @@ "group-status": { "content-the-group-is-being-deleted": "Il gruppo è in fase di eliminazione" }, + "group-wrapper": { + "toggle": "" + }, "header": { "tooltip-remove": "Rimuovi l'espressione \"{{refId}}\"" }, @@ -1710,6 +1713,9 @@ "placeholder-key": "chiave", "placeholder-value": "valore" }, + "left-column": { + "label-instances": "" + }, "link-to-contact-points": { "aria-label-view-or-create-contact-points": "Visualizza o crea punti di contatto", "view-or-create-contact-points": "Visualizza o crea punti di contatto" @@ -2025,6 +2031,12 @@ "body-selected-alertmanager-not-found": "L'Alertmanager selezionato non esiste più o potresti non avere l'autorizzazione per accedervi. È possibile selezionare un diverso Alertmanager dal menu a discesa.", "title-selected-alertmanager-not-found": "Alertmanager selezionato non trovato." }, + "pages": { + "triage": { + "subtitle": "", + "title": "" + } + }, "panel-alert-tab-content": { "alert": { "title-errors-loading-rules": "Errori durante il caricamento delle regole" @@ -2916,6 +2928,13 @@ "title-warning": "Attenzione" } }, + "triage": { + "alert-instances": "", + "firing-instances-count": "", + "no-instances-found": "", + "no-labels": "", + "pending-instances-count": "" + }, "type-selector-button": { "add-expression": "Aggiungi espressione" }, @@ -9446,6 +9465,7 @@ "description-enable-infinite-scrolling": "Sperimentale. Richiedi più risultati scorrendo fino alla fine dell'elenco dei registri.", "description-enable-logs-highlighting": "", "description-show-controls": "Visualizza i controlli per passare all'ultima o alla prima riga del registro e filtra per livello di registro", + "description-show-log-attributes": "", "fields": { "type": { "loki": { @@ -9496,9 +9516,6 @@ "collapse": "Riduci etichette", "expand": "Ingrandisci etichette" }, - "log-labels-list": { - "log-line": "riga registro" - }, "log-line": { "has-error": "Sono presenti errori", "is-sampled": "È campionato", @@ -9538,6 +9555,7 @@ "inline-mode": "Visualizza in linea", "link-value-tooltip": "", "links-section": "Collegamenti", + "log-attributes-field": "", "log-line-field": "Riga registro", "log-line-section": "Riga registro", "move-displayed-field-down": "Sposta in basso", @@ -9744,6 +9762,7 @@ "line-contains": "Aggiungi come riga contenente filtro", "line-contains-not": "Aggiungi come riga non contenente filtro" }, + "show-log-attributes": "", "timestamp-format": "Risoluzione marca temporale", "un-themed-log-details": { "aria-label-data-links": "Collegamenti dati", diff --git a/public/locales/ja-JP/grafana.json b/public/locales/ja-JP/grafana.json index ea02f2cbaab..871b7e1cc36 100644 --- a/public/locales/ja-JP/grafana.json +++ b/public/locales/ja-JP/grafana.json @@ -1530,6 +1530,9 @@ "group-status": { "content-the-group-is-being-deleted": "グループを削除中です" }, + "group-wrapper": { + "toggle": "" + }, "header": { "tooltip-remove": "式「{{refId}}」を削除" }, @@ -1705,6 +1708,9 @@ "placeholder-key": "キー", "placeholder-value": "値" }, + "left-column": { + "label-instances": "" + }, "link-to-contact-points": { "aria-label-view-or-create-contact-points": "連絡先を表示または作成", "view-or-create-contact-points": "連絡先を表示または作成" @@ -2019,6 +2025,12 @@ "body-selected-alertmanager-not-found": "選択したAlertmanagerは存在しないか、アクセス権限がない可能性があります。ドロップダウンから別のAlertmanagerを選択できます。", "title-selected-alertmanager-not-found": "選択したAlertmanagerが見つかりません。" }, + "pages": { + "triage": { + "subtitle": "", + "title": "" + } + }, "panel-alert-tab-content": { "alert": { "title-errors-loading-rules": "ルールの読み込みエラー" @@ -2904,6 +2916,13 @@ "title-warning": "警告" } }, + "triage": { + "alert-instances": "", + "firing-instances-count": "", + "no-instances-found": "", + "no-labels": "", + "pending-instances-count": "" + }, "type-selector-button": { "add-expression": "式を追加" }, @@ -9418,6 +9437,7 @@ "description-enable-infinite-scrolling": "実験的機能。ログリストの一番下までスクロールして、さらに結果をリクエストします。", "description-enable-logs-highlighting": "", "description-show-controls": "最後または最初のログ行にジャンプするコントロールを表示し、ログレベルでフィルタリングします", + "description-show-log-attributes": "", "fields": { "type": { "loki": { @@ -9465,9 +9485,6 @@ "collapse": "ラベルを折りたたむ", "expand": "ラベルを展開" }, - "log-labels-list": { - "log-line": "ログ行" - }, "log-line": { "has-error": "エラーあり", "is-sampled": "サンプリング済み", @@ -9507,6 +9524,7 @@ "inline-mode": "インラインで表示", "link-value-tooltip": "", "links-section": "リンク", + "log-attributes-field": "", "log-line-field": "ログ行", "log-line-section": "ログ行", "move-displayed-field-down": "下へ移動", @@ -9713,6 +9731,7 @@ "line-contains": "行にフィルタが含まれている場合はフィルタとして追加", "line-contains-not": "フィルタが含まれていない行として追加" }, + "show-log-attributes": "", "timestamp-format": "タイムスタンプの表示形式", "un-themed-log-details": { "aria-label-data-links": "データリンク", diff --git a/public/locales/ko-KR/grafana.json b/public/locales/ko-KR/grafana.json index 24199c6b985..dfd13b33cd5 100644 --- a/public/locales/ko-KR/grafana.json +++ b/public/locales/ko-KR/grafana.json @@ -1530,6 +1530,9 @@ "group-status": { "content-the-group-is-being-deleted": "그룹을 삭제하는 중입니다" }, + "group-wrapper": { + "toggle": "" + }, "header": { "tooltip-remove": "'{{refId}}' 표현식 제거" }, @@ -1705,6 +1708,9 @@ "placeholder-key": "키", "placeholder-value": "값" }, + "left-column": { + "label-instances": "" + }, "link-to-contact-points": { "aria-label-view-or-create-contact-points": "연락처 보기 또는 생성", "view-or-create-contact-points": "연락처 보기 또는 생성" @@ -2019,6 +2025,12 @@ "body-selected-alertmanager-not-found": "선택하신 Alertmanager가 더 이상 존재하지 않거나 액세스 권한이 없습니다. 드롭다운에서 다른 Alertmanager를 선택할 수 있습니다.", "title-selected-alertmanager-not-found": "선택한 Alertmanager를 찾을 수 없습니다." }, + "pages": { + "triage": { + "subtitle": "", + "title": "" + } + }, "panel-alert-tab-content": { "alert": { "title-errors-loading-rules": "규칙 로딩 중 오류 발생" @@ -2904,6 +2916,13 @@ "title-warning": "경고" } }, + "triage": { + "alert-instances": "", + "firing-instances-count": "", + "no-instances-found": "", + "no-labels": "", + "pending-instances-count": "" + }, "type-selector-button": { "add-expression": "표현식 추가" }, @@ -9418,6 +9437,7 @@ "description-enable-infinite-scrolling": "실험적. 로그 목록 하단으로 스크롤하여 더 많은 결과를 요청합니다.", "description-enable-logs-highlighting": "", "description-show-controls": "마지막 또는 첫 번째 로그 라인으로 이동하는 컨트롤과 로그 단계별 필터를 표시합니다", + "description-show-log-attributes": "", "fields": { "type": { "loki": { @@ -9465,9 +9485,6 @@ "collapse": "라벨 접기", "expand": "라벨 펼치기" }, - "log-labels-list": { - "log-line": "로그 줄" - }, "log-line": { "has-error": "오류 있음", "is-sampled": "샘플링됨", @@ -9507,6 +9524,7 @@ "inline-mode": "인라인 표시", "link-value-tooltip": "", "links-section": "링크", + "log-attributes-field": "", "log-line-field": "로그 라인", "log-line-section": "로그 선", "move-displayed-field-down": "아래로 이동", @@ -9713,6 +9731,7 @@ "line-contains": "라인에 포함 필터로 추가", "line-contains-not": "라인에 미포함 필터로 추가" }, + "show-log-attributes": "", "timestamp-format": "타임스탬프 해상도", "un-themed-log-details": { "aria-label-data-links": "데이터 링크", diff --git a/public/locales/nl-NL/grafana.json b/public/locales/nl-NL/grafana.json index 0dff04997c0..357f4eace38 100644 --- a/public/locales/nl-NL/grafana.json +++ b/public/locales/nl-NL/grafana.json @@ -1535,6 +1535,9 @@ "group-status": { "content-the-group-is-being-deleted": "De groep wordt verwijderd" }, + "group-wrapper": { + "toggle": "" + }, "header": { "tooltip-remove": "Expressie '{{refId}}' verwijderen" }, @@ -1710,6 +1713,9 @@ "placeholder-key": "sleutel", "placeholder-value": "waarde" }, + "left-column": { + "label-instances": "" + }, "link-to-contact-points": { "aria-label-view-or-create-contact-points": "Contactpunten bekijken of maken", "view-or-create-contact-points": "Contactpunten bekijken of maken" @@ -2025,6 +2031,12 @@ "body-selected-alertmanager-not-found": "De geselecteerde waarschuwingsmanager bestaat niet meer of je hebt mogelijk geen toestemming om deze te openen. Je kunt een andere waarschuwingsmanager selecteren in de vervolgkeuzelijst.", "title-selected-alertmanager-not-found": "Geselecteerde Alertmanager niet gevonden." }, + "pages": { + "triage": { + "subtitle": "", + "title": "" + } + }, "panel-alert-tab-content": { "alert": { "title-errors-loading-rules": "Er is een fout opgetreden bij het laden van regels" @@ -2916,6 +2928,13 @@ "title-warning": "Waarschuwing" } }, + "triage": { + "alert-instances": "", + "firing-instances-count": "", + "no-instances-found": "", + "no-labels": "", + "pending-instances-count": "" + }, "type-selector-button": { "add-expression": "Expressie toevoegen" }, @@ -9446,6 +9465,7 @@ "description-enable-infinite-scrolling": "Experimenteel. Vraag meer resultaten op door naar de onderkant van de loglijst te scrollen.", "description-enable-logs-highlighting": "", "description-show-controls": "Weergavebesturingselementen om naar de laatste of eerste logregel te gaan en filters op logniveau", + "description-show-log-attributes": "", "fields": { "type": { "loki": { @@ -9496,9 +9516,6 @@ "collapse": "Labels samenvouwen", "expand": "Labels uitvouwen" }, - "log-labels-list": { - "log-line": "loglijn" - }, "log-line": { "has-error": "Bevat fouten", "is-sampled": "Is bemonsterd", @@ -9538,6 +9555,7 @@ "inline-mode": "Inline weergave", "link-value-tooltip": "", "links-section": "Links", + "log-attributes-field": "", "log-line-field": "Logregel", "log-line-section": "Logregel", "move-displayed-field-down": "Naar beneden verplaatsen", @@ -9744,6 +9762,7 @@ "line-contains": "Toevoegen als regel filter bevat", "line-contains-not": "Toevoegen als regel geen filter bevat" }, + "show-log-attributes": "", "timestamp-format": "Tijdstempelresolutie", "un-themed-log-details": { "aria-label-data-links": "Gegevenslinks", diff --git a/public/locales/pl-PL/grafana.json b/public/locales/pl-PL/grafana.json index f14689efbeb..a681af84e71 100644 --- a/public/locales/pl-PL/grafana.json +++ b/public/locales/pl-PL/grafana.json @@ -1545,6 +1545,9 @@ "group-status": { "content-the-group-is-being-deleted": "Trwa usuwanie grupy" }, + "group-wrapper": { + "toggle": "" + }, "header": { "tooltip-remove": "Usuń wyrażenie „{{refId}}”" }, @@ -1720,6 +1723,9 @@ "placeholder-key": "klucz", "placeholder-value": "wartość" }, + "left-column": { + "label-instances": "" + }, "link-to-contact-points": { "aria-label-view-or-create-contact-points": "Wyświetl lub utwórz punkty kontaktu", "view-or-create-contact-points": "Wyświetl lub utwórz punkty kontaktu" @@ -2037,6 +2043,12 @@ "body-selected-alertmanager-not-found": "Wybrany menedżer alertów już nie istnieje lub możesz nie mieć do niego dostępu. Możesz wybrać innego menedżera alertów z listy rozwijanej.", "title-selected-alertmanager-not-found": "Nie znaleziono wybranego menedżera alertów." }, + "pages": { + "triage": { + "subtitle": "", + "title": "" + } + }, "panel-alert-tab-content": { "alert": { "title-errors-loading-rules": "Błędy wczytywania reguł" @@ -2940,6 +2952,13 @@ "title-warning": "Ostrzeżenie" } }, + "triage": { + "alert-instances": "", + "firing-instances-count": "", + "no-instances-found": "", + "no-labels": "", + "pending-instances-count": "" + }, "type-selector-button": { "add-expression": "Dodaj wyrażenie" }, @@ -9502,6 +9521,7 @@ "description-enable-infinite-scrolling": "Eksperymentalne. Poproś o więcej wyników, przewijając do dołu listy logów.", "description-enable-logs-highlighting": "", "description-show-controls": "Wyświetlaj elementy sterujące, aby przejść do ostatniego lub pierwszego wiersza logu, i filtruj według poziomu logów", + "description-show-log-attributes": "", "fields": { "type": { "loki": { @@ -9558,9 +9578,6 @@ "collapse": "Zwiń etykiety", "expand": "Rozwiń etykiety" }, - "log-labels-list": { - "log-line": "wiersz logu" - }, "log-line": { "has-error": "Zawiera błędy", "is-sampled": "Jest próbką", @@ -9600,6 +9617,7 @@ "inline-mode": "Wyświetl bezpośrednio", "link-value-tooltip": "", "links-section": "Linki", + "log-attributes-field": "", "log-line-field": "Wiersz logu", "log-line-section": "Wiersz logu", "move-displayed-field-down": "Przesuń w dół", @@ -9806,6 +9824,7 @@ "line-contains": "Dodaj jako wiersz zawierający filtr", "line-contains-not": "Dodaj jako wiersz nie zawierający filtra" }, + "show-log-attributes": "", "timestamp-format": "Rozdzielczość znacznika czasu", "un-themed-log-details": { "aria-label-data-links": "Połączenia danych", diff --git a/public/locales/pt-BR/grafana.json b/public/locales/pt-BR/grafana.json index 0bd9d5932f7..e76ecad087f 100644 --- a/public/locales/pt-BR/grafana.json +++ b/public/locales/pt-BR/grafana.json @@ -1535,6 +1535,9 @@ "group-status": { "content-the-group-is-being-deleted": "O grupo está sendo excluído" }, + "group-wrapper": { + "toggle": "" + }, "header": { "tooltip-remove": "Remover expressão \"{{refId}}\"" }, @@ -1710,6 +1713,9 @@ "placeholder-key": "chave", "placeholder-value": "valor" }, + "left-column": { + "label-instances": "" + }, "link-to-contact-points": { "aria-label-view-or-create-contact-points": "Visualizar ou criar pontos de contato", "view-or-create-contact-points": "Visualizar ou criar pontos de contato" @@ -2025,6 +2031,12 @@ "body-selected-alertmanager-not-found": "O Alertmanager selecionado não existe mais ou talvez você não tenha permissão para acessá-lo. Você pode selecionar um Alertmanager diferente no menu suspenso.", "title-selected-alertmanager-not-found": "Alertmanager selecionado não encontrado." }, + "pages": { + "triage": { + "subtitle": "", + "title": "" + } + }, "panel-alert-tab-content": { "alert": { "title-errors-loading-rules": "Erros ao carregar regras" @@ -2916,6 +2928,13 @@ "title-warning": "Aviso" } }, + "triage": { + "alert-instances": "", + "firing-instances-count": "", + "no-instances-found": "", + "no-labels": "", + "pending-instances-count": "" + }, "type-selector-button": { "add-expression": "Adicionar expressão" }, @@ -9446,6 +9465,7 @@ "description-enable-infinite-scrolling": "Experimental. Solicite mais resultados ao descer até a parte inferior da lista de logs.", "description-enable-logs-highlighting": "", "description-show-controls": "Exibir controles para pular para a última ou primeira linha do log e filtros por nível de log", + "description-show-log-attributes": "", "fields": { "type": { "loki": { @@ -9496,9 +9516,6 @@ "collapse": "Recolher rótulos", "expand": "Expandir rótulos" }, - "log-labels-list": { - "log-line": "linha de log" - }, "log-line": { "has-error": "Contém erros", "is-sampled": "É amostrado", @@ -9538,6 +9555,7 @@ "inline-mode": "Exibir em linha", "link-value-tooltip": "", "links-section": "Links", + "log-attributes-field": "", "log-line-field": "Linha de registro", "log-line-section": "Linha de log", "move-displayed-field-down": "Mover para baixo", @@ -9744,6 +9762,7 @@ "line-contains": "Adicionar como linha contém filtro", "line-contains-not": "Adicionar como linha não contém filtro" }, + "show-log-attributes": "", "timestamp-format": "Resolução de data e hora", "un-themed-log-details": { "aria-label-data-links": "Links de dados", diff --git a/public/locales/pt-PT/grafana.json b/public/locales/pt-PT/grafana.json index 810e35393e5..f398b5d02f4 100644 --- a/public/locales/pt-PT/grafana.json +++ b/public/locales/pt-PT/grafana.json @@ -1535,6 +1535,9 @@ "group-status": { "content-the-group-is-being-deleted": "O grupo está a ser eliminado" }, + "group-wrapper": { + "toggle": "" + }, "header": { "tooltip-remove": "Remover a expressão \"{{refId}}\"" }, @@ -1710,6 +1713,9 @@ "placeholder-key": "chave", "placeholder-value": "valor" }, + "left-column": { + "label-instances": "" + }, "link-to-contact-points": { "aria-label-view-or-create-contact-points": "Visualizar ou criar pontos de contacto", "view-or-create-contact-points": "Visualizar ou criar pontos de contacto" @@ -2025,6 +2031,12 @@ "body-selected-alertmanager-not-found": "O Alertmanager selecionado já não existe ou poderá não ter permissão para aceder ao mesmo. Pode selecionar um Alertmanager diferente no menu suspenso.", "title-selected-alertmanager-not-found": "Alertmanager selecionado não encontrado." }, + "pages": { + "triage": { + "subtitle": "", + "title": "" + } + }, "panel-alert-tab-content": { "alert": { "title-errors-loading-rules": "Erros ao carregar regras" @@ -2916,6 +2928,13 @@ "title-warning": "Aviso" } }, + "triage": { + "alert-instances": "", + "firing-instances-count": "", + "no-instances-found": "", + "no-labels": "", + "pending-instances-count": "" + }, "type-selector-button": { "add-expression": "Adicionar expressão" }, @@ -9446,6 +9465,7 @@ "description-enable-infinite-scrolling": "Experimental. Obtenha mais resultados deslocando o ecrã até à parte inferior da lista de registos.", "description-enable-logs-highlighting": "", "description-show-controls": "Mostrar controlos para aceder à última ou primeira linha de registo e filtrar por nível de registo", + "description-show-log-attributes": "", "fields": { "type": { "loki": { @@ -9496,9 +9516,6 @@ "collapse": "Recolher etiquetas", "expand": "Expandir etiquetas" }, - "log-labels-list": { - "log-line": "linha de registo" - }, "log-line": { "has-error": "Contém erros", "is-sampled": "É amostrado", @@ -9538,6 +9555,7 @@ "inline-mode": "Mostrar em linha", "link-value-tooltip": "", "links-section": "Links", + "log-attributes-field": "", "log-line-field": "Linha de registo", "log-line-section": "Linha de registo", "move-displayed-field-down": "Mover para baixo", @@ -9744,6 +9762,7 @@ "line-contains": "Adicionar, pois a linha contém filtro", "line-contains-not": "Adicionar, pois a linha não contém filtro" }, + "show-log-attributes": "", "timestamp-format": "Resolução do carimbo de data e hora", "un-themed-log-details": { "aria-label-data-links": "Ligações de dados", diff --git a/public/locales/ru-RU/grafana.json b/public/locales/ru-RU/grafana.json index 3b7b08dd15a..ce2adeb4039 100644 --- a/public/locales/ru-RU/grafana.json +++ b/public/locales/ru-RU/grafana.json @@ -1545,6 +1545,9 @@ "group-status": { "content-the-group-is-being-deleted": "Выполняется удаление группы" }, + "group-wrapper": { + "toggle": "" + }, "header": { "tooltip-remove": "Удалить выражение «{{refId}}»" }, @@ -1720,6 +1723,9 @@ "placeholder-key": "ключ", "placeholder-value": "значение" }, + "left-column": { + "label-instances": "" + }, "link-to-contact-points": { "aria-label-view-or-create-contact-points": "Просмотр или создание точек контакта", "view-or-create-contact-points": "Просмотр или создание точек контакта" @@ -2037,6 +2043,12 @@ "body-selected-alertmanager-not-found": "Выбранный обработчик оповещений Alertmanager больше не существует или у вас может не быть разрешения на доступ к нему. Вы можете выбрать другой обработчик оповещений из раскрывающегося списка.", "title-selected-alertmanager-not-found": "Выбранный обработчик оповещений Alertmanager не найден" }, + "pages": { + "triage": { + "subtitle": "", + "title": "" + } + }, "panel-alert-tab-content": { "alert": { "title-errors-loading-rules": "Ошибки при загрузке правил" @@ -2940,6 +2952,13 @@ "title-warning": "Предупреждение" } }, + "triage": { + "alert-instances": "", + "firing-instances-count": "", + "no-instances-found": "", + "no-labels": "", + "pending-instances-count": "" + }, "type-selector-button": { "add-expression": "Добавить выражение" }, @@ -9502,6 +9521,7 @@ "description-enable-infinite-scrolling": "Экспериментальная функция. Запросите больше результатов, прокрутив список журналов до конца.", "description-enable-logs-highlighting": "", "description-show-controls": "Отображать элементы управления для перехода к последней или первой строке журнала и фильтры по уровню журнала", + "description-show-log-attributes": "", "fields": { "type": { "loki": { @@ -9558,9 +9578,6 @@ "collapse": "Свернуть метки", "expand": "Развернуть метки" }, - "log-labels-list": { - "log-line": "строка журнала" - }, "log-line": { "has-error": "Содержит ошибки", "is-sampled": "Подвергается выборке", @@ -9600,6 +9617,7 @@ "inline-mode": "Показать встроенные", "link-value-tooltip": "", "links-section": "Ссылки", + "log-attributes-field": "", "log-line-field": "Строка журнала", "log-line-section": "Строка журнала", "move-displayed-field-down": "Вниз", @@ -9806,6 +9824,7 @@ "line-contains": "Добавить, поскольку строка содержит фильтр", "line-contains-not": "Добавить, поскольку строка не содержит фильтр" }, + "show-log-attributes": "", "timestamp-format": "Разрешение метки времени", "un-themed-log-details": { "aria-label-data-links": "Ссылки на данные", diff --git a/public/locales/sv-SE/grafana.json b/public/locales/sv-SE/grafana.json index 6aab37fd08d..c1234270da4 100644 --- a/public/locales/sv-SE/grafana.json +++ b/public/locales/sv-SE/grafana.json @@ -1535,6 +1535,9 @@ "group-status": { "content-the-group-is-being-deleted": "Gruppen raderas" }, + "group-wrapper": { + "toggle": "" + }, "header": { "tooltip-remove": "Ta bort uttryck ”{{refId}}”" }, @@ -1710,6 +1713,9 @@ "placeholder-key": "nyckel", "placeholder-value": "värde" }, + "left-column": { + "label-instances": "" + }, "link-to-contact-points": { "aria-label-view-or-create-contact-points": "Visa eller skapa kontaktpunkter", "view-or-create-contact-points": "Visa eller skapa kontaktpunkter" @@ -2025,6 +2031,12 @@ "body-selected-alertmanager-not-found": "Vald Alertmanager finns inte längre eller så kanske du inte har behörighet att komma åt den. Du kan välja en annan larmhanterare i rullgardinsmenyn.", "title-selected-alertmanager-not-found": "Vald Alertmanager hittades inte." }, + "pages": { + "triage": { + "subtitle": "", + "title": "" + } + }, "panel-alert-tab-content": { "alert": { "title-errors-loading-rules": "Fel vid laddning av regler" @@ -2916,6 +2928,13 @@ "title-warning": "Varning" } }, + "triage": { + "alert-instances": "", + "firing-instances-count": "", + "no-instances-found": "", + "no-labels": "", + "pending-instances-count": "" + }, "type-selector-button": { "add-expression": "Lägg till uttryck" }, @@ -9446,6 +9465,7 @@ "description-enable-infinite-scrolling": "Experimentellt. Begär fler resultat genom att bläddra till längst ner i logglistan.", "description-enable-logs-highlighting": "", "description-show-controls": "Visa kontroller för att hoppa till den sista eller första loggraden och filtrera efter loggnivå", + "description-show-log-attributes": "", "fields": { "type": { "loki": { @@ -9496,9 +9516,6 @@ "collapse": "Dölj etiketter", "expand": "Expandera etiketter" }, - "log-labels-list": { - "log-line": "loggrad" - }, "log-line": { "has-error": "Innehåller fel", "is-sampled": "Är samplad", @@ -9538,6 +9555,7 @@ "inline-mode": "Visa inline", "link-value-tooltip": "", "links-section": "Länkar", + "log-attributes-field": "", "log-line-field": "Loggrad", "log-line-section": "Loggrad", "move-displayed-field-down": "Flytta ner", @@ -9744,6 +9762,7 @@ "line-contains": "Lägg till som rad innehåller filter", "line-contains-not": "Lägg till som rad innehåller inte filter" }, + "show-log-attributes": "", "timestamp-format": "Tidsstämpelupplösning", "un-themed-log-details": { "aria-label-data-links": "Datalänkar", diff --git a/public/locales/tr-TR/grafana.json b/public/locales/tr-TR/grafana.json index cfb8e541e9c..1cf3a90924d 100644 --- a/public/locales/tr-TR/grafana.json +++ b/public/locales/tr-TR/grafana.json @@ -1535,6 +1535,9 @@ "group-status": { "content-the-group-is-being-deleted": "Grup siliniyor" }, + "group-wrapper": { + "toggle": "" + }, "header": { "tooltip-remove": "\"{{refId}}\" ifadesini kaldır" }, @@ -1710,6 +1713,9 @@ "placeholder-key": "anahtar", "placeholder-value": "değer" }, + "left-column": { + "label-instances": "" + }, "link-to-contact-points": { "aria-label-view-or-create-contact-points": "İletişim noktalarını görüntüle veya oluştur", "view-or-create-contact-points": "İletişim noktalarını görüntüle veya oluştur" @@ -2025,6 +2031,12 @@ "body-selected-alertmanager-not-found": "Seçilen Alertmanager artık mevcut değil veya erişim izniniz olmayabilir. Açılır menüden başka bir Alertmanager seçebilirsiniz.", "title-selected-alertmanager-not-found": "Seçili Alertmanager bulunamadı." }, + "pages": { + "triage": { + "subtitle": "", + "title": "" + } + }, "panel-alert-tab-content": { "alert": { "title-errors-loading-rules": "Kurallar yüklenirken hata oluştu" @@ -2916,6 +2928,13 @@ "title-warning": "Uyarı" } }, + "triage": { + "alert-instances": "", + "firing-instances-count": "", + "no-instances-found": "", + "no-labels": "", + "pending-instances-count": "" + }, "type-selector-button": { "add-expression": "İfade ekle" }, @@ -9446,6 +9465,7 @@ "description-enable-infinite-scrolling": "Deneysel. Günlük listesinin en altına kaydırarak daha fazla sonuç isteyin.", "description-enable-logs-highlighting": "", "description-show-controls": "Son veya ilk günlük satırına atlamak için ekran kontrolleri ve günlük düzeyine göre filtreler", + "description-show-log-attributes": "", "fields": { "type": { "loki": { @@ -9496,9 +9516,6 @@ "collapse": "Etiketleri daralt", "expand": "Etiketleri genişlet" }, - "log-labels-list": { - "log-line": "günlük kaydı satırı" - }, "log-line": { "has-error": "Hatalar var", "is-sampled": "Örneklendi", @@ -9538,6 +9555,7 @@ "inline-mode": "Satır içi görüntüle", "link-value-tooltip": "", "links-section": "Bağlantılar", + "log-attributes-field": "", "log-line-field": "Günlük satırı", "log-line-section": "Günlük satırı", "move-displayed-field-down": "Aşağı taşı", @@ -9744,6 +9762,7 @@ "line-contains": "Satır içerir filtresi olarak ekle", "line-contains-not": "Satır içermez filtresi olarak ekle" }, + "show-log-attributes": "", "timestamp-format": "", "un-themed-log-details": { "aria-label-data-links": "Veri bağlantıları", diff --git a/public/locales/zh-Hans/grafana.json b/public/locales/zh-Hans/grafana.json index b285f3a578e..ebf0174dd18 100644 --- a/public/locales/zh-Hans/grafana.json +++ b/public/locales/zh-Hans/grafana.json @@ -1530,6 +1530,9 @@ "group-status": { "content-the-group-is-being-deleted": "正在删除组" }, + "group-wrapper": { + "toggle": "" + }, "header": { "tooltip-remove": "移除表达式“{{refId}}”" }, @@ -1705,6 +1708,9 @@ "placeholder-key": "密钥", "placeholder-value": "值" }, + "left-column": { + "label-instances": "" + }, "link-to-contact-points": { "aria-label-view-or-create-contact-points": "查看或创建联络点", "view-or-create-contact-points": "查看或创建联络点" @@ -2019,6 +2025,12 @@ "body-selected-alertmanager-not-found": "所选 Alertmanager 不再存在,或者您可能没有访问它的权限。您可以从下拉列表中选择其他 Alertmanager。", "title-selected-alertmanager-not-found": "未找到所选的 Alertmanager。" }, + "pages": { + "triage": { + "subtitle": "", + "title": "" + } + }, "panel-alert-tab-content": { "alert": { "title-errors-loading-rules": "加载规则时出错" @@ -2904,6 +2916,13 @@ "title-warning": "警告" } }, + "triage": { + "alert-instances": "", + "firing-instances-count": "", + "no-instances-found": "", + "no-labels": "", + "pending-instances-count": "" + }, "type-selector-button": { "add-expression": "添加表达式" }, @@ -9418,6 +9437,7 @@ "description-enable-infinite-scrolling": "实验性。通过滚动到日志列表底部来请求更多结果。", "description-enable-logs-highlighting": "", "description-show-controls": "显示控件以跳转到最后一行或第一行日志,并按日志级别筛选", + "description-show-log-attributes": "", "fields": { "type": { "loki": { @@ -9465,9 +9485,6 @@ "collapse": "收起标签", "expand": "展开标签" }, - "log-labels-list": { - "log-line": "日志行" - }, "log-line": { "has-error": "有错误", "is-sampled": "已采样", @@ -9507,6 +9524,7 @@ "inline-mode": "内联显示", "link-value-tooltip": "", "links-section": "关联", + "log-attributes-field": "", "log-line-field": "日志行", "log-line-section": "日志行", "move-displayed-field-down": "下移", @@ -9713,6 +9731,7 @@ "line-contains": "添加为包含筛选器的行", "line-contains-not": "添加为不包含筛选器的行" }, + "show-log-attributes": "", "timestamp-format": "时间戳分辨率", "un-themed-log-details": { "aria-label-data-links": "数据关联", diff --git a/public/locales/zh-Hant/grafana.json b/public/locales/zh-Hant/grafana.json index 85df8d7ecdd..06e5c263aba 100644 --- a/public/locales/zh-Hant/grafana.json +++ b/public/locales/zh-Hant/grafana.json @@ -1530,6 +1530,9 @@ "group-status": { "content-the-group-is-being-deleted": "正在刪除群組" }, + "group-wrapper": { + "toggle": "" + }, "header": { "tooltip-remove": "移除表達式「{{refId}}」" }, @@ -1705,6 +1708,9 @@ "placeholder-key": "key", "placeholder-value": "值" }, + "left-column": { + "label-instances": "" + }, "link-to-contact-points": { "aria-label-view-or-create-contact-points": "檢視或建立聯絡點", "view-or-create-contact-points": "檢視或建立聯絡點" @@ -2019,6 +2025,12 @@ "body-selected-alertmanager-not-found": "所選的 Alertmanager 不再存在,或者您可能沒有存取權限。您可以從下拉式選單中選擇其他的 Alertmanager。", "title-selected-alertmanager-not-found": "找不到所選的 Alertmanager。" }, + "pages": { + "triage": { + "subtitle": "", + "title": "" + } + }, "panel-alert-tab-content": { "alert": { "title-errors-loading-rules": "載入規則時發生錯誤" @@ -2904,6 +2916,13 @@ "title-warning": "警告" } }, + "triage": { + "alert-instances": "", + "firing-instances-count": "", + "no-instances-found": "", + "no-labels": "", + "pending-instances-count": "" + }, "type-selector-button": { "add-expression": "新增表達式" }, @@ -9418,6 +9437,7 @@ "description-enable-infinite-scrolling": "實驗性。捲動至紀錄清單底部,以請求更多結果。", "description-enable-logs-highlighting": "", "description-show-controls": "顯示控制項以跳至最後或第一個紀錄行,並按紀錄層級篩選", + "description-show-log-attributes": "", "fields": { "type": { "loki": { @@ -9465,9 +9485,6 @@ "collapse": "收闔標籤", "expand": "展開標籤" }, - "log-labels-list": { - "log-line": "紀錄行" - }, "log-line": { "has-error": "有錯誤", "is-sampled": "已採樣", @@ -9507,6 +9524,7 @@ "inline-mode": "顯示內嵌", "link-value-tooltip": "", "links-section": "連結", + "log-attributes-field": "", "log-line-field": "紀錄行", "log-line-section": "紀錄行", "move-displayed-field-down": "下移", @@ -9713,6 +9731,7 @@ "line-contains": "新增為包含篩選條件的行", "line-contains-not": "新增為不包含篩選條件的行" }, + "show-log-attributes": "", "timestamp-format": "時間戳記解決方案", "un-themed-log-details": { "aria-label-data-links": "資料連結", From 69184b5027304555ff975bcb800245ea55e818b5 Mon Sep 17 00:00:00 2001 From: Matheus Macabu Date: Wed, 8 Oct 2025 12:35:28 +0200 Subject: [PATCH 003/112] Go: Update to 1.25.2 + golangci-lint v2.5.0 + golang.org/x/net v0.45.0 (#112149) * Go: Update to 1.25.2 * golangci-lint: Update to 2.5.0 * Dependencies: Bump golang.org/x/net to v0.45.0 --- .citools/src/air/go.mod | 7 +- .citools/src/air/go.sum | 153 +++++++- .citools/src/bra/go.mod | 4 +- .citools/src/bra/go.sum | 4 +- .citools/src/cog/go.mod | 12 +- .citools/src/cog/go.sum | 28 +- .citools/src/cue/go.mod | 14 +- .citools/src/cue/go.sum | 24 +- .citools/src/golangci-lint/go.mod | 141 ++++---- .citools/src/golangci-lint/go.sum | 329 +++++++++--------- .citools/src/jb/go.mod | 4 +- .citools/src/jb/go.sum | 4 +- .citools/src/lefthook/go.mod | 9 +- .citools/src/lefthook/go.sum | 16 +- .citools/src/swagger/go.mod | 14 +- .citools/src/swagger/go.sum | 24 +- .github/workflows/go-lint.yml | 2 +- Dockerfile | 2 +- Makefile | 2 +- apps/advisor/go.mod | 2 +- apps/alerting/alertenrichment/go.mod | 4 +- apps/alerting/alertenrichment/go.sum | 4 +- apps/alerting/notifications/go.mod | 4 +- apps/alerting/notifications/go.sum | 4 +- apps/alerting/rules/go.mod | 4 +- apps/alerting/rules/go.sum | 4 +- apps/correlations/go.mod | 6 +- apps/correlations/go.sum | 4 +- apps/dashboard/go.mod | 4 +- apps/dashboard/go.sum | 4 +- apps/folder/go.mod | 4 +- apps/folder/go.sum | 4 +- apps/iam/go.mod | 4 +- apps/iam/go.sum | 4 +- apps/iam/local/Dockerfile | 2 +- apps/investigations/go.mod | 4 +- apps/investigations/go.sum | 4 +- apps/playlist/go.mod | 4 +- apps/playlist/go.sum | 4 +- apps/plugins/go.mod | 4 +- apps/plugins/go.sum | 4 +- apps/preferences/go.mod | 4 +- apps/preferences/go.sum | 4 +- apps/provisioning/go.mod | 4 +- apps/provisioning/go.sum | 4 +- apps/scope/go.mod | 4 +- apps/scope/go.sum | 4 +- apps/secret/go.mod | 4 +- apps/secret/go.sum | 4 +- apps/shorturl/go.mod | 4 +- apps/shorturl/go.sum | 4 +- .../docker/blocks/prometheus_high_card/go.mod | 4 +- .../docker/blocks/prometheus_high_card/go.sum | 4 +- devenv/docker/blocks/prometheus_utf8/go.mod | 4 +- devenv/docker/blocks/prometheus_utf8/go.sum | 4 +- .../docker/blocks/stateful_webhook/Dockerfile | 2 +- go.mod | 4 +- go.sum | 4 +- go.work | 2 +- go.work.sum | 2 +- hack/go.mod | 10 +- hack/go.sum | 16 +- pkg/aggregator/go.mod | 4 +- pkg/aggregator/go.sum | 4 +- pkg/apimachinery/go.mod | 4 +- pkg/apimachinery/go.sum | 4 +- pkg/apiserver/go.mod | 4 +- pkg/apiserver/go.sum | 4 +- pkg/build/go.mod | 4 +- pkg/build/go.sum | 4 +- pkg/build/wire/go.mod | 2 +- pkg/codegen/go.mod | 4 +- pkg/codegen/go.sum | 4 +- pkg/plugins/codegen/go.mod | 4 +- pkg/plugins/codegen/go.sum | 4 +- pkg/promlib/go.mod | 4 +- pkg/promlib/go.sum | 4 +- pkg/semconv/go.mod | 2 +- scripts/go-workspace/go.mod | 2 +- scripts/modowners/go.mod | 2 +- 80 files changed, 597 insertions(+), 442 deletions(-) diff --git a/.citools/src/air/go.mod b/.citools/src/air/go.mod index 6fd4736b93d..5a54172ce9b 100644 --- a/.citools/src/air/go.mod +++ b/.citools/src/air/go.mod @@ -1,6 +1,6 @@ module air -go 1.24.4 +go 1.25.2 tool github.com/air-verse/air @@ -21,7 +21,8 @@ require ( github.com/spf13/afero v1.14.0 // indirect github.com/spf13/cast v1.8.0 // indirect github.com/tdewolff/parse/v2 v2.8.1 // indirect - golang.org/x/sys v0.33.0 // indirect - golang.org/x/text v0.25.0 // indirect + golang.org/x/net v0.45.0 // indirect + golang.org/x/sys v0.36.0 // indirect + golang.org/x/text v0.29.0 // indirect google.golang.org/protobuf v1.36.6 // indirect ) diff --git a/.citools/src/air/go.sum b/.citools/src/air/go.sum index d733e3519f3..fbefab6f556 100644 --- a/.citools/src/air/go.sum +++ b/.citools/src/air/go.sum @@ -1,45 +1,190 @@ dario.cat/mergo v1.0.2 h1:85+piFYR1tMbRrLcDwR18y4UKJ3aH1Tbzi24VRW1TK8= dario.cat/mergo v1.0.2/go.mod h1:E/hbnu0NxMFBjpMIE34DRGLWqDy0g5FuKDhCb31ngxA= +github.com/BurntSushi/locker v0.0.0-20171006230638-a6e239ea1c69 h1:+tu3HOoMXB7RXEINRVIpxJCT+KdYiI7LAEAUrOw3dIU= +github.com/BurntSushi/locker v0.0.0-20171006230638-a6e239ea1c69/go.mod h1:L1AbZdiDllfyYH5l5OkAaZtk7VkWe89bPJFmnDBNHxg= github.com/air-verse/air v1.62.0 h1:6CoXL4MAX9dc4xAzLfjMcDfbBoGmW5VjuuTV/1+bI+M= github.com/air-verse/air v1.62.0/go.mod h1:EO+jWuetL10tS9raffwg8WEV0t0KUeucRRaf9ii86dA= +github.com/alecthomas/chroma/v2 v2.17.2 h1:Rm81SCZ2mPoH+Q8ZCc/9YvzPUN/E7HgPiPJD8SLV6GI= +github.com/alecthomas/chroma/v2 v2.17.2/go.mod h1:RVX6AvYm4VfYe/zsk7mjHueLDZor3aWCNE14TFlepBk= +github.com/armon/go-radix v1.0.1-0.20221118154546-54df44f2176c h1:651/eoCRnQ7YtSjAnSzRucrJz+3iGEFt+ysraELS81M= +github.com/armon/go-radix v1.0.1-0.20221118154546-54df44f2176c/go.mod h1:ufUuZ+zHj4x4TnLV4JWEpy2hxWSpsRywHrMgIH9cCH8= +github.com/bep/clocks v0.5.0 h1:hhvKVGLPQWRVsBP/UB7ErrHYIO42gINVbvqxvYTPVps= +github.com/bep/clocks v0.5.0/go.mod h1:SUq3q+OOq41y2lRQqH5fsOoxN8GbxSiT6jvoVVLCVhU= +github.com/bep/debounce v1.2.1 h1:v67fRdBA9UQu2NhLFXrSg0Brw7CexQekrBwDMM8bzeY= +github.com/bep/debounce v1.2.1/go.mod h1:H8yggRPQKLUhUoqrJC1bO2xNya7vanpDl7xR3ISbCJ0= +github.com/bep/gitmap v1.6.0 h1:sDuQMm9HoTL0LtlrfxjbjgAg2wHQd4nkMup2FInYzhA= +github.com/bep/gitmap v1.6.0/go.mod h1:n+3W1f/rot2hynsqEGxGMErPRgT41n9CkGuzPvz9cIw= +github.com/bep/goat v0.5.0 h1:S8jLXHCVy/EHIoCY+btKkmcxcXFd34a0Q63/0D4TKeA= +github.com/bep/goat v0.5.0/go.mod h1:Md9x7gRxiWKs85yHlVTvHQw9rg86Bm+Y4SuYE8CTH7c= github.com/bep/godartsass/v2 v2.5.0 h1:tKRvwVdyjCIr48qgtLa4gHEdtRkPF8H1OeEhJAEv7xg= github.com/bep/godartsass/v2 v2.5.0/go.mod h1:rjsi1YSXAl/UbsGL85RLDEjRKdIKUlMQHr6ChUNYOFU= github.com/bep/golibsass v1.2.0 h1:nyZUkKP/0psr8nT6GR2cnmt99xS93Ji82ZD9AgOK6VI= github.com/bep/golibsass v1.2.0/go.mod h1:DL87K8Un/+pWUS75ggYv41bliGiolxzDKWJAq3eJ1MA= +github.com/bep/goportabletext v0.1.0 h1:8dqym2So1cEqVZiBa4ZnMM1R9l/DnC1h4ONg4J5kujw= +github.com/bep/goportabletext v0.1.0/go.mod h1:6lzSTsSue75bbcyvVc0zqd1CdApuT+xkZQ6Re5DzZFg= +github.com/bep/gowebp v0.4.0 h1:QihuVnvIKbRoeBNQkN0JPMM8ClLmD6V2jMftTFwSK3Q= +github.com/bep/gowebp v0.4.0/go.mod h1:95gtYkAA8iIn1t3HkAPurRCVGV/6NhgaHJ1urz0iIwc= +github.com/bep/imagemeta v0.12.0 h1:ARf+igs5B7pf079LrqRnwzQ/wEB8Q9v4NSDRZO1/F5k= +github.com/bep/imagemeta v0.12.0/go.mod h1:23AF6O+4fUi9avjiydpKLStUNtJr5hJB4rarG18JpN8= +github.com/bep/lazycache v0.8.0 h1:lE5frnRjxaOFbkPZ1YL6nijzOPPz6zeXasJq8WpG4L8= +github.com/bep/lazycache v0.8.0/go.mod h1:BQ5WZepss7Ko91CGdWz8GQZi/fFnCcyWupv8gyTeKwk= +github.com/bep/logg v0.4.0 h1:luAo5mO4ZkhA5M1iDVDqDqnBBnlHjmtZF6VAyTp+nCQ= +github.com/bep/logg v0.4.0/go.mod h1:Ccp9yP3wbR1mm++Kpxet91hAZBEQgmWgFgnXX3GkIV0= +github.com/bep/overlayfs v0.10.0 h1:wS3eQ6bRsLX+4AAmwGjvoFSAQoeheamxofFiJ2SthSE= +github.com/bep/overlayfs v0.10.0/go.mod h1:ouu4nu6fFJaL0sPzNICzxYsBeWwrjiTdFZdK4lI3tro= +github.com/bep/tmc v0.5.1 h1:CsQnSC6MsomH64gw0cT5f+EwQDcvZz4AazKunFwTpuI= +github.com/bep/tmc v0.5.1/go.mod h1:tGYHN8fS85aJPhDLgXETVKp+PR382OvFi2+q2GkGsq0= +github.com/cespare/xxhash/v2 v2.3.0 h1:UL815xU9SqsFlibzuggzjXhog7bL6oX9BbNZnL2UFvs= +github.com/cespare/xxhash/v2 v2.3.0/go.mod h1:VGX0DQ3Q6kWi7AoAeZDth3/j3BFtOZR5XLFGgcrjCOs= +github.com/clbanning/mxj/v2 v2.7.0 h1:WA/La7UGCanFe5NpHF0Q3DNtnCsVoxbPKuyBNHWRyME= +github.com/clbanning/mxj/v2 v2.7.0/go.mod h1:hNiWqW14h+kc+MdF9C6/YoRfjEJoR3ou6tn/Qo+ve2s= github.com/creack/pty v1.1.24 h1:bJrF4RRfyJnbTJqzRLHzcGaZK1NeM5kTC9jGgovnR1s= github.com/creack/pty v1.1.24/go.mod h1:08sCNb52WyoAwi2QDyzUCTgcvVFhUzewun7wtTfvcwE= +github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c= +github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= +github.com/disintegration/gift v1.2.1 h1:Y005a1X4Z7Uc+0gLpSAsKhWi4qLtsdEcMIbbdvdZ6pc= +github.com/disintegration/gift v1.2.1/go.mod h1:Jh2i7f7Q2BM7Ezno3PhfezbR1xpUg9dUg3/RlKGr4HI= +github.com/dlclark/regexp2 v1.11.5 h1:Q/sSnsKerHeCkc/jSTNq1oCm7KiVgUMZRDUoRu0JQZQ= +github.com/dlclark/regexp2 v1.11.5/go.mod h1:DHkYz0B9wPfa6wondMfaivmHpzrQ3v9q8cnmRbL6yW8= +github.com/evanw/esbuild v0.25.3 h1:4JKyUsm/nHDhpxis4IyWXAi8GiyTwG1WdEp6OhGVE8U= +github.com/evanw/esbuild v0.25.3/go.mod h1:D2vIQZqV/vIf/VRHtViaUtViZmG7o+kKmlBfVQuRi48= github.com/fatih/color v1.18.0 h1:S8gINlzdQ840/4pfAwic/ZE0djQEH3wM94VfqLTZcOM= github.com/fatih/color v1.18.0/go.mod h1:4FelSpRwEGDpQ12mAdzqdOukCy4u8WUtOY6lkT/6HfU= github.com/frankban/quicktest v1.7.2/go.mod h1:jaStnuzAqU1AJdCO0l53JDCJrVDKcS03DbaAcR7Ks/o= +github.com/frankban/quicktest v1.14.6 h1:7Xjx+VpznH+oBnejlPUj8oUpdxnVs4f8XU8WnHkI4W8= +github.com/frankban/quicktest v1.14.6/go.mod h1:4ptaffx2x8+WTWXmUCuVU6aPUX1/Mz7zb5vbUoiM6w0= github.com/fsnotify/fsnotify v1.9.0 h1:2Ml+OJNzbYCTzsxtv8vKSFD9PbJjmhYF14k/jKC7S9k= github.com/fsnotify/fsnotify v1.9.0/go.mod h1:8jBTzvmWwFyi3Pb8djgCCO5IBqzKJ/Jwo8TRcHyHii0= +github.com/getkin/kin-openapi v0.132.0 h1:3ISeLMsQzcb5v26yeJrBcdTCEQTag36ZjaGk7MIRUwk= +github.com/getkin/kin-openapi v0.132.0/go.mod h1:3OlG51PCYNsPByuiMB0t4fjnNlIDnaEDsjiKUV8nL58= +github.com/ghodss/yaml v1.0.0 h1:wQHKEahhL6wmXdzwWG11gIVCkOv05bNOh+Rxn0yngAk= +github.com/ghodss/yaml v1.0.0/go.mod h1:4dBDuWmgqj2HViK6kFavaiC9ZROes6MMH2rRYeMEF04= +github.com/go-openapi/jsonpointer v0.21.0 h1:YgdVicSA9vH5RiHs9TZW5oyafXZFc6+2Vc1rr/O9oNQ= +github.com/go-openapi/jsonpointer v0.21.0/go.mod h1:IUyH9l/+uyhIYQ/PXVA41Rexl+kOkAPDdXEYns6fzUY= +github.com/go-openapi/swag v0.23.0 h1:vsEVJDUo2hPJ2tu0/Xc+4noaxyEffXNIs3cOULZ+GrE= +github.com/go-openapi/swag v0.23.0/go.mod h1:esZ8ITTYEsH1V2trKHjAN8Ai7xHb8RV+YSZ577vPjgQ= +github.com/gobuffalo/flect v1.0.3 h1:xeWBM2nui+qnVvNM4S3foBhCAL2XgPU+a7FdpelbTq4= +github.com/gobuffalo/flect v1.0.3/go.mod h1:A5msMlrHtLqh9umBSnvabjsMrCcCpAyzglnDvkbYKHs= github.com/gobwas/glob v0.2.3 h1:A4xDbljILXROh+kObIiy5kIaPYD8e96x1tgBhUI5J+Y= github.com/gobwas/glob v0.2.3/go.mod h1:d3Ez4x06l9bZtSvzIay5+Yzi0fmZzPgnTbPcKjJAkT8= +github.com/gohugoio/go-i18n/v2 v2.1.3-0.20230805085216-e63c13218d0e h1:QArsSubW7eDh8APMXkByjQWvuljwPGAGQpJEFn0F0wY= +github.com/gohugoio/go-i18n/v2 v2.1.3-0.20230805085216-e63c13218d0e/go.mod h1:3Ltoo9Banwq0gOtcOwxuHG6omk+AwsQPADyw2vQYOJQ= +github.com/gohugoio/hashstructure v0.5.0 h1:G2fjSBU36RdwEJBWJ+919ERvOVqAg9tfcYp47K9swqg= +github.com/gohugoio/hashstructure v0.5.0/go.mod h1:Ser0TniXuu/eauYmrwM4o64EBvySxNzITEOLlm4igec= +github.com/gohugoio/httpcache v0.7.0 h1:ukPnn04Rgvx48JIinZvZetBfHaWE7I01JR2Q2RrQ3Vs= +github.com/gohugoio/httpcache v0.7.0/go.mod h1:fMlPrdY/vVJhAriLZnrF5QpN3BNAcoBClgAyQd+lGFI= github.com/gohugoio/hugo v0.147.6 h1:rL4rnus/5qzj4+FoA+JMzsVvFJ2YZdVIH6pbuCB2P84= github.com/gohugoio/hugo v0.147.6/go.mod h1:Sb2COQPDPYG+tRSpePtzKytiuVDqkBivEhgIew1QbNo= +github.com/gohugoio/hugo-goldmark-extensions/extras v0.3.0 h1:gj49kTR5Z4Hnm0ZaQrgPVazL3DUkppw+x6XhHCmh+Wk= +github.com/gohugoio/hugo-goldmark-extensions/extras v0.3.0/go.mod h1:IMMj7xiUbLt1YNJ6m7AM4cnsX4cFnnfkleO/lBHGzUg= +github.com/gohugoio/hugo-goldmark-extensions/passthrough v0.3.1 h1:nUzXfRTszLliZuN0JTKeunXTRaiFX6ksaWP0puLLYAY= +github.com/gohugoio/hugo-goldmark-extensions/passthrough v0.3.1/go.mod h1:Wy8ThAA8p2/w1DY05vEzq6EIeI2mzDjvHsu7ULBVwog= +github.com/gohugoio/locales v0.14.0 h1:Q0gpsZwfv7ATHMbcTNepFd59H7GoykzWJIxi113XGDc= +github.com/gohugoio/locales v0.14.0/go.mod h1:ip8cCAv/cnmVLzzXtiTpPwgJ4xhKZranqNqtoIu0b/4= +github.com/gohugoio/localescompressed v1.0.1 h1:KTYMi8fCWYLswFyJAeOtuk/EkXR/KPTHHNN9OS+RTxo= +github.com/gohugoio/localescompressed v1.0.1/go.mod h1:jBF6q8D7a0vaEmcWPNcAjUZLJaIVNiwvM3WlmTvooB0= github.com/google/go-cmp v0.3.1/go.mod h1:8QqcDgzrUqlUb/G2PQTWiueGozuR1884gddMywk6iLU= +github.com/google/go-cmp v0.7.0 h1:wk8382ETsv4JYUZwIsn6YpYiWiBsYLSJiTsyBybVuN8= +github.com/google/go-cmp v0.7.0/go.mod h1:pXiqmnSA92OHEEa9HXL2W4E7lf9JzCmGVUdgjX3N/iU= +github.com/hairyhenderson/go-codeowners v0.7.0 h1:s0W4wF8bdsBEjTWzwzSlsatSthWtTAF2xLgo4a4RwAo= +github.com/hairyhenderson/go-codeowners v0.7.0/go.mod h1:wUlNgQ3QjqC4z8DnM5nnCYVq/icpqXJyJOukKx5U8/Q= +github.com/hashicorp/golang-lru/v2 v2.0.7 h1:a+bsQ5rvGLjzHuww6tVxozPZFVghXaHOwFs4luLUK2k= +github.com/hashicorp/golang-lru/v2 v2.0.7/go.mod h1:QeFd9opnmA6QUJc5vARoKUSoFhyfM2/ZepoAG6RGpeM= +github.com/jdkato/prose v1.2.1 h1:Fp3UnJmLVISmlc57BgKUzdjr0lOtjqTZicL3PaYy6cU= +github.com/jdkato/prose v1.2.1/go.mod h1:AiRHgVagnEx2JbQRQowVBKjG0bcs/vtkGCH1dYAL1rA= +github.com/josharian/intern v1.0.0 h1:vlS4z54oSdjm0bgjRigI+G1HpF+tI+9rE5LLzOg8HmY= +github.com/josharian/intern v1.0.0/go.mod h1:5DoeVV0s6jJacbCEi61lwdGj/aVlrQvzHFFd8Hwg//Y= github.com/kr/pretty v0.1.0/go.mod h1:dAy3ld7l9f0ibDNOQOHHMYYIIbhfbHSm3C4ZsoJORNo= +github.com/kr/pretty v0.3.1 h1:flRD4NNwYAUpkphVc1HcthR4KEIFJ65n8Mw5qdRn3LE= +github.com/kr/pretty v0.3.1/go.mod h1:hoEshYVHaxMs3cyo3Yncou5ZscifuDolrwPKZanG3xk= github.com/kr/pty v1.1.1/go.mod h1:pFQYn66WHrOpPYNljwOMqo10TkYh1fy3cYio2l3bCsQ= github.com/kr/text v0.1.0/go.mod h1:4Jbv+DJW3UT/LiOwJeYQe1efqtUx/iVham/4vfdArNI= +github.com/kr/text v0.2.0 h1:5Nx0Ya0ZqY2ygV366QzturHI13Jq95ApcVaJBhpS+AY= +github.com/kr/text v0.2.0/go.mod h1:eLer722TekiGuMkidMxC/pM04lWEeraHUUmBw8l2grE= +github.com/kyokomi/emoji/v2 v2.2.13 h1:GhTfQa67venUUvmleTNFnb+bi7S3aocF7ZCXU9fSO7U= +github.com/kyokomi/emoji/v2 v2.2.13/go.mod h1:JUcn42DTdsXJo1SWanHh4HKDEyPaR5CqkmoirZZP9qE= +github.com/mailru/easyjson v0.7.7 h1:UGYAvKxe3sBsEDzO8ZeWOSlIQfWFlxbzLZe7hwFURr0= +github.com/mailru/easyjson v0.7.7/go.mod h1:xzfreul335JAWq5oZzymOObrkdz5UnU4kGfJJLY9Nlc= +github.com/makeworld-the-better-one/dither/v2 v2.4.0 h1:Az/dYXiTcwcRSe59Hzw4RI1rSnAZns+1msaCXetrMFE= +github.com/makeworld-the-better-one/dither/v2 v2.4.0/go.mod h1:VBtN8DXO7SNtyGmLiGA7IsFeKrBkQPze1/iAeM95arc= +github.com/marekm4/color-extractor v1.2.1 h1:3Zb2tQsn6bITZ8MBVhc33Qn1k5/SEuZ18mrXGUqIwn0= +github.com/marekm4/color-extractor v1.2.1/go.mod h1:90VjmiHI6M8ez9eYUaXLdcKnS+BAOp7w+NpwBdkJmpA= github.com/mattn/go-colorable v0.1.14 h1:9A9LHSqF/7dyVVX6g0U9cwm9pG3kP9gSzcuIPHPsaIE= github.com/mattn/go-colorable v0.1.14/go.mod h1:6LmQG8QLFO4G5z1gPvYEzlUgJ2wF+stgPZH1UqBm1s8= github.com/mattn/go-isatty v0.0.20 h1:xfD0iDuEKnDkl03q4limB+vH+GxLEtL/jb4xVJSWWEY= github.com/mattn/go-isatty v0.0.20/go.mod h1:W+V8PltTTMOvKvAeJH7IuucS94S2C6jfK/D7dTCTo3Y= +github.com/mattn/go-runewidth v0.0.16 h1:E5ScNMtiwvlvB5paMFdw9p4kSQzbXFikJ5SQO6TULQc= +github.com/mattn/go-runewidth v0.0.16/go.mod h1:Jdepj2loyihRzMpdS35Xk/zdY8IAYHsh153qUoGf23w= +github.com/mitchellh/mapstructure v1.5.1-0.20231216201459-8508981c8b6c h1:cqn374mizHuIWj+OSJCajGr/phAmuMug9qIX3l9CflE= +github.com/mitchellh/mapstructure v1.5.1-0.20231216201459-8508981c8b6c/go.mod h1:bFUtVrKA4DC2yAKiSyO/QUcy7e+RRV2QTWOzhPopBRo= +github.com/mohae/deepcopy v0.0.0-20170929034955-c48cc78d4826 h1:RWengNIwukTxcDr9M+97sNutRR1RKhG96O6jWumTTnw= +github.com/mohae/deepcopy v0.0.0-20170929034955-c48cc78d4826/go.mod h1:TaXosZuwdSHYgviHp1DAtfrULt5eUgsSMsZf+YrPgl8= +github.com/muesli/smartcrop v0.3.0 h1:JTlSkmxWg/oQ1TcLDoypuirdE8Y/jzNirQeLkxpA6Oc= +github.com/muesli/smartcrop v0.3.0/go.mod h1:i2fCI/UorTfgEpPPLWiFBv4pye+YAG78RwcQLUkocpI= +github.com/niklasfasching/go-org v1.7.0 h1:vyMdcMWWTe/XmANk19F4k8XGBYg0GQ/gJGMimOjGMek= +github.com/niklasfasching/go-org v1.7.0/go.mod h1:WuVm4d45oePiE0eX25GqTDQIt/qPW1T9DGkRscqLW5o= +github.com/oasdiff/yaml v0.0.0-20250309154309-f31be36b4037 h1:G7ERwszslrBzRxj//JalHPu/3yz+De2J+4aLtSRlHiY= +github.com/oasdiff/yaml v0.0.0-20250309154309-f31be36b4037/go.mod h1:2bpvgLBZEtENV5scfDFEtB/5+1M4hkQhDQrccEJ/qGw= +github.com/oasdiff/yaml3 v0.0.0-20250309153720-d2182401db90 h1:bQx3WeLcUWy+RletIKwUIt4x3t8n2SxavmoclizMb8c= +github.com/oasdiff/yaml3 v0.0.0-20250309153720-d2182401db90/go.mod h1:y5+oSEHCPT/DGrS++Wc/479ERge0zTFxaF8PbGKcg2o= +github.com/olekukonko/tablewriter v0.0.5 h1:P2Ga83D34wi1o9J6Wh1mRuqd4mF/x/lgBS7N7AbDhec= +github.com/olekukonko/tablewriter v0.0.5/go.mod h1:hPp6KlRPjbx+hW8ykQs1w3UBbZlj6HuIJcUGPhkA7kY= +github.com/pbnjay/memory v0.0.0-20210728143218-7b4eea64cf58 h1:onHthvaw9LFnH4t2DcNVpwGmV9E1BkGknEliJkfwQj0= +github.com/pbnjay/memory v0.0.0-20210728143218-7b4eea64cf58/go.mod h1:DXv8WO4yhMYhSNPKjeNKa5WY9YCIEBRbNzFFPJbWO6Y= github.com/pelletier/go-toml v1.9.5 h1:4yBQzkHv+7BHq2PQUZF3Mx0IYxG7LsP222s7Agd3ve8= github.com/pelletier/go-toml v1.9.5/go.mod h1:u1nR/EPcESfeI/szUZKdtJ0xRNbUoANCkoOuaOx1Y+c= github.com/pelletier/go-toml/v2 v2.2.4 h1:mye9XuhQ6gvn5h28+VilKrrPoQVanw5PMw/TB0t5Ec4= github.com/pelletier/go-toml/v2 v2.2.4/go.mod h1:2gIqNv+qfxSVS7cM2xJQKtLSTLUE9V8t9Stt+h56mCY= +github.com/perimeterx/marshmallow v1.1.5 h1:a2LALqQ1BlHM8PZblsDdidgv1mWi1DgC2UmX50IvK2s= +github.com/perimeterx/marshmallow v1.1.5/go.mod h1:dsXbUu8CRzfYP5a87xpp0xq9S3u0Vchtcl8we9tYaXw= +github.com/pkg/errors v0.9.1 h1:FEBLx1zS214owpjy7qsBeixbURkuhQAwrK5UwLGTwt4= +github.com/pkg/errors v0.9.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0= +github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM= +github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= +github.com/rivo/uniseg v0.4.7 h1:WUdvkW8uEhrYfLC4ZzdpI2ztxP1I582+49Oc5Mq64VQ= +github.com/rivo/uniseg v0.4.7/go.mod h1:FN3SvrM+Zdj16jyLfmOkMNblXMcoc8DfTHruCPUcx88= +github.com/rogpeppe/go-internal v1.14.1 h1:UQB4HGPB6osV0SQTLymcB4TgvyWu6ZyliaW0tI/otEQ= +github.com/rogpeppe/go-internal v1.14.1/go.mod h1:MaRKkUm5W0goXpeCfT7UZI6fk/L7L7so1lCWt35ZSgc= github.com/spf13/afero v1.14.0 h1:9tH6MapGnn/j0eb0yIXiLjERO8RB6xIVZRDCX7PtqWA= github.com/spf13/afero v1.14.0/go.mod h1:acJQ8t0ohCGuMN3O+Pv0V0hgMxNYDlvdk+VTfyZmbYo= github.com/spf13/cast v1.8.0 h1:gEN9K4b8Xws4EX0+a0reLmhq8moKn7ntRlQYgjPeCDk= github.com/spf13/cast v1.8.0/go.mod h1:ancEpBxwJDODSW/UG4rDrAqiKolqNNh2DX3mk86cAdo= +github.com/stretchr/testify v1.10.0 h1:Xv5erBjTwe/5IxqUQTdXv5kgmIvbHo3QQyRwhJsOfJA= +github.com/stretchr/testify v1.10.0/go.mod h1:r2ic/lqez/lEtzL7wO/rwa5dbSLXVDPFyf8C91i36aY= +github.com/tdewolff/minify/v2 v2.23.5 h1:/P548KcpTkIOUvNg22zN83/GiaYSOIrbqtoue4I7kYM= +github.com/tdewolff/minify/v2 v2.23.5/go.mod h1:2RI9tiIrzJU1Z5EasXEPaI1MqobRyxKHOOgrRkq5oEw= github.com/tdewolff/parse/v2 v2.8.1 h1:J5GSHru6o3jF1uLlEKVXkDxxcVx6yzOlIVIotK4w2po= github.com/tdewolff/parse/v2 v2.8.1/go.mod h1:Hwlni2tiVNKyzR1o6nUs4FOF07URA+JLBLd6dlIXYqo= +github.com/tdewolff/test v1.0.11 h1:FdLbwQVHxqG16SlkGveC0JVyrJN62COWTRyUFzfbtBE= github.com/tdewolff/test v1.0.11/go.mod h1:XPuWBzvdUzhCuxWO1ojpXsyzsA5bFoS3tO/Q3kFuTG8= +github.com/tetratelabs/wazero v1.9.0 h1:IcZ56OuxrtaEz8UYNRHBrUa9bYeX9oVY93KspZZBf/I= +github.com/tetratelabs/wazero v1.9.0/go.mod h1:TSbcXCfFP0L2FGkRPxHphadXPjo1T6W+CseNNY7EkjM= +github.com/yuin/goldmark v1.7.11 h1:ZCxLyDMtz0nT2HFfsYG8WZ47Trip2+JyLysKcMYE5bo= +github.com/yuin/goldmark v1.7.11/go.mod h1:ip/1k0VRfGynBgxOz0yCqHrbZXhcjxyuS66Brc7iBKg= +github.com/yuin/goldmark-emoji v1.0.6 h1:QWfF2FYaXwL74tfGOW5izeiZepUDroDJfWubQI9HTHs= +github.com/yuin/goldmark-emoji v1.0.6/go.mod h1:ukxJDKFpdFb5x0a5HqbdlcKtebh086iJpI31LTKmWuA= +golang.org/x/exp v0.0.0-20250531010427-b6e5de432a8b h1:QoALfVG9rhQ/M7vYDScfPdWjGL9dlsVVM5VGh7aKoAA= +golang.org/x/exp v0.0.0-20250531010427-b6e5de432a8b/go.mod h1:U6Lno4MTRCDY+Ba7aCcauB9T60gsv5s4ralQzP72ZoQ= +golang.org/x/image v0.27.0 h1:C8gA4oWU/tKkdCfYT6T2u4faJu3MeNS5O8UPWlPF61w= +golang.org/x/image v0.27.0/go.mod h1:xbdrClrAUway1MUTEZDq9mz/UpRwYAkFFNUslZtcB+g= +golang.org/x/mod v0.27.0 h1:kb+q2PyFnEADO2IEF935ehFUXlWiNjJWtRNgBLSfbxQ= +golang.org/x/mod v0.27.0/go.mod h1:rWI627Fq0DEoudcK+MBkNkCe0EetEaDSwJJkCcjpazc= +golang.org/x/net v0.45.0 h1:RLBg5JKixCy82FtLJpeNlVM0nrSqpCRYzVU1n8kj0tM= +golang.org/x/net v0.45.0/go.mod h1:ECOoLqd5U3Lhyeyo/QDCEVQ4sNgYsqvCZ722XogGieY= +golang.org/x/sync v0.17.0 h1:l60nONMj9l5drqw6jlhIELNv9I0A4OFgRsG9k2oT9Ug= +golang.org/x/sync v0.17.0/go.mod h1:9KTHXmSnoGruLpwFjVSX0lNNA75CykiMECbovNTZqGI= golang.org/x/sys v0.6.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= -golang.org/x/sys v0.33.0 h1:q3i8TbbEz+JRD9ywIRlyRAQbM0qF7hu24q3teo2hbuw= -golang.org/x/sys v0.33.0/go.mod h1:BJP2sWEmIv4KK5OTEluFJCKSidICx8ciO85XgH3Ak8k= -golang.org/x/text v0.25.0 h1:qVyWApTSYLk/drJRO5mDlNYskwQznZmkpV2c8q9zls4= -golang.org/x/text v0.25.0/go.mod h1:WEdwpYrmk1qmdHvhkSTNPm3app7v4rsT8F2UD6+VHIA= +golang.org/x/sys v0.36.0 h1:KVRy2GtZBrk1cBYA7MKu5bEZFxQk4NIDV6RLVcC8o0k= +golang.org/x/sys v0.36.0/go.mod h1:OgkHotnGiDImocRcuBABYBEXf8A9a87e/uXjp9XT3ks= +golang.org/x/text v0.29.0 h1:1neNs90w9YzJ9BocxfsQNHKuAT4pkghyXc4nhZ6sJvk= +golang.org/x/text v0.29.0/go.mod h1:7MhJOA9CD2qZyOKYazxdYMF85OwPdEr9jTtBpO7ydH4= +golang.org/x/tools v0.36.0 h1:kWS0uv/zsvHEle1LbV5LE8QujrxB3wfQyxHfhOk0Qkg= +golang.org/x/tools v0.36.0/go.mod h1:WBDiHKJK8YgLHlcQPYQzNCkUxUypCaa5ZegCVutKm+s= google.golang.org/protobuf v1.36.6 h1:z1NpPI8ku2WgiWnf+t9wTPsn6eP1L7ksHUlkfLvd9xY= google.golang.org/protobuf v1.36.6/go.mod h1:jduwjTPXsFjZGTmRluh+L6NjiWu7pchiJ2/5YcXBHnY= +gopkg.in/yaml.v2 v2.4.0 h1:D8xgwECY7CYvx+Y2n4sBz93Jn9JRvxdiyyo8CTfuKaY= +gopkg.in/yaml.v2 v2.4.0/go.mod h1:RDklbk79AGWmwhnvt/jBztapEOGDOx6ZbXqjP6csGnQ= +gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA= +gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= +rsc.io/qr v0.2.0 h1:6vBLea5/NRMVTz8V66gipeLycZMl/+UlFmk8DvqQ6WY= +rsc.io/qr v0.2.0/go.mod h1:IF+uZjkb9fqyeF/4tlBoynqmQxUoPfWEKh921coOuXs= diff --git a/.citools/src/bra/go.mod b/.citools/src/bra/go.mod index fec1ff55bbb..54658efb010 100644 --- a/.citools/src/bra/go.mod +++ b/.citools/src/bra/go.mod @@ -1,6 +1,6 @@ module bra -go 1.24.5 +go 1.25.2 tool github.com/unknwon/bra @@ -17,6 +17,6 @@ require ( github.com/unknwon/com v1.0.1 // indirect github.com/unknwon/log v0.0.0-20200308114134-929b1006e34a // indirect github.com/urfave/cli v1.22.16 // indirect - golang.org/x/sys v0.33.0 // indirect + golang.org/x/sys v0.36.0 // indirect gopkg.in/fsnotify/fsnotify.v1 v1.4.7 // indirect ) diff --git a/.citools/src/bra/go.sum b/.citools/src/bra/go.sum index 601cd7a8f3f..723e36ed099 100644 --- a/.citools/src/bra/go.sum +++ b/.citools/src/bra/go.sum @@ -56,8 +56,8 @@ golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACk golang.org/x/net v0.0.0-20190311183353-d8887717615a/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= golang.org/x/sys v0.0.0-20191020152052-9984515f0562/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.33.0 h1:q3i8TbbEz+JRD9ywIRlyRAQbM0qF7hu24q3teo2hbuw= -golang.org/x/sys v0.33.0/go.mod h1:BJP2sWEmIv4KK5OTEluFJCKSidICx8ciO85XgH3Ak8k= +golang.org/x/sys v0.36.0 h1:KVRy2GtZBrk1cBYA7MKu5bEZFxQk4NIDV6RLVcC8o0k= +golang.org/x/sys v0.36.0/go.mod h1:OgkHotnGiDImocRcuBABYBEXf8A9a87e/uXjp9XT3ks= golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= golang.org/x/tools v0.0.0-20190328211700-ab21143f2384/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs= gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= diff --git a/.citools/src/cog/go.mod b/.citools/src/cog/go.mod index 1b0f1246663..795e8c2007b 100644 --- a/.citools/src/cog/go.mod +++ b/.citools/src/cog/go.mod @@ -1,6 +1,6 @@ module cog -go 1.24.5 +go 1.25.2 tool github.com/grafana/cog/cmd/cli @@ -40,11 +40,11 @@ require ( github.com/spf13/pflag v1.0.6 // indirect github.com/ugorji/go/codec v1.2.11 // indirect github.com/yalue/merged_fs v1.3.0 // indirect - golang.org/x/mod v0.24.0 // indirect - golang.org/x/net v0.40.0 // indirect + golang.org/x/mod v0.27.0 // indirect + golang.org/x/net v0.45.0 // indirect golang.org/x/oauth2 v0.30.0 // indirect - golang.org/x/sync v0.14.0 // indirect - golang.org/x/text v0.25.0 // indirect - golang.org/x/tools v0.33.0 // indirect + golang.org/x/sync v0.17.0 // indirect + golang.org/x/text v0.29.0 // indirect + golang.org/x/tools v0.36.0 // indirect gopkg.in/yaml.v3 v3.0.1 // indirect ) diff --git a/.citools/src/cog/go.sum b/.citools/src/cog/go.sum index 513236e89f4..fc55dd4132c 100644 --- a/.citools/src/cog/go.sum +++ b/.citools/src/cog/go.sum @@ -85,20 +85,20 @@ github.com/ugorji/go/codec v1.2.11 h1:BMaWp1Bb6fHwEtbplGBGJ498wD+LKlNSl25MjdZY4d github.com/ugorji/go/codec v1.2.11/go.mod h1:UNopzCgEMSXjBc6AOMqYvWC1ktqTAfzJZUZgYf6w6lg= github.com/yalue/merged_fs v1.3.0 h1:qCeh9tMPNy/i8cwDsQTJ5bLr6IRxbs6meakNE5O+wyY= github.com/yalue/merged_fs v1.3.0/go.mod h1:WqqchfVYQyclV2tnR7wtRhBddzBvLVR83Cjw9BKQw0M= -golang.org/x/mod v0.24.0 h1:ZfthKaKaT4NrhGVZHO1/WDTwGES4De8KtWO0SIbNJMU= -golang.org/x/mod v0.24.0/go.mod h1:IXM97Txy2VM4PJ3gI61r1YEk/gAj6zAHN3AdZt6S9Ww= -golang.org/x/net v0.40.0 h1:79Xs7wF06Gbdcg4kdCCIQArK11Z1hr5POQ6+fIYHNuY= -golang.org/x/net v0.40.0/go.mod h1:y0hY0exeL2Pku80/zKK7tpntoX23cqL3Oa6njdgRtds= -golang.org/x/oauth2 v0.27.0 h1:da9Vo7/tDv5RH/7nZDz1eMGS/q1Vv1N/7FCrBhI9I3M= -golang.org/x/oauth2 v0.27.0/go.mod h1:onh5ek6nERTohokkhCD/y2cV4Do3fxFHFuAejCkRWT8= -golang.org/x/sync v0.14.0 h1:woo0S4Yywslg6hp4eUFjTVOyKt0RookbpAHG4c1HmhQ= -golang.org/x/sync v0.14.0/go.mod h1:1dzgHSNfp02xaA81J2MS99Qcpr2w7fw1gpm99rleRqA= -golang.org/x/sys v0.33.0 h1:q3i8TbbEz+JRD9ywIRlyRAQbM0qF7hu24q3teo2hbuw= -golang.org/x/sys v0.33.0/go.mod h1:BJP2sWEmIv4KK5OTEluFJCKSidICx8ciO85XgH3Ak8k= -golang.org/x/text v0.25.0 h1:qVyWApTSYLk/drJRO5mDlNYskwQznZmkpV2c8q9zls4= -golang.org/x/text v0.25.0/go.mod h1:WEdwpYrmk1qmdHvhkSTNPm3app7v4rsT8F2UD6+VHIA= -golang.org/x/tools v0.33.0 h1:4qz2S3zmRxbGIhDIAgjxvFutSvH5EfnsYrRBj0UI0bc= -golang.org/x/tools v0.33.0/go.mod h1:CIJMaWEY88juyUfo7UbgPqbC8rU2OqfAV1h2Qp0oMYI= +golang.org/x/mod v0.27.0 h1:kb+q2PyFnEADO2IEF935ehFUXlWiNjJWtRNgBLSfbxQ= +golang.org/x/mod v0.27.0/go.mod h1:rWI627Fq0DEoudcK+MBkNkCe0EetEaDSwJJkCcjpazc= +golang.org/x/net v0.45.0 h1:RLBg5JKixCy82FtLJpeNlVM0nrSqpCRYzVU1n8kj0tM= +golang.org/x/net v0.45.0/go.mod h1:ECOoLqd5U3Lhyeyo/QDCEVQ4sNgYsqvCZ722XogGieY= +golang.org/x/oauth2 v0.30.0 h1:dnDm7JmhM45NNpd8FDDeLhK6FwqbOf4MLCM9zb1BOHI= +golang.org/x/oauth2 v0.30.0/go.mod h1:B++QgG3ZKulg6sRPGD/mqlHQs5rB3Ml9erfeDY7xKlU= +golang.org/x/sync v0.17.0 h1:l60nONMj9l5drqw6jlhIELNv9I0A4OFgRsG9k2oT9Ug= +golang.org/x/sync v0.17.0/go.mod h1:9KTHXmSnoGruLpwFjVSX0lNNA75CykiMECbovNTZqGI= +golang.org/x/sys v0.36.0 h1:KVRy2GtZBrk1cBYA7MKu5bEZFxQk4NIDV6RLVcC8o0k= +golang.org/x/sys v0.36.0/go.mod h1:OgkHotnGiDImocRcuBABYBEXf8A9a87e/uXjp9XT3ks= +golang.org/x/text v0.29.0 h1:1neNs90w9YzJ9BocxfsQNHKuAT4pkghyXc4nhZ6sJvk= +golang.org/x/text v0.29.0/go.mod h1:7MhJOA9CD2qZyOKYazxdYMF85OwPdEr9jTtBpO7ydH4= +golang.org/x/tools v0.36.0 h1:kWS0uv/zsvHEle1LbV5LE8QujrxB3wfQyxHfhOk0Qkg= +golang.org/x/tools v0.36.0/go.mod h1:WBDiHKJK8YgLHlcQPYQzNCkUxUypCaa5ZegCVutKm+s= gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c h1:Hei/4ADfdWqJk1ZMxUNpqntNwaWcugrBjAiHlqqRiVk= gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c/go.mod h1:JHkPIbrfpd72SG/EVd6muEfDQjcINNoR0C8j2r3qZ4Q= diff --git a/.citools/src/cue/go.mod b/.citools/src/cue/go.mod index d2b53be05ce..dc9c63ea64f 100644 --- a/.citools/src/cue/go.mod +++ b/.citools/src/cue/go.mod @@ -1,6 +1,6 @@ module cue -go 1.24.5 +go 1.25.2 tool cuelang.org/go/cmd/cue @@ -25,13 +25,13 @@ require ( github.com/spf13/pflag v1.0.6 // indirect github.com/stretchr/testify v1.10.0 // indirect github.com/tetratelabs/wazero v1.6.0 // indirect - golang.org/x/mod v0.24.0 // indirect - golang.org/x/net v0.40.0 // indirect + golang.org/x/mod v0.27.0 // indirect + golang.org/x/net v0.45.0 // indirect golang.org/x/oauth2 v0.30.0 // indirect - golang.org/x/sync v0.14.0 // indirect - golang.org/x/sys v0.33.0 // indirect - golang.org/x/text v0.25.0 // indirect - golang.org/x/tools v0.33.0 // indirect + golang.org/x/sync v0.17.0 // indirect + golang.org/x/sys v0.36.0 // indirect + golang.org/x/text v0.29.0 // indirect + golang.org/x/tools v0.36.0 // indirect gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c // indirect gopkg.in/yaml.v3 v3.0.1 // indirect ) diff --git a/.citools/src/cue/go.sum b/.citools/src/cue/go.sum index ff7385db2e7..86c2675545b 100644 --- a/.citools/src/cue/go.sum +++ b/.citools/src/cue/go.sum @@ -53,20 +53,20 @@ github.com/stretchr/testify v1.10.0 h1:Xv5erBjTwe/5IxqUQTdXv5kgmIvbHo3QQyRwhJsOf github.com/stretchr/testify v1.10.0/go.mod h1:r2ic/lqez/lEtzL7wO/rwa5dbSLXVDPFyf8C91i36aY= github.com/tetratelabs/wazero v1.6.0 h1:z0H1iikCdP8t+q341xqepY4EWvHEw8Es7tlqiVzlP3g= github.com/tetratelabs/wazero v1.6.0/go.mod h1:0U0G41+ochRKoPKCJlh0jMg1CHkyfK8kDqiirMmKY8A= -golang.org/x/mod v0.24.0 h1:ZfthKaKaT4NrhGVZHO1/WDTwGES4De8KtWO0SIbNJMU= -golang.org/x/mod v0.24.0/go.mod h1:IXM97Txy2VM4PJ3gI61r1YEk/gAj6zAHN3AdZt6S9Ww= -golang.org/x/net v0.40.0 h1:79Xs7wF06Gbdcg4kdCCIQArK11Z1hr5POQ6+fIYHNuY= -golang.org/x/net v0.40.0/go.mod h1:y0hY0exeL2Pku80/zKK7tpntoX23cqL3Oa6njdgRtds= +golang.org/x/mod v0.27.0 h1:kb+q2PyFnEADO2IEF935ehFUXlWiNjJWtRNgBLSfbxQ= +golang.org/x/mod v0.27.0/go.mod h1:rWI627Fq0DEoudcK+MBkNkCe0EetEaDSwJJkCcjpazc= +golang.org/x/net v0.45.0 h1:RLBg5JKixCy82FtLJpeNlVM0nrSqpCRYzVU1n8kj0tM= +golang.org/x/net v0.45.0/go.mod h1:ECOoLqd5U3Lhyeyo/QDCEVQ4sNgYsqvCZ722XogGieY= golang.org/x/oauth2 v0.30.0 h1:dnDm7JmhM45NNpd8FDDeLhK6FwqbOf4MLCM9zb1BOHI= golang.org/x/oauth2 v0.30.0/go.mod h1:B++QgG3ZKulg6sRPGD/mqlHQs5rB3Ml9erfeDY7xKlU= -golang.org/x/sync v0.14.0 h1:woo0S4Yywslg6hp4eUFjTVOyKt0RookbpAHG4c1HmhQ= -golang.org/x/sync v0.14.0/go.mod h1:1dzgHSNfp02xaA81J2MS99Qcpr2w7fw1gpm99rleRqA= -golang.org/x/sys v0.33.0 h1:q3i8TbbEz+JRD9ywIRlyRAQbM0qF7hu24q3teo2hbuw= -golang.org/x/sys v0.33.0/go.mod h1:BJP2sWEmIv4KK5OTEluFJCKSidICx8ciO85XgH3Ak8k= -golang.org/x/text v0.25.0 h1:qVyWApTSYLk/drJRO5mDlNYskwQznZmkpV2c8q9zls4= -golang.org/x/text v0.25.0/go.mod h1:WEdwpYrmk1qmdHvhkSTNPm3app7v4rsT8F2UD6+VHIA= -golang.org/x/tools v0.33.0 h1:4qz2S3zmRxbGIhDIAgjxvFutSvH5EfnsYrRBj0UI0bc= -golang.org/x/tools v0.33.0/go.mod h1:CIJMaWEY88juyUfo7UbgPqbC8rU2OqfAV1h2Qp0oMYI= +golang.org/x/sync v0.17.0 h1:l60nONMj9l5drqw6jlhIELNv9I0A4OFgRsG9k2oT9Ug= +golang.org/x/sync v0.17.0/go.mod h1:9KTHXmSnoGruLpwFjVSX0lNNA75CykiMECbovNTZqGI= +golang.org/x/sys v0.36.0 h1:KVRy2GtZBrk1cBYA7MKu5bEZFxQk4NIDV6RLVcC8o0k= +golang.org/x/sys v0.36.0/go.mod h1:OgkHotnGiDImocRcuBABYBEXf8A9a87e/uXjp9XT3ks= +golang.org/x/text v0.29.0 h1:1neNs90w9YzJ9BocxfsQNHKuAT4pkghyXc4nhZ6sJvk= +golang.org/x/text v0.29.0/go.mod h1:7MhJOA9CD2qZyOKYazxdYMF85OwPdEr9jTtBpO7ydH4= +golang.org/x/tools v0.36.0 h1:kWS0uv/zsvHEle1LbV5LE8QujrxB3wfQyxHfhOk0Qkg= +golang.org/x/tools v0.36.0/go.mod h1:WBDiHKJK8YgLHlcQPYQzNCkUxUypCaa5ZegCVutKm+s= gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c h1:Hei/4ADfdWqJk1ZMxUNpqntNwaWcugrBjAiHlqqRiVk= gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c/go.mod h1:JHkPIbrfpd72SG/EVd6muEfDQjcINNoR0C8j2r3qZ4Q= diff --git a/.citools/src/golangci-lint/go.mod b/.citools/src/golangci-lint/go.mod index b5152308c30..ad72a098ba9 100644 --- a/.citools/src/golangci-lint/go.mod +++ b/.citools/src/golangci-lint/go.mod @@ -1,41 +1,48 @@ module golangci-lint -go 1.24.5 +go 1.25.2 tool github.com/golangci/golangci-lint/v2/cmd/golangci-lint require ( 4d63.com/gocheckcompilerdirectives v1.3.0 // indirect 4d63.com/gochecknoglobals v0.2.2 // indirect - github.com/4meepo/tagalign v1.4.2 // indirect - github.com/Abirdcfly/dupword v0.1.3 // indirect - github.com/Antonboom/errname v1.1.0 // indirect - github.com/Antonboom/nilnil v1.1.0 // indirect - github.com/Antonboom/testifylint v1.6.0 // indirect + codeberg.org/chavacava/garif v0.2.0 // indirect + dev.gaijin.team/go/exhaustruct/v4 v4.0.0 // indirect + dev.gaijin.team/go/golib v0.6.0 // indirect + github.com/4meepo/tagalign v1.4.3 // indirect + github.com/Abirdcfly/dupword v0.1.6 // indirect + github.com/AdminBenni/iota-mixing v1.0.0 // indirect + github.com/AlwxSin/noinlineerr v1.0.5 // indirect + github.com/Antonboom/errname v1.1.1 // indirect + github.com/Antonboom/nilnil v1.1.1 // indirect + github.com/Antonboom/testifylint v1.6.4 // indirect github.com/BurntSushi/toml v1.5.0 // indirect - github.com/Crocmagnon/fatcontext v0.7.1 // indirect - github.com/Djarvur/go-err113 v0.0.0-20210108212216-aea10b59be24 // indirect - github.com/GaijinEntertainment/go-exhaustruct/v3 v3.3.1 // indirect + github.com/Djarvur/go-err113 v0.1.1 // indirect github.com/Masterminds/semver/v3 v3.3.1 // indirect + github.com/MirrexOne/unqueryvet v1.2.1 // indirect github.com/OpenPeeDeeP/depguard/v2 v2.2.1 // indirect + github.com/alecthomas/chroma/v2 v2.20.0 // indirect github.com/alecthomas/go-check-sumtype v0.3.1 // indirect - github.com/alexkohler/nakedret/v2 v2.0.5 // indirect + github.com/alexkohler/nakedret/v2 v2.0.6 // indirect github.com/alexkohler/prealloc v1.0.0 // indirect + github.com/alfatraining/structtag v1.0.0 // indirect github.com/alingse/asasalint v0.0.11 // indirect - github.com/alingse/nilnesserr v0.1.2 // indirect - github.com/ashanbrown/forbidigo v1.6.0 // indirect - github.com/ashanbrown/makezero v1.2.0 // indirect + github.com/alingse/nilnesserr v0.2.0 // indirect + github.com/ashanbrown/forbidigo/v2 v2.1.0 // indirect + github.com/ashanbrown/makezero/v2 v2.0.1 // indirect github.com/aymanbagabas/go-osc52/v2 v2.0.1 // indirect github.com/beorn7/perks v1.0.1 // indirect github.com/bkielbasa/cyclop v1.2.3 // indirect github.com/blizzy78/varnamelen v0.8.0 // indirect - github.com/bombsimon/wsl/v4 v4.6.0 // indirect + github.com/bombsimon/wsl/v4 v4.7.0 // indirect + github.com/bombsimon/wsl/v5 v5.2.0 // indirect github.com/breml/bidichk v0.3.3 // indirect github.com/breml/errchkjson v0.4.1 // indirect - github.com/butuzov/ireturn v0.3.1 // indirect + github.com/butuzov/ireturn v0.4.0 // indirect github.com/butuzov/mirror v1.3.0 // indirect github.com/catenacyber/perfsprint v0.9.1 // indirect - github.com/ccojocar/zxcvbn-go v1.0.2 // indirect + github.com/ccojocar/zxcvbn-go v1.0.4 // indirect github.com/cespare/xxhash/v2 v2.3.0 // indirect github.com/charithe/durationcheck v0.0.10 // indirect github.com/charmbracelet/colorprofile v0.2.3-0.20250311203215-f60798e515dc // indirect @@ -43,20 +50,20 @@ require ( github.com/charmbracelet/x/ansi v0.8.0 // indirect github.com/charmbracelet/x/cellbuf v0.0.13-0.20250311204145-2c3ea96c31dd // indirect github.com/charmbracelet/x/term v0.2.1 // indirect - github.com/chavacava/garif v0.1.0 // indirect github.com/ckaznocha/intrange v0.3.1 // indirect github.com/curioswitch/go-reassign v0.3.0 // indirect - github.com/daixiang0/gci v0.13.6 // indirect + github.com/daixiang0/gci v0.13.7 // indirect github.com/dave/dst v0.27.3 // indirect github.com/davecgh/go-spew v1.1.2-0.20180830191138-d8f796af33cc // indirect github.com/denis-tingaikin/go-header v0.5.0 // indirect + github.com/dlclark/regexp2 v1.11.5 // indirect github.com/ettle/strcase v0.2.0 // indirect github.com/fatih/color v1.18.0 // indirect github.com/fatih/structtag v1.2.0 // indirect - github.com/firefart/nonamedreturns v1.0.5 // indirect + github.com/firefart/nonamedreturns v1.0.6 // indirect github.com/fsnotify/fsnotify v1.8.0 // indirect github.com/fzipp/gocyclo v0.6.0 // indirect - github.com/ghostiam/protogetter v0.3.12 // indirect + github.com/ghostiam/protogetter v0.3.16 // indirect github.com/go-critic/go-critic v0.13.0 // indirect github.com/go-toolsmith/astcast v1.1.0 // indirect github.com/go-toolsmith/astcopy v1.1.0 // indirect @@ -68,46 +75,50 @@ require ( github.com/go-viper/mapstructure/v2 v2.4.0 // indirect github.com/go-xmlfmt/xmlfmt v1.1.3 // indirect github.com/gobwas/glob v0.2.3 // indirect + github.com/godoc-lint/godoc-lint v0.10.0 // indirect github.com/gofrs/flock v0.12.1 // indirect + github.com/golangci/asciicheck v0.5.0 // indirect github.com/golangci/dupl v0.0.0-20250308024227-f665c8d69b32 // indirect - github.com/golangci/go-printf-func-name v0.1.0 // indirect + github.com/golangci/go-printf-func-name v0.1.1 // indirect github.com/golangci/gofmt v0.0.0-20250106114630-d62b90e6713d // indirect - github.com/golangci/golangci-lint/v2 v2.0.2 // indirect + github.com/golangci/golangci-lint/v2 v2.5.0 // indirect github.com/golangci/golines v0.0.0-20250217134842-442fd0091d95 // indirect - github.com/golangci/misspell v0.6.0 // indirect - github.com/golangci/plugin-module-register v0.1.1 // indirect + github.com/golangci/misspell v0.7.0 // indirect + github.com/golangci/nilerr v0.0.0-20250918000102-015671e622fe // indirect + github.com/golangci/plugin-module-register v0.1.2 // indirect github.com/golangci/revgrep v0.8.0 // indirect - github.com/golangci/unconvert v0.0.0-20240309020433-c5143eacb3ed // indirect + github.com/golangci/swaggoswag v0.0.0-20250504205917-77f2aca3143e // indirect + github.com/golangci/unconvert v0.0.0-20250410112200-a129a6e6413e // indirect github.com/google/go-cmp v0.7.0 // indirect - github.com/google/pprof v0.0.0-20250317173921-a4b03ec1a45e // indirect - github.com/gordonklaus/ineffassign v0.1.0 // indirect + github.com/gordonklaus/ineffassign v0.2.0 // indirect github.com/gostaticanalysis/analysisutil v0.7.1 // indirect github.com/gostaticanalysis/comment v1.5.0 // indirect github.com/gostaticanalysis/forcetypeassert v0.2.0 // indirect - github.com/gostaticanalysis/nilerr v0.1.1 // indirect github.com/hashicorp/go-immutable-radix/v2 v2.1.0 // indirect github.com/hashicorp/go-version v1.7.0 // indirect github.com/hashicorp/golang-lru/v2 v2.0.7 // indirect github.com/hexops/gotextdiff v1.0.3 // indirect github.com/inconshreveable/mousetrap v1.1.0 // indirect - github.com/jgautheron/goconst v1.7.1 // indirect + github.com/jgautheron/goconst v1.8.2 // indirect github.com/jingyugao/rowserrcheck v1.1.1 // indirect - github.com/jjti/go-spancheck v0.6.4 // indirect + github.com/jjti/go-spancheck v0.6.5 // indirect github.com/julz/importas v0.2.0 // indirect github.com/karamaru-alpha/copyloopvar v1.2.1 // indirect github.com/kisielk/errcheck v1.9.0 // indirect github.com/kkHAIKE/contextcheck v1.1.6 // indirect - github.com/kulti/thelper v0.6.3 // indirect - github.com/kunwardeep/paralleltest v1.0.10 // indirect + github.com/kulti/thelper v0.7.1 // indirect + github.com/kunwardeep/paralleltest v1.0.14 // indirect github.com/lasiar/canonicalheader v1.1.2 // indirect - github.com/ldez/exptostd v0.4.2 // indirect - github.com/ldez/gomoddirectives v0.6.1 // indirect - github.com/ldez/grignotin v0.9.0 // indirect - github.com/ldez/tagliatelle v0.7.1 // indirect - github.com/ldez/usetesting v0.4.2 // indirect + github.com/ldez/exptostd v0.4.4 // indirect + github.com/ldez/gomoddirectives v0.7.0 // indirect + github.com/ldez/grignotin v0.10.1 // indirect + github.com/ldez/tagliatelle v0.7.2 // indirect + github.com/ldez/usetesting v0.5.0 // indirect github.com/leonklingele/grouper v1.1.2 // indirect github.com/lucasb-eyer/go-colorful v1.2.0 // indirect github.com/macabu/inamedparam v0.2.0 // indirect + github.com/manuelarte/embeddedstructfieldcheck v0.4.0 // indirect + github.com/manuelarte/funcorder v0.5.0 // indirect github.com/maratori/testableexamples v1.0.0 // indirect github.com/maratori/testpackage v1.1.1 // indirect github.com/matoous/godox v1.1.0 // indirect @@ -115,7 +126,7 @@ require ( github.com/mattn/go-colorable v0.1.14 // indirect github.com/mattn/go-isatty v0.0.20 // indirect github.com/mattn/go-runewidth v0.0.16 // indirect - github.com/mgechev/revive v1.7.0 // indirect + github.com/mgechev/revive v1.12.0 // indirect github.com/mitchellh/go-homedir v1.1.0 // indirect github.com/moricho/tparallel v0.3.2 // indirect github.com/muesli/termenv v0.16.0 // indirect @@ -123,11 +134,10 @@ require ( github.com/nakabonne/nestif v0.3.1 // indirect github.com/nishanths/exhaustive v0.12.0 // indirect github.com/nishanths/predeclared v0.2.2 // indirect - github.com/nunnatsa/ginkgolinter v0.19.1 // indirect - github.com/olekukonko/tablewriter v0.0.5 // indirect - github.com/pelletier/go-toml/v2 v2.2.3 // indirect + github.com/nunnatsa/ginkgolinter v0.21.0 // indirect + github.com/pelletier/go-toml/v2 v2.2.4 // indirect github.com/pmezard/go-difflib v1.0.1-0.20181226105442-5d4384ee4fb2 // indirect - github.com/polyfloyd/go-errorlint v1.7.1 // indirect + github.com/polyfloyd/go-errorlint v1.8.0 // indirect github.com/prometheus/client_golang v1.22.0 // indirect github.com/prometheus/client_model v0.6.1 // indirect github.com/prometheus/common v0.63.0 // indirect @@ -144,58 +154,59 @@ require ( github.com/ryanrolds/sqlclosecheck v0.5.1 // indirect github.com/sagikazarmark/locafero v0.7.0 // indirect github.com/sanposhiho/wastedassign/v2 v2.1.0 // indirect - github.com/santhosh-tekuri/jsonschema/v6 v6.0.1 // indirect + github.com/santhosh-tekuri/jsonschema/v6 v6.0.2 // indirect github.com/sashamelentyev/interfacebloat v1.1.0 // indirect - github.com/sashamelentyev/usestdlibvars v1.28.0 // indirect - github.com/securego/gosec/v2 v2.22.2 // indirect + github.com/sashamelentyev/usestdlibvars v1.29.0 // indirect + github.com/securego/gosec/v2 v2.22.8 // indirect github.com/sergi/go-diff v1.3.2-0.20230802210424-5b0b94c5c0d3 // indirect github.com/sirupsen/logrus v1.9.3 // indirect github.com/sivchari/containedctx v1.0.3 // indirect - github.com/sonatard/noctx v0.1.0 // indirect + github.com/sonatard/noctx v0.4.0 // indirect github.com/sourcegraph/conc v0.3.0 // indirect github.com/sourcegraph/go-diff v0.7.0 // indirect - github.com/spf13/afero v1.12.0 // indirect + github.com/spf13/afero v1.14.0 // indirect github.com/spf13/cast v1.7.1 // indirect - github.com/spf13/cobra v1.9.1 // indirect - github.com/spf13/pflag v1.0.6 // indirect + github.com/spf13/cobra v1.10.1 // indirect + github.com/spf13/pflag v1.0.10 // indirect github.com/spf13/viper v1.20.1 // indirect github.com/ssgreg/nlreturn/v2 v2.2.1 // indirect github.com/stbenjam/no-sprintf-host-port v0.2.0 // indirect github.com/stretchr/objx v0.5.2 // indirect - github.com/stretchr/testify v1.10.0 // indirect + github.com/stretchr/testify v1.11.1 // indirect github.com/subosito/gotenv v1.6.0 // indirect - github.com/tdakkota/asciicheck v0.4.1 // indirect - github.com/tetafro/godot v1.5.0 // indirect + github.com/tetafro/godot v1.5.4 // indirect github.com/timakin/bodyclose v0.0.0-20241222091800-1db5c5ca4d67 // indirect - github.com/timonwong/loggercheck v0.10.1 // indirect - github.com/tomarrell/wrapcheck/v2 v2.10.0 // indirect + github.com/timonwong/loggercheck v0.11.0 // indirect + github.com/tomarrell/wrapcheck/v2 v2.11.0 // indirect github.com/tommy-muehle/go-mnd/v2 v2.5.1 // indirect github.com/ultraware/funlen v0.2.0 // indirect github.com/ultraware/whitespace v0.2.0 // indirect github.com/uudashr/gocognit v1.2.0 // indirect - github.com/uudashr/iface v1.3.1 // indirect + github.com/uudashr/iface v1.4.1 // indirect github.com/xen0n/gosmopolitan v1.3.0 // indirect github.com/xo/terminfo v0.0.0-20220910002029-abceb7e1c41e // indirect github.com/yagipy/maintidx v1.0.0 // indirect github.com/yeya24/promlinter v0.3.0 // indirect github.com/ykadowak/zerologlint v0.1.5 // indirect gitlab.com/bosi/decorder v0.4.2 // indirect - go-simpler.org/musttag v0.13.0 // indirect - go-simpler.org/sloglint v0.9.0 // indirect + go-simpler.org/musttag v0.14.0 // indirect + go-simpler.org/sloglint v0.11.1 // indirect + go.augendre.info/arangolint v0.2.0 // indirect + go.augendre.info/fatcontext v0.8.1 // indirect go.uber.org/automaxprocs v1.6.0 // indirect go.uber.org/multierr v1.11.0 // indirect go.uber.org/zap v1.27.0 // indirect golang.org/x/exp v0.0.0-20250305212735-054e65f0b394 // indirect - golang.org/x/exp/typeparams v0.0.0-20250210185358-939b2ce775ac // indirect - golang.org/x/mod v0.24.0 // indirect - golang.org/x/sync v0.14.0 // indirect - golang.org/x/sys v0.33.0 // indirect - golang.org/x/text v0.25.0 // indirect - golang.org/x/tools v0.33.0 // indirect + golang.org/x/exp/typeparams v0.0.0-20250911091902-df9299821621 // indirect + golang.org/x/mod v0.28.0 // indirect + golang.org/x/net v0.45.0 // indirect + golang.org/x/sync v0.17.0 // indirect + golang.org/x/sys v0.36.0 // indirect + golang.org/x/text v0.29.0 // indirect + golang.org/x/tools v0.37.0 // indirect google.golang.org/protobuf v1.36.6 // indirect - gopkg.in/yaml.v2 v2.4.0 // indirect gopkg.in/yaml.v3 v3.0.1 // indirect honnef.co/go/tools v0.6.1 // indirect - mvdan.cc/gofumpt v0.7.0 // indirect + mvdan.cc/gofumpt v0.9.1 // indirect mvdan.cc/unparam v0.0.0-20250301125049-0df0534333a4 // indirect ) diff --git a/.citools/src/golangci-lint/go.sum b/.citools/src/golangci-lint/go.sum index 37d9819bfeb..4748f406fb8 100644 --- a/.citools/src/golangci-lint/go.sum +++ b/.citools/src/golangci-lint/go.sum @@ -2,46 +2,58 @@ 4d63.com/gocheckcompilerdirectives v1.3.0/go.mod h1:ofsJ4zx2QAuIP/NO/NAh1ig6R1Fb18/GI7RVMwz7kAY= 4d63.com/gochecknoglobals v0.2.2 h1:H1vdnwnMaZdQW/N+NrkT1SZMTBmcwHe9Vq8lJcYYTtU= 4d63.com/gochecknoglobals v0.2.2/go.mod h1:lLxwTQjL5eIesRbvnzIP3jZtG140FnTdz+AlMa+ogt0= -github.com/4meepo/tagalign v1.4.2 h1:0hcLHPGMjDyM1gHG58cS73aQF8J4TdVR96TZViorO9E= -github.com/4meepo/tagalign v1.4.2/go.mod h1:+p4aMyFM+ra7nb41CnFG6aSDXqRxU/w1VQqScKqDARI= -github.com/Abirdcfly/dupword v0.1.3 h1:9Pa1NuAsZvpFPi9Pqkd93I7LIYRURj+A//dFd5tgBeE= -github.com/Abirdcfly/dupword v0.1.3/go.mod h1:8VbB2t7e10KRNdwTVoxdBaxla6avbhGzb8sCTygUMhw= -github.com/Antonboom/errname v1.1.0 h1:A+ucvdpMwlo/myWrkHEUEBWc/xuXdud23S8tmTb/oAE= -github.com/Antonboom/errname v1.1.0/go.mod h1:O1NMrzgUcVBGIfi3xlVuvX8Q/VP/73sseCaAppfjqZw= -github.com/Antonboom/nilnil v1.1.0 h1:jGxJxjgYS3VUUtOTNk8Z1icwT5ESpLH/426fjmQG+ng= -github.com/Antonboom/nilnil v1.1.0/go.mod h1:b7sAlogQjFa1wV8jUW3o4PMzDVFLbTux+xnQdvzdcIE= -github.com/Antonboom/testifylint v1.6.0 h1:6rdILVPt4+rqcvhid8w9wJNynKLUgqHNpFyM67UeXyc= -github.com/Antonboom/testifylint v1.6.0/go.mod h1:k+nEkathI2NFjKO6HvwmSrbzUcQ6FAnbZV+ZRrnXPLI= +codeberg.org/chavacava/garif v0.2.0 h1:F0tVjhYbuOCnvNcU3YSpO6b3Waw6Bimy4K0mM8y6MfY= +codeberg.org/chavacava/garif v0.2.0/go.mod h1:P2BPbVbT4QcvLZrORc2T29szK3xEOlnl0GiPTJmEqBQ= +dev.gaijin.team/go/exhaustruct/v4 v4.0.0 h1:873r7aNneqoBB3IaFIzhvt2RFYTuHgmMjoKfwODoI1Y= +dev.gaijin.team/go/exhaustruct/v4 v4.0.0/go.mod h1:aZ/k2o4Y05aMJtiux15x8iXaumE88YdiB0Ai4fXOzPI= +dev.gaijin.team/go/golib v0.6.0 h1:v6nnznFTs4bppib/NyU1PQxobwDHwCXXl15P7DV5Zgo= +dev.gaijin.team/go/golib v0.6.0/go.mod h1:uY1mShx8Z/aNHWDyAkZTkX+uCi5PdX7KsG1eDQa2AVE= +github.com/4meepo/tagalign v1.4.3 h1:Bnu7jGWwbfpAie2vyl63Zup5KuRv21olsPIha53BJr8= +github.com/4meepo/tagalign v1.4.3/go.mod h1:00WwRjiuSbrRJnSVeGWPLp2epS5Q/l4UEy0apLLS37c= +github.com/Abirdcfly/dupword v0.1.6 h1:qeL6u0442RPRe3mcaLcbaCi2/Y/hOcdtw6DE9odjz9c= +github.com/Abirdcfly/dupword v0.1.6/go.mod h1:s+BFMuL/I4YSiFv29snqyjwzDp4b65W2Kvy+PKzZ6cw= +github.com/AdminBenni/iota-mixing v1.0.0 h1:Os6lpjG2dp/AE5fYBPAA1zfa2qMdCAWwPMCgpwKq7wo= +github.com/AdminBenni/iota-mixing v1.0.0/go.mod h1:i4+tpAaB+qMVIV9OK3m4/DAynOd5bQFaOu+2AhtBCNY= +github.com/AlwxSin/noinlineerr v1.0.5 h1:RUjt63wk1AYWTXtVXbSqemlbVTb23JOSRiNsshj7TbY= +github.com/AlwxSin/noinlineerr v1.0.5/go.mod h1:+QgkkoYrMH7RHvcdxdlI7vYYEdgeoFOVjU9sUhw/rQc= +github.com/Antonboom/errname v1.1.1 h1:bllB7mlIbTVzO9jmSWVWLjxTEbGBVQ1Ff/ClQgtPw9Q= +github.com/Antonboom/errname v1.1.1/go.mod h1:gjhe24xoxXp0ScLtHzjiXp0Exi1RFLKJb0bVBtWKCWQ= +github.com/Antonboom/nilnil v1.1.1 h1:9Mdr6BYd8WHCDngQnNVV0b554xyisFioEKi30sksufQ= +github.com/Antonboom/nilnil v1.1.1/go.mod h1:yCyAmSw3doopbOWhJlVci+HuyNRuHJKIv6V2oYQa8II= +github.com/Antonboom/testifylint v1.6.4 h1:gs9fUEy+egzxkEbq9P4cpcMB6/G0DYdMeiFS87UiqmQ= +github.com/Antonboom/testifylint v1.6.4/go.mod h1:YO33FROXX2OoUfwjz8g+gUxQXio5i9qpVy7nXGbxDD4= github.com/BurntSushi/toml v1.5.0 h1:W5quZX/G/csjUnuI8SUYlsHs9M38FC7znL0lIO+DvMg= github.com/BurntSushi/toml v1.5.0/go.mod h1:ukJfTF/6rtPPRCnwkur4qwRxa8vTRFBF0uk2lLoLwho= -github.com/Crocmagnon/fatcontext v0.7.1 h1:SC/VIbRRZQeQWj/TcQBS6JmrXcfA+BU4OGSVUt54PjM= -github.com/Crocmagnon/fatcontext v0.7.1/go.mod h1:1wMvv3NXEBJucFGfwOJBxSVWcoIO6emV215SMkW9MFU= -github.com/Djarvur/go-err113 v0.0.0-20210108212216-aea10b59be24 h1:sHglBQTwgx+rWPdisA5ynNEsoARbiCBOyGcJM4/OzsM= -github.com/Djarvur/go-err113 v0.0.0-20210108212216-aea10b59be24/go.mod h1:4UJr5HIiMZrwgkSPdsjy2uOQExX/WEILpIrO9UPGuXs= -github.com/GaijinEntertainment/go-exhaustruct/v3 v3.3.1 h1:Sz1JIXEcSfhz7fUi7xHnhpIE0thVASYjvosApmHuD2k= -github.com/GaijinEntertainment/go-exhaustruct/v3 v3.3.1/go.mod h1:n/LSCXNuIYqVfBlVXyHfMQkZDdp1/mmxfSjADd3z1Zg= +github.com/Djarvur/go-err113 v0.1.1 h1:eHfopDqXRwAi+YmCUas75ZE0+hoBHJ2GQNLYRSxao4g= +github.com/Djarvur/go-err113 v0.1.1/go.mod h1:IaWJdYFLg76t2ihfflPZnM1LIQszWOsFDh2hhhAVF6k= github.com/Masterminds/semver/v3 v3.3.1 h1:QtNSWtVZ3nBfk8mAOu/B6v7FMJ+NHTIgUPi7rj+4nv4= github.com/Masterminds/semver/v3 v3.3.1/go.mod h1:4V+yj/TJE1HU9XfppCwVMZq3I84lprf4nC11bSS5beM= +github.com/MirrexOne/unqueryvet v1.2.1 h1:M+zdXMq84g+E1YOLa7g7ExN3dWfZQrdDSTCM7gC+m/A= +github.com/MirrexOne/unqueryvet v1.2.1/go.mod h1:IWwCwMQlSWjAIteW0t+28Q5vouyktfujzYznSIWiuOg= github.com/OpenPeeDeeP/depguard/v2 v2.2.1 h1:vckeWVESWp6Qog7UZSARNqfu/cZqvki8zsuj3piCMx4= github.com/OpenPeeDeeP/depguard/v2 v2.2.1/go.mod h1:q4DKzC4UcVaAvcfd41CZh0PWpGgzrVxUYBlgKNGquUo= github.com/alecthomas/assert/v2 v2.11.0 h1:2Q9r3ki8+JYXvGsDyBXwH3LcJ+WK5D0gc5E8vS6K3D0= github.com/alecthomas/assert/v2 v2.11.0/go.mod h1:Bze95FyfUr7x34QZrjL+XP+0qgp/zg8yS+TtBj1WA3k= +github.com/alecthomas/chroma/v2 v2.20.0 h1:sfIHpxPyR07/Oylvmcai3X/exDlE8+FA820NTz+9sGw= +github.com/alecthomas/chroma/v2 v2.20.0/go.mod h1:e7tViK0xh/Nf4BYHl00ycY6rV7b8iXBksI9E359yNmA= github.com/alecthomas/go-check-sumtype v0.3.1 h1:u9aUvbGINJxLVXiFvHUlPEaD7VDULsrxJb4Aq31NLkU= github.com/alecthomas/go-check-sumtype v0.3.1/go.mod h1:A8TSiN3UPRw3laIgWEUOHHLPa6/r9MtoigdlP5h3K/E= -github.com/alecthomas/repr v0.4.0 h1:GhI2A8MACjfegCPVq9f1FLvIBS+DrQ2KQBFZP1iFzXc= -github.com/alecthomas/repr v0.4.0/go.mod h1:Fr0507jx4eOXV7AlPV6AVZLYrLIuIeSOWtW57eE/O/4= -github.com/alexkohler/nakedret/v2 v2.0.5 h1:fP5qLgtwbx9EJE8dGEERT02YwS8En4r9nnZ71RK+EVU= -github.com/alexkohler/nakedret/v2 v2.0.5/go.mod h1:bF5i0zF2Wo2o4X4USt9ntUWve6JbFv02Ff4vlkmS/VU= +github.com/alecthomas/repr v0.5.1 h1:E3G4t2QbHTSNpPKBgMTln5KLkZHLOcU7r37J4pXBuIg= +github.com/alecthomas/repr v0.5.1/go.mod h1:Fr0507jx4eOXV7AlPV6AVZLYrLIuIeSOWtW57eE/O/4= +github.com/alexkohler/nakedret/v2 v2.0.6 h1:ME3Qef1/KIKr3kWX3nti3hhgNxw6aqN5pZmQiFSsuzQ= +github.com/alexkohler/nakedret/v2 v2.0.6/go.mod h1:l3RKju/IzOMQHmsEvXwkqMDzHHvurNQfAgE1eVmT40Q= github.com/alexkohler/prealloc v1.0.0 h1:Hbq0/3fJPQhNkN0dR95AVrr6R7tou91y0uHG5pOcUuw= github.com/alexkohler/prealloc v1.0.0/go.mod h1:VetnK3dIgFBBKmg0YnD9F9x6Icjd+9cvfHR56wJVlKE= +github.com/alfatraining/structtag v1.0.0 h1:2qmcUqNcCoyVJ0up879K614L9PazjBSFruTB0GOFjCc= +github.com/alfatraining/structtag v1.0.0/go.mod h1:p3Xi5SwzTi+Ryj64DqjLWz7XurHxbGsq6y3ubePJPus= github.com/alingse/asasalint v0.0.11 h1:SFwnQXJ49Kx/1GghOFz1XGqHYKp21Kq1nHad/0WQRnw= github.com/alingse/asasalint v0.0.11/go.mod h1:nCaoMhw7a9kSJObvQyVzNTPBDbNpdocqrSP7t/cW5+I= -github.com/alingse/nilnesserr v0.1.2 h1:Yf8Iwm3z2hUUrP4muWfW83DF4nE3r1xZ26fGWUKCZlo= -github.com/alingse/nilnesserr v0.1.2/go.mod h1:1xJPrXonEtX7wyTq8Dytns5P2hNzoWymVUIaKm4HNFg= -github.com/ashanbrown/forbidigo v1.6.0 h1:D3aewfM37Yb3pxHujIPSpTf6oQk9sc9WZi8gerOIVIY= -github.com/ashanbrown/forbidigo v1.6.0/go.mod h1:Y8j9jy9ZYAEHXdu723cUlraTqbzjKF1MUyfOKL+AjcU= -github.com/ashanbrown/makezero v1.2.0 h1:/2Lp1bypdmK9wDIq7uWBlDF1iMUpIIS4A+pF6C9IEUU= -github.com/ashanbrown/makezero v1.2.0/go.mod h1:dxlPhHbDMC6N6xICzFBSK+4njQDdK8euNO0qjQMtGY4= +github.com/alingse/nilnesserr v0.2.0 h1:raLem5KG7EFVb4UIDAXgrv3N2JIaffeKNtcEXkEWd/w= +github.com/alingse/nilnesserr v0.2.0/go.mod h1:1xJPrXonEtX7wyTq8Dytns5P2hNzoWymVUIaKm4HNFg= +github.com/ashanbrown/forbidigo/v2 v2.1.0 h1:NAxZrWqNUQiDz19FKScQ/xvwzmij6BiOw3S0+QUQ+Hs= +github.com/ashanbrown/forbidigo/v2 v2.1.0/go.mod h1:0zZfdNAuZIL7rSComLGthgc/9/n2FqspBOH90xlCHdA= +github.com/ashanbrown/makezero/v2 v2.0.1 h1:r8GtKetWOgoJ4sLyUx97UTwyt2dO7WkGFHizn/Lo8TY= +github.com/ashanbrown/makezero/v2 v2.0.1/go.mod h1:kKU4IMxmYW1M4fiEHMb2vc5SFoPzXvgbMR9gIp5pjSw= github.com/aymanbagabas/go-osc52/v2 v2.0.1 h1:HwpRHbFMcZLEVr42D4p7XBqjyuxQH5SMiErDT4WkJ2k= github.com/aymanbagabas/go-osc52/v2 v2.0.1/go.mod h1:uYgXzlJ7ZpABp8OJ+exZzJJhRNQ2ASbcXHWsFqH8hp8= github.com/beorn7/perks v1.0.1 h1:VlbKKnNfV8bJzeqoa4cOKqO6bYr3WgKZxO8Z16+hsOM= @@ -50,20 +62,22 @@ github.com/bkielbasa/cyclop v1.2.3 h1:faIVMIGDIANuGPWH031CZJTi2ymOQBULs9H21HSMa5 github.com/bkielbasa/cyclop v1.2.3/go.mod h1:kHTwA9Q0uZqOADdupvcFJQtp/ksSnytRMe8ztxG8Fuo= github.com/blizzy78/varnamelen v0.8.0 h1:oqSblyuQvFsW1hbBHh1zfwrKe3kcSj0rnXkKzsQ089M= github.com/blizzy78/varnamelen v0.8.0/go.mod h1:V9TzQZ4fLJ1DSrjVDfl89H7aMnTvKkApdHeyESmyR7k= -github.com/bombsimon/wsl/v4 v4.6.0 h1:ew2R/N42su553DKTYqt3HSxaQN+uHQPv4xZ2MBmwaW4= -github.com/bombsimon/wsl/v4 v4.6.0/go.mod h1:uV/+6BkffuzSAVYD+yGyld1AChO7/EuLrCF/8xTiapg= +github.com/bombsimon/wsl/v4 v4.7.0 h1:1Ilm9JBPRczjyUs6hvOPKvd7VL1Q++PL8M0SXBDf+jQ= +github.com/bombsimon/wsl/v4 v4.7.0/go.mod h1:uV/+6BkffuzSAVYD+yGyld1AChO7/EuLrCF/8xTiapg= +github.com/bombsimon/wsl/v5 v5.2.0 h1:PyCCwd3Q7abGs3e34IW4jLYlBS+FbsU6iK+Tb3NnDp4= +github.com/bombsimon/wsl/v5 v5.2.0/go.mod h1:Gp8lD04z27wm3FANIUPZycXp+8huVsn0oxc+n4qfV9I= github.com/breml/bidichk v0.3.3 h1:WSM67ztRusf1sMoqH6/c4OBCUlRVTKq+CbSeo0R17sE= github.com/breml/bidichk v0.3.3/go.mod h1:ISbsut8OnjB367j5NseXEGGgO/th206dVa427kR8YTE= github.com/breml/errchkjson v0.4.1 h1:keFSS8D7A2T0haP9kzZTi7o26r7kE3vymjZNeNDRDwg= github.com/breml/errchkjson v0.4.1/go.mod h1:a23OvR6Qvcl7DG/Z4o0el6BRAjKnaReoPQFciAl9U3s= -github.com/butuzov/ireturn v0.3.1 h1:mFgbEI6m+9W8oP/oDdfA34dLisRFCj2G6o/yiI1yZrY= -github.com/butuzov/ireturn v0.3.1/go.mod h1:ZfRp+E7eJLC0NQmk1Nrm1LOrn/gQlOykv+cVPdiXH5M= +github.com/butuzov/ireturn v0.4.0 h1:+s76bF/PfeKEdbG8b54aCocxXmi0wvYdOVsWxVO7n8E= +github.com/butuzov/ireturn v0.4.0/go.mod h1:ghI0FrCmap8pDWZwfPisFD1vEc56VKH4NpQUxDHta70= github.com/butuzov/mirror v1.3.0 h1:HdWCXzmwlQHdVhwvsfBb2Au0r3HyINry3bDWLYXiKoc= github.com/butuzov/mirror v1.3.0/go.mod h1:AEij0Z8YMALaq4yQj9CPPVYOyJQyiexpQEQgihajRfI= github.com/catenacyber/perfsprint v0.9.1 h1:5LlTp4RwTooQjJCvGEFV6XksZvWE7wCOUvjD2z0vls0= github.com/catenacyber/perfsprint v0.9.1/go.mod h1:q//VWC2fWbcdSLEY1R3l8n0zQCDPdE4IjZwyY1HMunM= -github.com/ccojocar/zxcvbn-go v1.0.2 h1:na/czXU8RrhXO4EZme6eQJLR4PzcGsahsBOAwU6I3Vg= -github.com/ccojocar/zxcvbn-go v1.0.2/go.mod h1:g1qkXtUSvHP8lhHp5GrSmTz6uWALGRMQdw6Qnz/hi60= +github.com/ccojocar/zxcvbn-go v1.0.4 h1:FWnCIRMXPj43ukfX000kvBZvV6raSxakYr1nzyNrUcc= +github.com/ccojocar/zxcvbn-go v1.0.4/go.mod h1:3GxGX+rHmueTUMvm5ium7irpyjmm7ikxYFOSJB21Das= github.com/cespare/xxhash/v2 v2.3.0 h1:UL815xU9SqsFlibzuggzjXhog7bL6oX9BbNZnL2UFvs= github.com/cespare/xxhash/v2 v2.3.0/go.mod h1:VGX0DQ3Q6kWi7AoAeZDth3/j3BFtOZR5XLFGgcrjCOs= github.com/charithe/durationcheck v0.0.10 h1:wgw73BiocdBDQPik+zcEoBG/ob8uyBHf2iyoHGPf5w4= @@ -78,15 +92,13 @@ github.com/charmbracelet/x/cellbuf v0.0.13-0.20250311204145-2c3ea96c31dd h1:vy0G github.com/charmbracelet/x/cellbuf v0.0.13-0.20250311204145-2c3ea96c31dd/go.mod h1:xe0nKWGd3eJgtqZRaN9RjMtK7xUYchjzPr7q6kcvCCs= github.com/charmbracelet/x/term v0.2.1 h1:AQeHeLZ1OqSXhrAWpYUtZyX1T3zVxfpZuEQMIQaGIAQ= github.com/charmbracelet/x/term v0.2.1/go.mod h1:oQ4enTYFV7QN4m0i9mzHrViD7TQKvNEEkHUMCmsxdUg= -github.com/chavacava/garif v0.1.0 h1:2JHa3hbYf5D9dsgseMKAmc/MZ109otzgNFk5s87H9Pc= -github.com/chavacava/garif v0.1.0/go.mod h1:XMyYCkEL58DF0oyW4qDjjnPWONs2HBqYKI+UIPD+Gww= github.com/ckaznocha/intrange v0.3.1 h1:j1onQyXvHUsPWujDH6WIjhyH26gkRt/txNlV7LspvJs= github.com/ckaznocha/intrange v0.3.1/go.mod h1:QVepyz1AkUoFQkpEqksSYpNpUo3c5W7nWh/s6SHIJJk= github.com/cpuguy83/go-md2man/v2 v2.0.6/go.mod h1:oOW0eioCTA6cOiMLiUPZOpcVxMig6NIQQ7OS05n1F4g= github.com/curioswitch/go-reassign v0.3.0 h1:dh3kpQHuADL3cobV/sSGETA8DOv457dwl+fbBAhrQPs= github.com/curioswitch/go-reassign v0.3.0/go.mod h1:nApPCCTtqLJN/s8HfItCcKV0jIPwluBOvZP+dsJGA88= -github.com/daixiang0/gci v0.13.6 h1:RKuEOSkGpSadkGbvZ6hJ4ddItT3cVZ9Vn9Rybk6xjl8= -github.com/daixiang0/gci v0.13.6/go.mod h1:12etP2OniiIdP4q+kjUGrC/rUagga7ODbqsom5Eo5Yk= +github.com/daixiang0/gci v0.13.7 h1:+0bG5eK9vlI08J+J/NWGbWPTNiXPG4WhNLJOkSxWITQ= +github.com/daixiang0/gci v0.13.7/go.mod h1:812WVN6JLFY9S6Tv76twqmNqevN0pa3SX3nih0brVzQ= github.com/dave/dst v0.27.3 h1:P1HPoMza3cMEquVf9kKy8yXsFirry4zEnWOdYPOoIzY= github.com/dave/dst v0.27.3/go.mod h1:jHh6EOibnHgcUW3WjKHisiooEkYwqpHLBSX1iOBhEyc= github.com/dave/jennifer v1.7.1 h1:B4jJJDHelWcDhlRQxWeo0Npa/pYKBLrirAQoTN45txo= @@ -97,28 +109,28 @@ github.com/davecgh/go-spew v1.1.2-0.20180830191138-d8f796af33cc h1:U9qPSI2PIWSS1 github.com/davecgh/go-spew v1.1.2-0.20180830191138-d8f796af33cc/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= github.com/denis-tingaikin/go-header v0.5.0 h1:SRdnP5ZKvcO9KKRP1KJrhFR3RrlGuD+42t4429eC9k8= github.com/denis-tingaikin/go-header v0.5.0/go.mod h1:mMenU5bWrok6Wl2UsZjy+1okegmwQ3UgWl4V1D8gjlY= -github.com/dlclark/regexp2 v1.11.0 h1:G/nrcoOa7ZXlpoa/91N3X7mM3r8eIlMBBJZvsz/mxKI= -github.com/dlclark/regexp2 v1.11.0/go.mod h1:DHkYz0B9wPfa6wondMfaivmHpzrQ3v9q8cnmRbL6yW8= +github.com/dlclark/regexp2 v1.11.5 h1:Q/sSnsKerHeCkc/jSTNq1oCm7KiVgUMZRDUoRu0JQZQ= +github.com/dlclark/regexp2 v1.11.5/go.mod h1:DHkYz0B9wPfa6wondMfaivmHpzrQ3v9q8cnmRbL6yW8= github.com/ettle/strcase v0.2.0 h1:fGNiVF21fHXpX1niBgk0aROov1LagYsOwV/xqKDKR/Q= github.com/ettle/strcase v0.2.0/go.mod h1:DajmHElDSaX76ITe3/VHVyMin4LWSJN5Z909Wp+ED1A= github.com/fatih/color v1.18.0 h1:S8gINlzdQ840/4pfAwic/ZE0djQEH3wM94VfqLTZcOM= github.com/fatih/color v1.18.0/go.mod h1:4FelSpRwEGDpQ12mAdzqdOukCy4u8WUtOY6lkT/6HfU= github.com/fatih/structtag v1.2.0 h1:/OdNE99OxoI/PqaW/SuSK9uxxT3f/tcSZgon/ssNSx4= github.com/fatih/structtag v1.2.0/go.mod h1:mBJUNpUnHmRKrKlQQlmCrh5PuhftFbNv8Ys4/aAZl94= -github.com/firefart/nonamedreturns v1.0.5 h1:tM+Me2ZaXs8tfdDw3X6DOX++wMCOqzYUho6tUTYIdRA= -github.com/firefart/nonamedreturns v1.0.5/go.mod h1:gHJjDqhGM4WyPt639SOZs+G89Ko7QKH5R5BhnO6xJhw= +github.com/firefart/nonamedreturns v1.0.6 h1:vmiBcKV/3EqKY3ZiPxCINmpS431OcE1S47AQUwhrg8E= +github.com/firefart/nonamedreturns v1.0.6/go.mod h1:R8NisJnSIpvPWheCq0mNRXJok6D8h7fagJTF8EMEwCo= github.com/frankban/quicktest v1.14.6 h1:7Xjx+VpznH+oBnejlPUj8oUpdxnVs4f8XU8WnHkI4W8= github.com/frankban/quicktest v1.14.6/go.mod h1:4ptaffx2x8+WTWXmUCuVU6aPUX1/Mz7zb5vbUoiM6w0= github.com/fsnotify/fsnotify v1.8.0 h1:dAwr6QBTBZIkG8roQaJjGof0pp0EeF+tNV7YBP3F/8M= github.com/fsnotify/fsnotify v1.8.0/go.mod h1:8jBTzvmWwFyi3Pb8djgCCO5IBqzKJ/Jwo8TRcHyHii0= github.com/fzipp/gocyclo v0.6.0 h1:lsblElZG7d3ALtGMx9fmxeTKZaLLpU8mET09yN4BBLo= github.com/fzipp/gocyclo v0.6.0/go.mod h1:rXPyn8fnlpa0R2csP/31uerbiVBugk5whMdlyaLkLoA= -github.com/ghostiam/protogetter v0.3.12 h1:xTPjH97iKph27vXRRKV0OCke5sAMoHPbVeVstdzmCLE= -github.com/ghostiam/protogetter v0.3.12/go.mod h1:WZ0nw9pfzsgxuRsPOFQomgDVSWtDLJRfQJEhsGbmQMA= +github.com/ghostiam/protogetter v0.3.16 h1:UkrisuJBYLnZW6FcYUNBDJOqY3X22RtoYMlCsiNlFFA= +github.com/ghostiam/protogetter v0.3.16/go.mod h1:4SRRIv6PcjkIMpUkRUsP4TsUTqO/N3Fmvwivuc/sCHA= github.com/go-critic/go-critic v0.13.0 h1:kJzM7wzltQasSUXtYyTl6UaPVySO6GkaR1thFnJ6afY= github.com/go-critic/go-critic v0.13.0/go.mod h1:M/YeuJ3vOCQDnP2SU+ZhjgRzwzcBW87JqLpMJLrZDLI= -github.com/go-logr/logr v1.4.2 h1:6pFjapn8bFcIbiKo3XT4j/BhANplGihG6tvd+8rYgrY= -github.com/go-logr/logr v1.4.2/go.mod h1:9T104GzyrTigFIr8wt5mBrctHMim0Nb2HLGrmQ40KvY= +github.com/go-logr/logr v1.4.3 h1:CjnDlHq8ikf6E492q6eKboGOC0T8CDaOvkHCIg8idEI= +github.com/go-logr/logr v1.4.3/go.mod h1:9T104GzyrTigFIr8wt5mBrctHMim0Nb2HLGrmQ40KvY= github.com/go-quicktest/qt v1.101.0 h1:O1K29Txy5P2OK0dGo59b7b0LR6wKfIhttaAhHUyn7eI= github.com/go-quicktest/qt v1.101.0/go.mod h1:14Bz/f7NwaXPtdYEgzsx46kqSxVwTbzVZsDC26tQJow= github.com/go-task/slim-sprig/v3 v3.0.0 h1:sUs3vkvUymDpBKi3qH1YSqBQk9+9D/8M2mN1vB6EwHI= @@ -148,47 +160,50 @@ github.com/go-xmlfmt/xmlfmt v1.1.3 h1:t8Ey3Uy7jDSEisW2K3somuMKIpzktkWptA0iFCnRUW github.com/go-xmlfmt/xmlfmt v1.1.3/go.mod h1:aUCEOzzezBEjDBbFBoSiya/gduyIiWYRP6CnSFIV8AM= github.com/gobwas/glob v0.2.3 h1:A4xDbljILXROh+kObIiy5kIaPYD8e96x1tgBhUI5J+Y= github.com/gobwas/glob v0.2.3/go.mod h1:d3Ez4x06l9bZtSvzIay5+Yzi0fmZzPgnTbPcKjJAkT8= +github.com/godoc-lint/godoc-lint v0.10.0 h1:OcyrziBi18sQSEpib6NesVHEJ/Xcng97NunePBA48g4= +github.com/godoc-lint/godoc-lint v0.10.0/go.mod h1:KleLcHu/CGSvkjUH2RvZyoK1MBC7pDQg4NxMYLcBBsw= github.com/gofrs/flock v0.12.1 h1:MTLVXXHf8ekldpJk3AKicLij9MdwOWkZ+a/jHHZby9E= github.com/gofrs/flock v0.12.1/go.mod h1:9zxTsyu5xtJ9DK+1tFZyibEV7y3uwDxPPfbxeeHCoD0= +github.com/golangci/asciicheck v0.5.0 h1:jczN/BorERZwK8oiFBOGvlGPknhvq0bjnysTj4nUfo0= +github.com/golangci/asciicheck v0.5.0/go.mod h1:5RMNAInbNFw2krqN6ibBxN/zfRFa9S6tA1nPdM0l8qQ= github.com/golangci/dupl v0.0.0-20250308024227-f665c8d69b32 h1:WUvBfQL6EW/40l6OmeSBYQJNSif4O11+bmWEz+C7FYw= github.com/golangci/dupl v0.0.0-20250308024227-f665c8d69b32/go.mod h1:NUw9Zr2Sy7+HxzdjIULge71wI6yEg1lWQr7Evcu8K0E= -github.com/golangci/go-printf-func-name v0.1.0 h1:dVokQP+NMTO7jwO4bwsRwLWeudOVUPPyAKJuzv8pEJU= -github.com/golangci/go-printf-func-name v0.1.0/go.mod h1:wqhWFH5mUdJQhweRnldEywnR5021wTdZSNgwYceV14s= +github.com/golangci/go-printf-func-name v0.1.1 h1:hIYTFJqAGp1iwoIfsNTpoq1xZAarogrvjO9AfiW3B4U= +github.com/golangci/go-printf-func-name v0.1.1/go.mod h1:Es64MpWEZbh0UBtTAICOZiB+miW53w/K9Or/4QogJss= github.com/golangci/gofmt v0.0.0-20250106114630-d62b90e6713d h1:viFft9sS/dxoYY0aiOTsLKO2aZQAPT4nlQCsimGcSGE= github.com/golangci/gofmt v0.0.0-20250106114630-d62b90e6713d/go.mod h1:ivJ9QDg0XucIkmwhzCDsqcnxxlDStoTl89jDMIoNxKY= -github.com/golangci/golangci-lint/v2 v2.0.2 h1:dMCC8ikPiLDvHMFy3+XypSAuGDBOLzwWqqamer+bWsY= -github.com/golangci/golangci-lint/v2 v2.0.2/go.mod h1:ptNNMeGBQrbves0Qq38xvfdJg18PzxmT+7KRCOpm6i8= +github.com/golangci/golangci-lint/v2 v2.5.0 h1:BDRg4ASm4J1y/DSRY6zwJ5tr5Yy8ZqbZ79XrCeFxaQo= +github.com/golangci/golangci-lint/v2 v2.5.0/go.mod h1:IJtWJBZkLbx7AVrIUzLd8Oi3ADtwaNpWbR3wthVWHcc= github.com/golangci/golines v0.0.0-20250217134842-442fd0091d95 h1:AkK+w9FZBXlU/xUmBtSJN1+tAI4FIvy5WtnUnY8e4p8= github.com/golangci/golines v0.0.0-20250217134842-442fd0091d95/go.mod h1:k9mmcyWKSTMcPPvQUCfRWWQ9VHJ1U9Dc0R7kaXAgtnQ= -github.com/golangci/misspell v0.6.0 h1:JCle2HUTNWirNlDIAUO44hUsKhOFqGPoC4LZxlaSXDs= -github.com/golangci/misspell v0.6.0/go.mod h1:keMNyY6R9isGaSAu+4Q8NMBwMPkh15Gtc8UCVoDtAWo= -github.com/golangci/plugin-module-register v0.1.1 h1:TCmesur25LnyJkpsVrupv1Cdzo+2f7zX0H6Jkw1Ol6c= -github.com/golangci/plugin-module-register v0.1.1/go.mod h1:TTpqoB6KkwOJMV8u7+NyXMrkwwESJLOkfl9TxR1DGFc= +github.com/golangci/misspell v0.7.0 h1:4GOHr/T1lTW0hhR4tgaaV1WS/lJ+ncvYCoFKmqJsj0c= +github.com/golangci/misspell v0.7.0/go.mod h1:WZyyI2P3hxPY2UVHs3cS8YcllAeyfquQcKfdeE9AFVg= +github.com/golangci/nilerr v0.0.0-20250918000102-015671e622fe h1:F1pK9tBy41i7eesBFkSNMldwtiAaWiU+3fT/24sTnNI= +github.com/golangci/nilerr v0.0.0-20250918000102-015671e622fe/go.mod h1:CtTxAluxD2ng9aIT9bPrVoMuISFWCD+SaxtvYtdWA2k= +github.com/golangci/plugin-module-register v0.1.2 h1:e5WM6PO6NIAEcij3B053CohVp3HIYbzSuP53UAYgOpg= +github.com/golangci/plugin-module-register v0.1.2/go.mod h1:1+QGTsKBvAIvPvoY/os+G5eoqxWn70HYDm2uvUyGuVw= github.com/golangci/revgrep v0.8.0 h1:EZBctwbVd0aMeRnNUsFogoyayvKHyxlV3CdUA46FX2s= github.com/golangci/revgrep v0.8.0/go.mod h1:U4R/s9dlXZsg8uJmaR1GrloUr14D7qDl8gi2iPXJH8k= -github.com/golangci/unconvert v0.0.0-20240309020433-c5143eacb3ed h1:IURFTjxeTfNFP0hTEi1YKjB/ub8zkpaOqFFMApi2EAs= -github.com/golangci/unconvert v0.0.0-20240309020433-c5143eacb3ed/go.mod h1:XLXN8bNw4CGRPaqgl3bv/lhz7bsGPh4/xSaMTbo2vkQ= -github.com/google/go-cmp v0.5.1/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= +github.com/golangci/swaggoswag v0.0.0-20250504205917-77f2aca3143e h1:ai0EfmVYE2bRA5htgAG9r7s3tHsfjIhN98WshBTJ9jM= +github.com/golangci/swaggoswag v0.0.0-20250504205917-77f2aca3143e/go.mod h1:Vrn4B5oR9qRwM+f54koyeH3yzphlecwERs0el27Fr/s= +github.com/golangci/unconvert v0.0.0-20250410112200-a129a6e6413e h1:gD6P7NEo7Eqtt0ssnqSJNNndxe69DOQ24A5h7+i3KpM= +github.com/golangci/unconvert v0.0.0-20250410112200-a129a6e6413e/go.mod h1:h+wZwLjUTJnm/P2rwlbJdRPZXOzaT36/FwnPnY2inzc= github.com/google/go-cmp v0.5.2/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= github.com/google/go-cmp v0.5.4/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= -github.com/google/go-cmp v0.5.6/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= github.com/google/go-cmp v0.5.8/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeNGIjoY= github.com/google/go-cmp v0.7.0 h1:wk8382ETsv4JYUZwIsn6YpYiWiBsYLSJiTsyBybVuN8= github.com/google/go-cmp v0.7.0/go.mod h1:pXiqmnSA92OHEEa9HXL2W4E7lf9JzCmGVUdgjX3N/iU= -github.com/google/pprof v0.0.0-20250317173921-a4b03ec1a45e h1:ijClszYn+mADRFY17kjQEVQ1XRhq2/JR1M3sGqeJoxs= -github.com/google/pprof v0.0.0-20250317173921-a4b03ec1a45e/go.mod h1:boTsfXsheKC2y+lKOCMpSfarhxDeIzfZG1jqGcPl3cA= -github.com/gordonklaus/ineffassign v0.1.0 h1:y2Gd/9I7MdY1oEIt+n+rowjBNDcLQq3RsH5hwJd0f9s= -github.com/gordonklaus/ineffassign v0.1.0/go.mod h1:Qcp2HIAYhR7mNUVSIxZww3Guk4it82ghYcEXIAk+QT0= +github.com/google/pprof v0.0.0-20250607225305-033d6d78b36a h1://KbezygeMJZCSHH+HgUZiTeSoiuFspbMg1ge+eFj18= +github.com/google/pprof v0.0.0-20250607225305-033d6d78b36a/go.mod h1:5hDyRhoBCxViHszMt12TnOpEI4VVi+U8Gm9iphldiMA= +github.com/gordonklaus/ineffassign v0.2.0 h1:Uths4KnmwxNJNzq87fwQQDDnbNb7De00VOk9Nu0TySs= +github.com/gordonklaus/ineffassign v0.2.0/go.mod h1:TIpymnagPSexySzs7F9FnO1XFTy8IT3a59vmZp5Y9Lw= github.com/gostaticanalysis/analysisutil v0.7.1 h1:ZMCjoue3DtDWQ5WyU16YbjbQEQ3VuzwxALrpYd+HeKk= github.com/gostaticanalysis/analysisutil v0.7.1/go.mod h1:v21E3hY37WKMGSnbsw2S/ojApNWb6C1//mXO48CXbVc= -github.com/gostaticanalysis/comment v1.4.1/go.mod h1:ih6ZxzTHLdadaiSnF5WY3dxUoXfXAlTaRzuaNDlSado= github.com/gostaticanalysis/comment v1.4.2/go.mod h1:KLUTGDv6HOCotCH8h2erHKmpci2ZoR8VPu34YA2uzdM= github.com/gostaticanalysis/comment v1.5.0 h1:X82FLl+TswsUMpMh17srGRuKaaXprTaytmEpgnKIDu8= github.com/gostaticanalysis/comment v1.5.0/go.mod h1:V6eb3gpCv9GNVqb6amXzEUX3jXLVK/AdA+IrAMSqvEc= github.com/gostaticanalysis/forcetypeassert v0.2.0 h1:uSnWrrUEYDr86OCxWa4/Tp2jeYDlogZiZHzGkWFefTk= github.com/gostaticanalysis/forcetypeassert v0.2.0/go.mod h1:M5iPavzE9pPqWyeiVXSFghQjljW1+l/Uke3PXHS6ILY= -github.com/gostaticanalysis/nilerr v0.1.1 h1:ThE+hJP0fEp4zWLkWHWcRyI2Od0p7DlgYG3Uqrmrcpk= -github.com/gostaticanalysis/nilerr v0.1.1/go.mod h1:wZYb6YI5YAxxq0i1+VJbY0s2YONW0HU0GPE3+5PWN4A= github.com/gostaticanalysis/testutil v0.3.1-0.20210208050101-bfb5c8eec0e4/go.mod h1:D+FIZ+7OahH3ePw/izIEeH5I06eKs1IKI4Xr64/Am3M= github.com/gostaticanalysis/testutil v0.5.0 h1:Dq4wT1DdTwTGCQQv3rl3IvD5Ld0E6HiY+3Zh0sUGqw8= github.com/gostaticanalysis/testutil v0.5.0/go.mod h1:OLQSbuM6zw2EvCcXTz1lVq5unyoNft372msDY0nY5Hs= @@ -205,12 +220,12 @@ github.com/hexops/gotextdiff v1.0.3 h1:gitA9+qJrrTCsiCl7+kh75nPqQt1cx4ZkudSTLoUq github.com/hexops/gotextdiff v1.0.3/go.mod h1:pSWU5MAI3yDq+fZBTazCSJysOMbxWL1BSow5/V2vxeg= github.com/inconshreveable/mousetrap v1.1.0 h1:wN+x4NVGpMsO7ErUn/mUI3vEoE6Jt13X2s0bqwp9tc8= github.com/inconshreveable/mousetrap v1.1.0/go.mod h1:vpF70FUmC8bwa3OWnCshd2FqLfsEA9PFc4w1p2J65bw= -github.com/jgautheron/goconst v1.7.1 h1:VpdAG7Ca7yvvJk5n8dMwQhfEZJh95kl/Hl9S1OI5Jkk= -github.com/jgautheron/goconst v1.7.1/go.mod h1:aAosetZ5zaeC/2EfMeRswtxUFBpe2Hr7HzkgX4fanO4= +github.com/jgautheron/goconst v1.8.2 h1:y0XF7X8CikZ93fSNT6WBTb/NElBu9IjaY7CCYQrCMX4= +github.com/jgautheron/goconst v1.8.2/go.mod h1:A0oxgBCHy55NQn6sYpO7UdnA9p+h7cPtoOZUmvNIako= github.com/jingyugao/rowserrcheck v1.1.1 h1:zibz55j/MJtLsjP1OF4bSdgXxwL1b+Vn7Tjzq7gFzUs= github.com/jingyugao/rowserrcheck v1.1.1/go.mod h1:4yvlZSDb3IyDTUZJUmpZfm2Hwok+Dtp+nu2qOq+er9c= -github.com/jjti/go-spancheck v0.6.4 h1:Tl7gQpYf4/TMU7AT84MN83/6PutY21Nb9fuQjFTpRRc= -github.com/jjti/go-spancheck v0.6.4/go.mod h1:yAEYdKJ2lRkDA8g7X+oKUHXOWVAXSBJRv04OhF+QUjk= +github.com/jjti/go-spancheck v0.6.5 h1:lmi7pKxa37oKYIMScialXUK6hP3iY5F1gu+mLBPgYB8= +github.com/jjti/go-spancheck v0.6.5/go.mod h1:aEogkeatBrbYsyW6y5TgDfihCulDYciL1B7rG2vSsrU= github.com/julz/importas v0.2.0 h1:y+MJN/UdL63QbFJHws9BVC5RpA2iq0kpjrFajTGivjQ= github.com/julz/importas v0.2.0/go.mod h1:pThlt589EnCYtMnmhmRYY/qn9lCf/frPOK+WMx3xiJY= github.com/karamaru-alpha/copyloopvar v1.2.1 h1:wmZaZYIjnJ0b5UoKDjUHrikcV0zuPyyxI4SVplLd2CI= @@ -226,28 +241,32 @@ github.com/kr/pty v1.1.1/go.mod h1:pFQYn66WHrOpPYNljwOMqo10TkYh1fy3cYio2l3bCsQ= github.com/kr/text v0.1.0/go.mod h1:4Jbv+DJW3UT/LiOwJeYQe1efqtUx/iVham/4vfdArNI= github.com/kr/text v0.2.0 h1:5Nx0Ya0ZqY2ygV366QzturHI13Jq95ApcVaJBhpS+AY= github.com/kr/text v0.2.0/go.mod h1:eLer722TekiGuMkidMxC/pM04lWEeraHUUmBw8l2grE= -github.com/kulti/thelper v0.6.3 h1:ElhKf+AlItIu+xGnI990no4cE2+XaSu1ULymV2Yulxs= -github.com/kulti/thelper v0.6.3/go.mod h1:DsqKShOvP40epevkFrvIwkCMNYxMeTNjdWL4dqWHZ6I= -github.com/kunwardeep/paralleltest v1.0.10 h1:wrodoaKYzS2mdNVnc4/w31YaXFtsc21PCTdvWJ/lDDs= -github.com/kunwardeep/paralleltest v1.0.10/go.mod h1:2C7s65hONVqY7Q5Efj5aLzRCNLjw2h4eMc9EcypGjcY= +github.com/kulti/thelper v0.7.1 h1:fI8QITAoFVLx+y+vSyuLBP+rcVIB8jKooNSCT2EiI98= +github.com/kulti/thelper v0.7.1/go.mod h1:NsMjfQEy6sd+9Kfw8kCP61W1I0nerGSYSFnGaxQkcbs= +github.com/kunwardeep/paralleltest v1.0.14 h1:wAkMoMeGX/kGfhQBPODT/BL8XhK23ol/nuQ3SwFaUw8= +github.com/kunwardeep/paralleltest v1.0.14/go.mod h1:di4moFqtfz3ToSKxhNjhOZL+696QtJGCFe132CbBLGk= github.com/lasiar/canonicalheader v1.1.2 h1:vZ5uqwvDbyJCnMhmFYimgMZnJMjwljN5VGY0VKbMXb4= github.com/lasiar/canonicalheader v1.1.2/go.mod h1:qJCeLFS0G/QlLQ506T+Fk/fWMa2VmBUiEI2cuMK4djI= -github.com/ldez/exptostd v0.4.2 h1:l5pOzHBz8mFOlbcifTxzfyYbgEmoUqjxLFHZkjlbHXs= -github.com/ldez/exptostd v0.4.2/go.mod h1:iZBRYaUmcW5jwCR3KROEZ1KivQQp6PHXbDPk9hqJKCQ= -github.com/ldez/gomoddirectives v0.6.1 h1:Z+PxGAY+217f/bSGjNZr/b2KTXcyYLgiWI6geMBN2Qc= -github.com/ldez/gomoddirectives v0.6.1/go.mod h1:cVBiu3AHR9V31em9u2kwfMKD43ayN5/XDgr+cdaFaKs= -github.com/ldez/grignotin v0.9.0 h1:MgOEmjZIVNn6p5wPaGp/0OKWyvq42KnzAt/DAb8O4Ow= -github.com/ldez/grignotin v0.9.0/go.mod h1:uaVTr0SoZ1KBii33c47O1M8Jp3OP3YDwhZCmzT9GHEk= -github.com/ldez/tagliatelle v0.7.1 h1:bTgKjjc2sQcsgPiT902+aadvMjCeMHrY7ly2XKFORIk= -github.com/ldez/tagliatelle v0.7.1/go.mod h1:3zjxUpsNB2aEZScWiZTHrAXOl1x25t3cRmzfK1mlo2I= -github.com/ldez/usetesting v0.4.2 h1:J2WwbrFGk3wx4cZwSMiCQQ00kjGR0+tuuyW0Lqm4lwA= -github.com/ldez/usetesting v0.4.2/go.mod h1:eEs46T3PpQ+9RgN9VjpY6qWdiw2/QmfiDeWmdZdrjIQ= +github.com/ldez/exptostd v0.4.4 h1:58AtQjnLcT/tI5W/1KU7xE/O7zW9RAWB6c/ScQAnfus= +github.com/ldez/exptostd v0.4.4/go.mod h1:QfdzPw6oHjFVdNV7ILoPu5sw3OZ3OG1JS0I5JN3J4Js= +github.com/ldez/gomoddirectives v0.7.0 h1:EOx8Dd56BZYSez11LVgdj025lKwlP0/E5OLSl9HDwsY= +github.com/ldez/gomoddirectives v0.7.0/go.mod h1:wR4v8MN9J8kcwvrkzrx6sC9xe9Cp68gWYCsda5xvyGc= +github.com/ldez/grignotin v0.10.1 h1:keYi9rYsgbvqAZGI1liek5c+jv9UUjbvdj3Tbn5fn4o= +github.com/ldez/grignotin v0.10.1/go.mod h1:UlDbXFCARrXbWGNGP3S5vsysNXAPhnSuBufpTEbwOas= +github.com/ldez/tagliatelle v0.7.2 h1:KuOlL70/fu9paxuxbeqlicJnCspCRjH0x8FW+NfgYUk= +github.com/ldez/tagliatelle v0.7.2/go.mod h1:PtGgm163ZplJfZMZ2sf5nhUT170rSuPgBimoyYtdaSI= +github.com/ldez/usetesting v0.5.0 h1:3/QtzZObBKLy1F4F8jLuKJiKBjjVFi1IavpoWbmqLwc= +github.com/ldez/usetesting v0.5.0/go.mod h1:Spnb4Qppf8JTuRgblLrEWb7IE6rDmUpGvxY3iRrzvDQ= github.com/leonklingele/grouper v1.1.2 h1:o1ARBDLOmmasUaNDesWqWCIFH3u7hoFlM84YrjT3mIY= github.com/leonklingele/grouper v1.1.2/go.mod h1:6D0M/HVkhs2yRKRFZUoGjeDy7EZTfFBE9gl4kjmIGkA= github.com/lucasb-eyer/go-colorful v1.2.0 h1:1nnpGOrhyZZuNyfu1QjKiUICQ74+3FNCN69Aj6K7nkY= github.com/lucasb-eyer/go-colorful v1.2.0/go.mod h1:R4dSotOR9KMtayYi1e77YzuveK+i7ruzyGqttikkLy0= github.com/macabu/inamedparam v0.2.0 h1:VyPYpOc10nkhI2qeNUdh3Zket4fcZjEWe35poddBCpE= github.com/macabu/inamedparam v0.2.0/go.mod h1:+Pee9/YfGe5LJ62pYXqB89lJ+0k5bsR8Wgz/C0Zlq3U= +github.com/manuelarte/embeddedstructfieldcheck v0.4.0 h1:3mAIyaGRtjK6EO9E73JlXLtiy7ha80b2ZVGyacxgfww= +github.com/manuelarte/embeddedstructfieldcheck v0.4.0/go.mod h1:z8dFSyXqp+fC6NLDSljRJeNQJJDWnY7RoWFzV3PC6UM= +github.com/manuelarte/funcorder v0.5.0 h1:llMuHXXbg7tD0i/LNw8vGnkDTHFpTnWqKPI85Rknc+8= +github.com/manuelarte/funcorder v0.5.0/go.mod h1:Yt3CiUQthSBMBxjShjdXMexmzpP8YGvGLjrxJNkO2hA= github.com/maratori/testableexamples v1.0.0 h1:dU5alXRrD8WKSjOUnmJZuzdxWOEQ57+7s93SLMxb2vI= github.com/maratori/testableexamples v1.0.0/go.mod h1:4rhjL1n20TUTT4vdh3RDqSizKLyXp7K2u6HgraZCGzE= github.com/maratori/testpackage v1.1.1 h1:S58XVV5AD7HADMmD0fNnziNHqKvSdDuEKdPD1rNTU04= @@ -261,11 +280,10 @@ github.com/mattn/go-colorable v0.1.14 h1:9A9LHSqF/7dyVVX6g0U9cwm9pG3kP9gSzcuIPHP github.com/mattn/go-colorable v0.1.14/go.mod h1:6LmQG8QLFO4G5z1gPvYEzlUgJ2wF+stgPZH1UqBm1s8= github.com/mattn/go-isatty v0.0.20 h1:xfD0iDuEKnDkl03q4limB+vH+GxLEtL/jb4xVJSWWEY= github.com/mattn/go-isatty v0.0.20/go.mod h1:W+V8PltTTMOvKvAeJH7IuucS94S2C6jfK/D7dTCTo3Y= -github.com/mattn/go-runewidth v0.0.9/go.mod h1:H031xJmbD/WCDINGzjvQ9THkh0rPKHF+m2gUSrubnMI= github.com/mattn/go-runewidth v0.0.16 h1:E5ScNMtiwvlvB5paMFdw9p4kSQzbXFikJ5SQO6TULQc= github.com/mattn/go-runewidth v0.0.16/go.mod h1:Jdepj2loyihRzMpdS35Xk/zdY8IAYHsh153qUoGf23w= -github.com/mgechev/revive v1.7.0 h1:JyeQ4yO5K8aZhIKf5rec56u0376h8AlKNQEmjfkjKlY= -github.com/mgechev/revive v1.7.0/go.mod h1:qZnwcNhoguE58dfi96IJeSTPeZQejNeoMQLUZGi4SW4= +github.com/mgechev/revive v1.12.0 h1:Q+/kkbbwerrVYPv9d9efaPGmAO/NsxwW/nE6ahpQaCU= +github.com/mgechev/revive v1.12.0/go.mod h1:VXsY2LsTigk8XU9BpZauVLjVrhICMOV3k1lpB3CXrp8= github.com/mitchellh/go-homedir v1.1.0 h1:lukF9ziXFxDFPkA1vsr5zpc1XuPDn/wFntq5mG+4E0Y= github.com/mitchellh/go-homedir v1.1.0/go.mod h1:SfyaCUpYCn1Vlf4IUYiD9fPX4A5wJrkLzIz1N1q0pr0= github.com/moricho/tparallel v0.3.2 h1:odr8aZVFA3NZrNybggMkYO3rgPRcqjeQUlBBFVxKHTI= @@ -280,14 +298,12 @@ github.com/nishanths/exhaustive v0.12.0 h1:vIY9sALmw6T/yxiASewa4TQcFsVYZQQRUQJhK github.com/nishanths/exhaustive v0.12.0/go.mod h1:mEZ95wPIZW+x8kC4TgC+9YCUgiST7ecevsVDTgc2obs= github.com/nishanths/predeclared v0.2.2 h1:V2EPdZPliZymNAn79T8RkNApBjMmVKh5XRpLm/w98Vk= github.com/nishanths/predeclared v0.2.2/go.mod h1:RROzoN6TnGQupbC+lqggsOlcgysk3LMK/HI84Mp280c= -github.com/nunnatsa/ginkgolinter v0.19.1 h1:mjwbOlDQxZi9Cal+KfbEJTCz327OLNfwNvoZ70NJ+c4= -github.com/nunnatsa/ginkgolinter v0.19.1/go.mod h1:jkQ3naZDmxaZMXPWaS9rblH+i+GWXQCaS/JFIWcOH2s= -github.com/olekukonko/tablewriter v0.0.5 h1:P2Ga83D34wi1o9J6Wh1mRuqd4mF/x/lgBS7N7AbDhec= -github.com/olekukonko/tablewriter v0.0.5/go.mod h1:hPp6KlRPjbx+hW8ykQs1w3UBbZlj6HuIJcUGPhkA7kY= -github.com/onsi/ginkgo/v2 v2.22.2 h1:/3X8Panh8/WwhU/3Ssa6rCKqPLuAkVY2I0RoyDLySlU= -github.com/onsi/ginkgo/v2 v2.22.2/go.mod h1:oeMosUL+8LtarXBHu/c0bx2D/K9zyQ6uX3cTyztHwsk= -github.com/onsi/gomega v1.36.2 h1:koNYke6TVk6ZmnyHrCXba/T/MoLBXFjeC1PtvYgw0A8= -github.com/onsi/gomega v1.36.2/go.mod h1:DdwyADRjrc825LhMEkD76cHR5+pUnjhUN8GlHlRPHzY= +github.com/nunnatsa/ginkgolinter v0.21.0 h1:IYwuX+ajy3G1MezlMLB1BENRtFj16+Evyi4uki1NOOQ= +github.com/nunnatsa/ginkgolinter v0.21.0/go.mod h1:QlzY9UP9zaqu58FjYxhp9bnjuwXwG1bfW5rid9ChNMw= +github.com/onsi/ginkgo/v2 v2.23.4 h1:ktYTpKJAVZnDT4VjxSbiBenUjmlL/5QkBEocaWXiQus= +github.com/onsi/ginkgo/v2 v2.23.4/go.mod h1:Bt66ApGPBFzHyR+JO10Zbt0Gsp4uWxu5mIOTusL46e8= +github.com/onsi/gomega v1.38.0 h1:c/WX+w8SLAinvuKKQFh77WEucCnPk4j2OTUr7lt7BeY= +github.com/onsi/gomega v1.38.0/go.mod h1:OcXcwId0b9QsE7Y49u+BTrL4IdKOBOKnD6VQNTJEB6o= github.com/otiai10/copy v1.2.0/go.mod h1:rrF5dJ5F0t/EWSYODDu4j9/vEeYHMkc8jt0zJChqQWw= github.com/otiai10/copy v1.14.0 h1:dCI/t1iTdYGtkvCuBG2BgR6KZa83PTclw4U5n2wAllU= github.com/otiai10/copy v1.14.0/go.mod h1:ECfuL02W+/FkTWZWgQqXPWZgW9oeKCSQ5qVfSc4qc4w= @@ -295,13 +311,13 @@ github.com/otiai10/curr v0.0.0-20150429015615-9b4961190c95/go.mod h1:9qAhocn7zKJ github.com/otiai10/curr v1.0.0/go.mod h1:LskTG5wDwr8Rs+nNQ+1LlxRjAtTZZjtJW4rMXl6j4vs= github.com/otiai10/mint v1.3.0/go.mod h1:F5AjcsTsWUqX+Na9fpHb52P8pcRX2CI6A3ctIT91xUo= github.com/otiai10/mint v1.3.1/go.mod h1:/yxELlJQ0ufhjUwhshSj+wFjZ78CnZ48/1wtmBH1OTc= -github.com/pelletier/go-toml/v2 v2.2.3 h1:YmeHyLY8mFWbdkNWwpr+qIL2bEqT0o95WSdkNHvL12M= -github.com/pelletier/go-toml/v2 v2.2.3/go.mod h1:MfCQTFTvCcUyyvvwm1+G6H/jORL20Xlb6rzQu9GuUkc= +github.com/pelletier/go-toml/v2 v2.2.4 h1:mye9XuhQ6gvn5h28+VilKrrPoQVanw5PMw/TB0t5Ec4= +github.com/pelletier/go-toml/v2 v2.2.4/go.mod h1:2gIqNv+qfxSVS7cM2xJQKtLSTLUE9V8t9Stt+h56mCY= github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= github.com/pmezard/go-difflib v1.0.1-0.20181226105442-5d4384ee4fb2 h1:Jamvg5psRIccs7FGNTlIRMkT8wgtp5eCXdBlqhYGL6U= github.com/pmezard/go-difflib v1.0.1-0.20181226105442-5d4384ee4fb2/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= -github.com/polyfloyd/go-errorlint v1.7.1 h1:RyLVXIbosq1gBdk/pChWA8zWYLsq9UEw7a1L5TVMCnA= -github.com/polyfloyd/go-errorlint v1.7.1/go.mod h1:aXjNb1x2TNhoLsk26iv1yl7a+zTnXPhwEMtEXukiLR8= +github.com/polyfloyd/go-errorlint v1.8.0 h1:DL4RestQqRLr8U4LygLw8g2DX6RN1eBJOpa2mzsrl1Q= +github.com/polyfloyd/go-errorlint v1.8.0/go.mod h1:G2W0Q5roxbLCt0ZQbdoxQxXktTjwNyDbEaj3n7jvl4s= github.com/prashantv/gostub v1.1.0 h1:BTyx3RfQjRHnUWaGF9oQos79AlQ5k8WNktv7VGvVH4g= github.com/prashantv/gostub v1.1.0/go.mod h1:A5zLQHz7ieHGG7is6LLXLz7I8+3LZzsrV0P1IAHhP5U= github.com/prometheus/client_golang v1.22.0 h1:rb93p9lokFEsctTys46VnV1kLCDpVZ0a/Y92Vm0Zc6Q= @@ -338,14 +354,14 @@ github.com/sagikazarmark/locafero v0.7.0 h1:5MqpDsTGNDhY8sGp0Aowyf0qKsPrhewaLSsF github.com/sagikazarmark/locafero v0.7.0/go.mod h1:2za3Cg5rMaTMoG/2Ulr9AwtFaIppKXTRYnozin4aB5k= github.com/sanposhiho/wastedassign/v2 v2.1.0 h1:crurBF7fJKIORrV85u9UUpePDYGWnwvv3+A96WvwXT0= github.com/sanposhiho/wastedassign/v2 v2.1.0/go.mod h1:+oSmSC+9bQ+VUAxA66nBb0Z7N8CK7mscKTDYC6aIek4= -github.com/santhosh-tekuri/jsonschema/v6 v6.0.1 h1:PKK9DyHxif4LZo+uQSgXNqs0jj5+xZwwfKHgph2lxBw= -github.com/santhosh-tekuri/jsonschema/v6 v6.0.1/go.mod h1:JXeL+ps8p7/KNMjDQk3TCwPpBy0wYklyWTfbkIzdIFU= +github.com/santhosh-tekuri/jsonschema/v6 v6.0.2 h1:KRzFb2m7YtdldCEkzs6KqmJw4nqEVZGK7IN2kJkjTuQ= +github.com/santhosh-tekuri/jsonschema/v6 v6.0.2/go.mod h1:JXeL+ps8p7/KNMjDQk3TCwPpBy0wYklyWTfbkIzdIFU= github.com/sashamelentyev/interfacebloat v1.1.0 h1:xdRdJp0irL086OyW1H/RTZTr1h/tMEOsumirXcOJqAw= github.com/sashamelentyev/interfacebloat v1.1.0/go.mod h1:+Y9yU5YdTkrNvoX0xHc84dxiN1iBi9+G8zZIhPVoNjQ= -github.com/sashamelentyev/usestdlibvars v1.28.0 h1:jZnudE2zKCtYlGzLVreNp5pmCdOxXUzwsMDBkR21cyQ= -github.com/sashamelentyev/usestdlibvars v1.28.0/go.mod h1:9nl0jgOfHKWNFS43Ojw0i7aRoS4j6EBye3YBhmAIRF8= -github.com/securego/gosec/v2 v2.22.2 h1:IXbuI7cJninj0nRpZSLCUlotsj8jGusohfONMrHoF6g= -github.com/securego/gosec/v2 v2.22.2/go.mod h1:UEBGA+dSKb+VqM6TdehR7lnQtIIMorYJ4/9CW1KVQBE= +github.com/sashamelentyev/usestdlibvars v1.29.0 h1:8J0MoRrw4/NAXtjQqTHrbW9NN+3iMf7Knkq057v4XOQ= +github.com/sashamelentyev/usestdlibvars v1.29.0/go.mod h1:8PpnjHMk5VdeWlVb4wCdrB8PNbLqZ3wBZTZWkrpZZL8= +github.com/securego/gosec/v2 v2.22.8 h1:3NMpmfXO8wAVFZPNsd3EscOTa32Jyo6FLLlW53bexMI= +github.com/securego/gosec/v2 v2.22.8/go.mod h1:ZAw8K2ikuH9qDlfdV87JmNghnVfKB1XC7+TVzk6Utto= github.com/sergi/go-diff v1.3.2-0.20230802210424-5b0b94c5c0d3 h1:n661drycOFuPLCN3Uc8sB6B/s6Z4t2xvBgU1htSHuq8= github.com/sergi/go-diff v1.3.2-0.20230802210424-5b0b94c5c0d3/go.mod h1:A0bzQcvG0E7Rwjx0REVgAGH58e96+X0MeOfepqsbeW4= github.com/shurcooL/go v0.0.0-20180423040247-9e1955d9fb6e/go.mod h1:TDJrrUr11Vxrven61rcy3hJMUqaf/CLWYhHNPmT14Lk= @@ -354,21 +370,22 @@ github.com/sirupsen/logrus v1.9.3 h1:dueUQJ1C2q9oE3F7wvmSGAaVtTmUizReu6fjN8uqzbQ github.com/sirupsen/logrus v1.9.3/go.mod h1:naHLuLoDiP4jHNo9R0sCBMtWGeIprob74mVsIT4qYEQ= github.com/sivchari/containedctx v1.0.3 h1:x+etemjbsh2fB5ewm5FeLNi5bUjK0V8n0RB+Wwfd0XE= github.com/sivchari/containedctx v1.0.3/go.mod h1:c1RDvCbnJLtH4lLcYD/GqwiBSSf4F5Qk0xld2rBqzJ4= -github.com/sonatard/noctx v0.1.0 h1:JjqOc2WN16ISWAjAk8M5ej0RfExEXtkEyExl2hLW+OM= -github.com/sonatard/noctx v0.1.0/go.mod h1:0RvBxqY8D4j9cTTTWE8ylt2vqj2EPI8fHmrxHdsaZ2c= +github.com/sonatard/noctx v0.4.0 h1:7MC/5Gg4SQ4lhLYR6mvOP6mQVSxCrdyiExo7atBs27o= +github.com/sonatard/noctx v0.4.0/go.mod h1:64XdbzFb18XL4LporKXp8poqZtPKbCrqQ402CV+kJas= github.com/sourcegraph/conc v0.3.0 h1:OQTbbt6P72L20UqAkXXuLOj79LfEanQ+YQFNpLA9ySo= github.com/sourcegraph/conc v0.3.0/go.mod h1:Sdozi7LEKbFPqYX2/J+iBAM6HpqSLTASQIKqDmF7Mt0= github.com/sourcegraph/go-diff v0.7.0 h1:9uLlrd5T46OXs5qpp8L/MTltk0zikUGi0sNNyCpA8G0= github.com/sourcegraph/go-diff v0.7.0/go.mod h1:iBszgVvyxdc8SFZ7gm69go2KDdt3ag071iBaWPF6cjs= -github.com/spf13/afero v1.12.0 h1:UcOPyRBYczmFn6yvphxkn9ZEOY65cpwGKb5mL36mrqs= -github.com/spf13/afero v1.12.0/go.mod h1:ZTlWwG4/ahT8W7T0WQ5uYmjI9duaLQGy3Q2OAl4sk/4= +github.com/spf13/afero v1.14.0 h1:9tH6MapGnn/j0eb0yIXiLjERO8RB6xIVZRDCX7PtqWA= +github.com/spf13/afero v1.14.0/go.mod h1:acJQ8t0ohCGuMN3O+Pv0V0hgMxNYDlvdk+VTfyZmbYo= github.com/spf13/cast v1.7.1 h1:cuNEagBQEHWN1FnbGEjCXL2szYEXqfJPbP2HNUaca9Y= github.com/spf13/cast v1.7.1/go.mod h1:ancEpBxwJDODSW/UG4rDrAqiKolqNNh2DX3mk86cAdo= -github.com/spf13/cobra v1.9.1 h1:CXSaggrXdbHK9CF+8ywj8Amf7PBRmPCOJugH954Nnlo= -github.com/spf13/cobra v1.9.1/go.mod h1:nDyEzZ8ogv936Cinf6g1RU9MRY64Ir93oCnqb9wxYW0= +github.com/spf13/cobra v1.10.1 h1:lJeBwCfmrnXthfAupyUTzJ/J4Nc1RsHC/mSRU2dll/s= +github.com/spf13/cobra v1.10.1/go.mod h1:7SmJGaTHFVBY0jW4NXGluQoLvhqFQM+6XSKD+P4XaB0= github.com/spf13/pflag v1.0.5/go.mod h1:McXfInJRrz4CZXVZOBLb0bTZqETkiAhM9Iw0y3An2Bg= -github.com/spf13/pflag v1.0.6 h1:jFzHGLGAlb3ruxLB8MhbI6A8+AQX/2eW4qeyNZXNp2o= -github.com/spf13/pflag v1.0.6/go.mod h1:McXfInJRrz4CZXVZOBLb0bTZqETkiAhM9Iw0y3An2Bg= +github.com/spf13/pflag v1.0.9/go.mod h1:McXfInJRrz4CZXVZOBLb0bTZqETkiAhM9Iw0y3An2Bg= +github.com/spf13/pflag v1.0.10 h1:4EBh2KAYBwaONj6b2Ye1GiHfwjqyROoF4RwYO+vPwFk= +github.com/spf13/pflag v1.0.10/go.mod h1:McXfInJRrz4CZXVZOBLb0bTZqETkiAhM9Iw0y3An2Bg= github.com/spf13/viper v1.20.1 h1:ZMi+z/lvLyPSCoNtFCpqjy0S4kPbirhpTMwl8BkW9X4= github.com/spf13/viper v1.20.1/go.mod h1:P9Mdzt1zoHIG8m2eZQinpiBjo6kCmZSKBClNNqjJvu4= github.com/ssgreg/nlreturn/v2 v2.2.1 h1:X4XDI7jstt3ySqGU86YGAURbxw3oTDPK9sPEi6YEwQ0= @@ -376,35 +393,27 @@ github.com/ssgreg/nlreturn/v2 v2.2.1/go.mod h1:E/iiPB78hV7Szg2YfRgyIrk1AD6JVMTRk github.com/stbenjam/no-sprintf-host-port v0.2.0 h1:i8pxvGrt1+4G0czLr/WnmyH7zbZ8Bg8etvARQ1rpyl4= github.com/stbenjam/no-sprintf-host-port v0.2.0/go.mod h1:eL0bQ9PasS0hsyTyfTjjG+E80QIyPnBVQbYZyv20Jfk= github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= -github.com/stretchr/objx v0.4.0/go.mod h1:YvHI0jy2hoMjB+UWwv71VJQ9isScKT/TqJzVSSt89Yw= -github.com/stretchr/objx v0.5.0/go.mod h1:Yh+to48EsGEfYuaHDzXPcE3xhTkx73EhmCGUpEOglKo= github.com/stretchr/objx v0.5.2 h1:xuMeJ0Sdp5ZMRXx/aWO6RZxdr3beISkG5/G/aIRr3pY= github.com/stretchr/objx v0.5.2/go.mod h1:FRsXN1f5AsAjCGJKqEizvkpNtU+EGNCLh3NxZ/8L+MA= github.com/stretchr/testify v1.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UVUgZn+9EI= github.com/stretchr/testify v1.4.0/go.mod h1:j7eGeouHqKxXV5pUuKE4zz7dFj8WfuZ+81PSLYec5m4= -github.com/stretchr/testify v1.5.1/go.mod h1:5W2xD1RspED5o8YsWQXVCued0rvSQ+mT+I5cxcmMvtA= github.com/stretchr/testify v1.7.0/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= -github.com/stretchr/testify v1.7.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= -github.com/stretchr/testify v1.8.0/go.mod h1:yNjHg4UonilssWZ8iaSj1OCr/vHnekPRkoO+kdMU+MU= -github.com/stretchr/testify v1.8.4/go.mod h1:sz/lmYIOXD/1dqDmKjjqLyZ2RngseejIcXlSw2iwfAo= -github.com/stretchr/testify v1.10.0 h1:Xv5erBjTwe/5IxqUQTdXv5kgmIvbHo3QQyRwhJsOfJA= -github.com/stretchr/testify v1.10.0/go.mod h1:r2ic/lqez/lEtzL7wO/rwa5dbSLXVDPFyf8C91i36aY= +github.com/stretchr/testify v1.11.1 h1:7s2iGBzp5EwR7/aIZr8ao5+dra3wiQyKjjFuvgVKu7U= +github.com/stretchr/testify v1.11.1/go.mod h1:wZwfW3scLgRK+23gO65QZefKpKQRnfz6sD981Nm4B6U= github.com/subosito/gotenv v1.6.0 h1:9NlTDc1FTs4qu0DDq7AEtTPNw6SVm7uBMsUCUjABIf8= github.com/subosito/gotenv v1.6.0/go.mod h1:Dk4QP5c2W3ibzajGcXpNraDfq2IrhjMIvMSWPKKo0FU= -github.com/tdakkota/asciicheck v0.4.1 h1:bm0tbcmi0jezRA2b5kg4ozmMuGAFotKI3RZfrhfovg8= -github.com/tdakkota/asciicheck v0.4.1/go.mod h1:0k7M3rCfRXb0Z6bwgvkEIMleKH3kXNz9UqJ9Xuqopr8= github.com/tenntenn/modver v1.0.1 h1:2klLppGhDgzJrScMpkj9Ujy3rXPUspSjAcev9tSEBgA= github.com/tenntenn/modver v1.0.1/go.mod h1:bePIyQPb7UeioSRkw3Q0XeMhYZSMx9B8ePqg6SAMGH0= github.com/tenntenn/text/transform v0.0.0-20200319021203-7eef512accb3 h1:f+jULpRQGxTSkNYKJ51yaw6ChIqO+Je8UqsTKN/cDag= github.com/tenntenn/text/transform v0.0.0-20200319021203-7eef512accb3/go.mod h1:ON8b8w4BN/kE1EOhwT0o+d62W65a6aPw1nouo9LMgyY= -github.com/tetafro/godot v1.5.0 h1:aNwfVI4I3+gdxjMgYPus9eHmoBeJIbnajOyqZYStzuw= -github.com/tetafro/godot v1.5.0/go.mod h1:2oVxTBSftRTh4+MVfUaUXR6bn2GDXCaMcOG4Dk3rfio= +github.com/tetafro/godot v1.5.4 h1:u1ww+gqpRLiIA16yF2PV1CV1n/X3zhyezbNXC3E14Sg= +github.com/tetafro/godot v1.5.4/go.mod h1:eOkMrVQurDui411nBY2FA05EYH01r14LuWY/NrVDVcU= github.com/timakin/bodyclose v0.0.0-20241222091800-1db5c5ca4d67 h1:9LPGD+jzxMlnk5r6+hJnar67cgpDIz/iyD+rfl5r2Vk= github.com/timakin/bodyclose v0.0.0-20241222091800-1db5c5ca4d67/go.mod h1:mkjARE7Yr8qU23YcGMSALbIxTQ9r9QBVahQOBRfU460= -github.com/timonwong/loggercheck v0.10.1 h1:uVZYClxQFpw55eh+PIoqM7uAOHMrhVcDoWDery9R8Lg= -github.com/timonwong/loggercheck v0.10.1/go.mod h1:HEAWU8djynujaAVX7QI65Myb8qgfcZ1uKbdpg3ZzKl8= -github.com/tomarrell/wrapcheck/v2 v2.10.0 h1:SzRCryzy4IrAH7bVGG4cK40tNUhmVmMDuJujy4XwYDg= -github.com/tomarrell/wrapcheck/v2 v2.10.0/go.mod h1:g9vNIyhb5/9TQgumxQyOEqDHsmGYcGsVMOx/xGkqdMo= +github.com/timonwong/loggercheck v0.11.0 h1:jdaMpYBl+Uq9mWPXv1r8jc5fC3gyXx4/WGwTnnNKn4M= +github.com/timonwong/loggercheck v0.11.0/go.mod h1:HEAWU8djynujaAVX7QI65Myb8qgfcZ1uKbdpg3ZzKl8= +github.com/tomarrell/wrapcheck/v2 v2.11.0 h1:BJSt36snX9+4WTIXeJ7nvHBQBcm1h2SjQMSlmQ6aFSU= +github.com/tomarrell/wrapcheck/v2 v2.11.0/go.mod h1:wFL9pDWDAbXhhPZZt+nG8Fu+h29TtnZ2MW6Lx4BRXIU= github.com/tommy-muehle/go-mnd/v2 v2.5.1 h1:NowYhSdyE/1zwK9QCLeRb6USWdoif80Ie+v+yU8u1Zw= github.com/tommy-muehle/go-mnd/v2 v2.5.1/go.mod h1:WsUAkMJMYww6l/ufffCD3m+P7LEvr8TnZn9lwVDlgzw= github.com/ultraware/funlen v0.2.0 h1:gCHmCn+d2/1SemTdYMiKLAHFYxTYz7z9VIDRaTGyLkI= @@ -413,8 +422,8 @@ github.com/ultraware/whitespace v0.2.0 h1:TYowo2m9Nfj1baEQBjuHzvMRbp19i+RCcRYrSW github.com/ultraware/whitespace v0.2.0/go.mod h1:XcP1RLD81eV4BW8UhQlpaR+SDc2givTvyI8a586WjW8= github.com/uudashr/gocognit v1.2.0 h1:3BU9aMr1xbhPlvJLSydKwdLN3tEUUrzPSSM8S4hDYRA= github.com/uudashr/gocognit v1.2.0/go.mod h1:k/DdKPI6XBZO1q7HgoV2juESI2/Ofj9AcHPZhBBdrTU= -github.com/uudashr/iface v1.3.1 h1:bA51vmVx1UIhiIsQFSNq6GZ6VPTk3WNMZgRiCe9R29U= -github.com/uudashr/iface v1.3.1/go.mod h1:4QvspiRd3JLPAEXBQ9AiZpLbJlrWWgRChOKDJEuQTdg= +github.com/uudashr/iface v1.4.1 h1:J16Xl1wyNX9ofhpHmQ9h9gk5rnv2A6lX/2+APLTo0zU= +github.com/uudashr/iface v1.4.1/go.mod h1:pbeBPlbuU2qkNDn0mmfrxP2X+wjPMIQAy+r1MBXSXtg= github.com/xen0n/gosmopolitan v1.3.0 h1:zAZI1zefvo7gcpbCOrPSHJZJYA9ZgLfJqtKzZ5pHqQM= github.com/xen0n/gosmopolitan v1.3.0/go.mod h1:rckfr5T6o4lBtM1ga7mLGKZmLxswUoH1zxHgNXOsEt4= github.com/xo/terminfo v0.0.0-20220910002029-abceb7e1c41e h1:JVG44RsyaB9T2KIHavMF/ppJZNG9ZpyihvCd0w101no= @@ -435,10 +444,14 @@ gitlab.com/bosi/decorder v0.4.2 h1:qbQaV3zgwnBZ4zPMhGLW4KZe7A7NwxEhJx39R3shffo= gitlab.com/bosi/decorder v0.4.2/go.mod h1:muuhHoaJkA9QLcYHq4Mj8FJUwDZ+EirSHRiaTcTf6T8= go-simpler.org/assert v0.9.0 h1:PfpmcSvL7yAnWyChSjOz6Sp6m9j5lyK8Ok9pEL31YkQ= go-simpler.org/assert v0.9.0/go.mod h1:74Eqh5eI6vCK6Y5l3PI8ZYFXG4Sa+tkr70OIPJAUr28= -go-simpler.org/musttag v0.13.0 h1:Q/YAW0AHvaoaIbsPj3bvEI5/QFP7w696IMUpnKXQfCE= -go-simpler.org/musttag v0.13.0/go.mod h1:FTzIGeK6OkKlUDVpj0iQUXZLUO1Js9+mvykDQy9C5yM= -go-simpler.org/sloglint v0.9.0 h1:/40NQtjRx9txvsB/RN022KsUJU+zaaSb/9q9BSefSrE= -go-simpler.org/sloglint v0.9.0/go.mod h1:G/OrAF6uxj48sHahCzrbarVMptL2kjWTaUeC8+fOGww= +go-simpler.org/musttag v0.14.0 h1:XGySZATqQYSEV3/YTy+iX+aofbZZllJaqwFWs+RTtSo= +go-simpler.org/musttag v0.14.0/go.mod h1:uP8EymctQjJ4Z1kUnjX0u2l60WfUdQxCwSNKzE1JEOE= +go-simpler.org/sloglint v0.11.1 h1:xRbPepLT/MHPTCA6TS/wNfZrDzkGvCCqUv4Bdwc3H7s= +go-simpler.org/sloglint v0.11.1/go.mod h1:2PowwiCOK8mjiF+0KGifVOT8ZsCNiFzvfyJeJOIt8MQ= +go.augendre.info/arangolint v0.2.0 h1:2NP/XudpPmfBhQKX4rMk+zDYIj//qbt4hfZmSSTcpj8= +go.augendre.info/arangolint v0.2.0/go.mod h1:Vx4KSJwu48tkE+8uxuf0cbBnAPgnt8O1KWiT7bljq7w= +go.augendre.info/fatcontext v0.8.1 h1:/T4+cCjpL9g71gJpcFAgVo/K5VFpqlN+NPU7QXxD5+A= +go.augendre.info/fatcontext v0.8.1/go.mod h1:r3Qz4ZOzex66wfyyj5VZ1xUcl81vzvHQ6/GWzzlMEwA= go.uber.org/automaxprocs v1.6.0 h1:O3y2/QNTOdbF+e/dpXNNW7Rx2hZ4sTIPyybbxyNqTUs= go.uber.org/automaxprocs v1.6.0/go.mod h1:ifeIMSnPZuznNm6jmdzmU3/bfk01Fe2fotchwEFJ8r8= go.uber.org/goleak v1.3.0 h1:2K3zAYmnTNqV73imy9J1T3WC+gmCePx2hEGkimedGto= @@ -457,21 +470,19 @@ golang.org/x/exp v0.0.0-20250305212735-054e65f0b394 h1:nDVHiLt8aIbd/VzvPWN6kSOPE golang.org/x/exp v0.0.0-20250305212735-054e65f0b394/go.mod h1:sIifuuw/Yco/y6yb6+bDNfyeQ/MdPUy/hKEMYQV17cM= golang.org/x/exp/typeparams v0.0.0-20220428152302-39d4317da171/go.mod h1:AbB0pIl9nAr9wVwH+Z2ZpaocVmF5I4GyWCDIsVjR0bk= golang.org/x/exp/typeparams v0.0.0-20230203172020-98cc5a0785f9/go.mod h1:AbB0pIl9nAr9wVwH+Z2ZpaocVmF5I4GyWCDIsVjR0bk= -golang.org/x/exp/typeparams v0.0.0-20250210185358-939b2ce775ac h1:TSSpLIG4v+p0rPv1pNOQtl1I8knsO4S9trOxNMOLVP4= -golang.org/x/exp/typeparams v0.0.0-20250210185358-939b2ce775ac/go.mod h1:AbB0pIl9nAr9wVwH+Z2ZpaocVmF5I4GyWCDIsVjR0bk= +golang.org/x/exp/typeparams v0.0.0-20250911091902-df9299821621 h1:Yl4H5w2RV7L/dvSHp2GerziT5K2CORgFINPaMFxWGWw= +golang.org/x/exp/typeparams v0.0.0-20250911091902-df9299821621/go.mod h1:4Mzdyp/6jzw9auFDJ3OMF5qksa7UvPnzKqTVGcb04ms= golang.org/x/mod v0.2.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA= golang.org/x/mod v0.3.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA= golang.org/x/mod v0.4.1/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA= golang.org/x/mod v0.4.2/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA= golang.org/x/mod v0.6.0-dev.0.20220106191415-9b9b3d81d5e3/go.mod h1:3p9vT2HGsQu2K1YbXdKPJLVgG5VJdoTa1poYQBtP1AY= golang.org/x/mod v0.6.0-dev.0.20220419223038-86c51ed26bb4/go.mod h1:jJ57K6gSWd91VN4djpZkiMVwK6gcyfeH4XE8wZrZaV4= -golang.org/x/mod v0.7.0/go.mod h1:iBbtSCu2XBx23ZKBPSOrRkjjQPZFPuis4dIYUhu/chs= golang.org/x/mod v0.8.0/go.mod h1:iBbtSCu2XBx23ZKBPSOrRkjjQPZFPuis4dIYUhu/chs= -golang.org/x/mod v0.9.0/go.mod h1:iBbtSCu2XBx23ZKBPSOrRkjjQPZFPuis4dIYUhu/chs= golang.org/x/mod v0.12.0/go.mod h1:iBbtSCu2XBx23ZKBPSOrRkjjQPZFPuis4dIYUhu/chs= golang.org/x/mod v0.13.0/go.mod h1:hTbmBsO62+eylJbnUtE2MGJUyE7QWk4xUqPFrRgJ+7c= -golang.org/x/mod v0.24.0 h1:ZfthKaKaT4NrhGVZHO1/WDTwGES4De8KtWO0SIbNJMU= -golang.org/x/mod v0.24.0/go.mod h1:IXM97Txy2VM4PJ3gI61r1YEk/gAj6zAHN3AdZt6S9Ww= +golang.org/x/mod v0.28.0 h1:gQBtGhjxykdjY9YhZpSlZIsbnaE2+PgjfLWUQTnoZ1U= +golang.org/x/mod v0.28.0/go.mod h1:yfB/L0NOf/kmEbXjzCPOx1iK1fRutOydrCMsqRhEBxI= golang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= golang.org/x/net v0.0.0-20190620200207-3b0461eec859/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= golang.org/x/net v0.0.0-20200226121028-0de0cce0169b/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= @@ -481,14 +492,12 @@ golang.org/x/net v0.0.0-20210226172049-e18ecbb05110/go.mod h1:m0MpNAwzfU5UDzcl9v golang.org/x/net v0.0.0-20210405180319-a5a99cb37ef4/go.mod h1:p54w0d4576C0XHj96bSt6lcn1PtDYWL6XObtHCRCNQM= golang.org/x/net v0.0.0-20211015210444-4f30a5c0130f/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y= golang.org/x/net v0.0.0-20220722155237-a158d28d115b/go.mod h1:XRhObCWvk6IyKnWLug+ECip1KBveYUHfp+8e9klMJ9c= -golang.org/x/net v0.2.0/go.mod h1:KqCZLdyyvdV855qA2rE3GC2aiw5xGR5TEjj8smXukLY= golang.org/x/net v0.6.0/go.mod h1:2Tu9+aMcznHK/AK1HMvgo6xiTLG5rD5rZLDS+rp2Bjs= -golang.org/x/net v0.8.0/go.mod h1:QVkue5JL9kW//ek3r6jTKnTFis1tRmNAW2P1shuFdJc= golang.org/x/net v0.10.0/go.mod h1:0qNGK6F8kojg2nk9dLZ2mShWaEBan6FAoqfSigmmuDg= golang.org/x/net v0.15.0/go.mod h1:idbUs1IY1+zTqbi8yxTbhexhEEk5ur9LInksu6HrEpk= golang.org/x/net v0.16.0/go.mod h1:NxSsAGuq816PNPmqtQdLE42eU2Fs7NoRIZrHJAlaCOE= -golang.org/x/net v0.40.0 h1:79Xs7wF06Gbdcg4kdCCIQArK11Z1hr5POQ6+fIYHNuY= -golang.org/x/net v0.40.0/go.mod h1:y0hY0exeL2Pku80/zKK7tpntoX23cqL3Oa6njdgRtds= +golang.org/x/net v0.45.0 h1:RLBg5JKixCy82FtLJpeNlVM0nrSqpCRYzVU1n8kj0tM= +golang.org/x/net v0.45.0/go.mod h1:ECOoLqd5U3Lhyeyo/QDCEVQ4sNgYsqvCZ722XogGieY= golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.0.0-20190911185100-cd5d95a43a6e/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.0.0-20200625203802-6e8e738ad208/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= @@ -498,8 +507,8 @@ golang.org/x/sync v0.0.0-20220722155255-886fb9371eb4/go.mod h1:RxMgew5VJxzue5/jJ golang.org/x/sync v0.1.0/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.3.0/go.mod h1:FU7BRWz2tNW+3quACPkgCx/L+uEAv1htQ0V83Z9Rj+Y= golang.org/x/sync v0.4.0/go.mod h1:FU7BRWz2tNW+3quACPkgCx/L+uEAv1htQ0V83Z9Rj+Y= -golang.org/x/sync v0.14.0 h1:woo0S4Yywslg6hp4eUFjTVOyKt0RookbpAHG4c1HmhQ= -golang.org/x/sync v0.14.0/go.mod h1:1dzgHSNfp02xaA81J2MS99Qcpr2w7fw1gpm99rleRqA= +golang.org/x/sync v0.17.0 h1:l60nONMj9l5drqw6jlhIELNv9I0A4OFgRsG9k2oT9Ug= +golang.org/x/sync v0.17.0/go.mod h1:9KTHXmSnoGruLpwFjVSX0lNNA75CykiMECbovNTZqGI= golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= golang.org/x/sys v0.0.0-20190412213103-97732733099d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20200323222414-85ca7c5b95cd/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= @@ -515,19 +524,16 @@ golang.org/x/sys v0.0.0-20211105183446-c75c47738b0c/go.mod h1:oPkhp1MJrh7nUepCBc golang.org/x/sys v0.0.0-20220520151302-bc2c85ada10a/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20220715151400-c0bba94af5f8/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20220722155257-8c9f86f7a55f/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= -golang.org/x/sys v0.2.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.5.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.6.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.8.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.12.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.13.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= -golang.org/x/sys v0.33.0 h1:q3i8TbbEz+JRD9ywIRlyRAQbM0qF7hu24q3teo2hbuw= -golang.org/x/sys v0.33.0/go.mod h1:BJP2sWEmIv4KK5OTEluFJCKSidICx8ciO85XgH3Ak8k= +golang.org/x/sys v0.36.0 h1:KVRy2GtZBrk1cBYA7MKu5bEZFxQk4NIDV6RLVcC8o0k= +golang.org/x/sys v0.36.0/go.mod h1:OgkHotnGiDImocRcuBABYBEXf8A9a87e/uXjp9XT3ks= golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo= golang.org/x/term v0.0.0-20210927222741-03fcf44c2211/go.mod h1:jbD1KX2456YbFQfuXm/mYQcufACuNUgVhRMnK/tPxf8= -golang.org/x/term v0.2.0/go.mod h1:TVmDHMZPmdnySmBfhjOoOdhjzdE1h4u1VwSiw2l1Nuc= golang.org/x/term v0.5.0/go.mod h1:jMB1sMXY+tzblOD4FWmEbocvup2/aLOaQEp7JmGp78k= -golang.org/x/term v0.6.0/go.mod h1:m6U89DPEgQRMq3DNkDClhWw02AUbt2daBVO4cn4Hv9U= golang.org/x/term v0.8.0/go.mod h1:xPskH00ivmX89bAKVGSKKtLOWNx2+17Eiy94tnKShWo= golang.org/x/term v0.12.0/go.mod h1:owVbMEjm3cBLCHdkQu9b1opXd4ETQWc3BhuQGKgXgvU= golang.org/x/term v0.13.0/go.mod h1:LTmsnFJwVN6bCy1rVCoS+qHT1HhALEFxKncY3WNNh4U= @@ -536,33 +542,29 @@ golang.org/x/text v0.3.2/go.mod h1:bEr9sfX3Q8Zfm5fL9x+3itogRgK3+ptLWKqgva+5dAk= golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= golang.org/x/text v0.3.6/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= golang.org/x/text v0.3.7/go.mod h1:u+2+/6zg+i71rQMx5EYifcz6MCKuco9NR6JIITiCfzQ= -golang.org/x/text v0.4.0/go.mod h1:mrYo+phRRbMaCq/xk9113O4dZlRixOauAjOtrjsXDZ8= golang.org/x/text v0.7.0/go.mod h1:mrYo+phRRbMaCq/xk9113O4dZlRixOauAjOtrjsXDZ8= -golang.org/x/text v0.8.0/go.mod h1:e1OnstbJyHTd6l/uOt8jFFHp6TRDWZR/bV3emEE/zU8= golang.org/x/text v0.9.0/go.mod h1:e1OnstbJyHTd6l/uOt8jFFHp6TRDWZR/bV3emEE/zU8= golang.org/x/text v0.13.0/go.mod h1:TvPlkZtksWOMsz7fbANvkp4WM8x/WCo/om8BMLbz+aE= -golang.org/x/text v0.25.0 h1:qVyWApTSYLk/drJRO5mDlNYskwQznZmkpV2c8q9zls4= -golang.org/x/text v0.25.0/go.mod h1:WEdwpYrmk1qmdHvhkSTNPm3app7v4rsT8F2UD6+VHIA= +golang.org/x/text v0.29.0 h1:1neNs90w9YzJ9BocxfsQNHKuAT4pkghyXc4nhZ6sJvk= +golang.org/x/text v0.29.0/go.mod h1:7MhJOA9CD2qZyOKYazxdYMF85OwPdEr9jTtBpO7ydH4= golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= golang.org/x/tools v0.0.0-20191119224855-298f0cb1881e/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= -golang.org/x/tools v0.0.0-20200324003944-a576cf524670/go.mod h1:Sl4aGygMT6LrqrWclx+PTx3U+LnKx/seiNR+3G19Ar8= golang.org/x/tools v0.0.0-20200329025819-fd4102a86c65/go.mod h1:Sl4aGygMT6LrqrWclx+PTx3U+LnKx/seiNR+3G19Ar8= golang.org/x/tools v0.0.0-20200724022722-7017fd6b1305/go.mod h1:njjCfa9FT2d7l9Bc6FUM5FLjQPp3cFF28FI3qnDFljA= -golang.org/x/tools v0.0.0-20200820010801-b793a1359eac/go.mod h1:njjCfa9FT2d7l9Bc6FUM5FLjQPp3cFF28FI3qnDFljA= -golang.org/x/tools v0.0.0-20201023174141-c8cfbd0f21e6/go.mod h1:emZCQorbCU4vsT4fOWvOPXz4eW1wZW4PmDk9uLelYpA= golang.org/x/tools v0.1.1-0.20210205202024-ef80cdb6ec6d/go.mod h1:9bzcO0MWcOuT0tm1iBGzDVPshzfwoVvREIui8C+MHqU= golang.org/x/tools v0.1.1-0.20210302220138-2ac05c832e1a/go.mod h1:9bzcO0MWcOuT0tm1iBGzDVPshzfwoVvREIui8C+MHqU= golang.org/x/tools v0.1.1/go.mod h1:o0xws9oXOQQZyjljx8fwUC0k7L1pTE6eaCbjGeHmOkk= -golang.org/x/tools v0.1.5/go.mod h1:o0xws9oXOQQZyjljx8fwUC0k7L1pTE6eaCbjGeHmOkk= golang.org/x/tools v0.1.10/go.mod h1:Uh6Zz+xoGYZom868N8YTex3t7RhtHDBrE8Gzo9bV56E= golang.org/x/tools v0.1.12/go.mod h1:hNGJHUnrk76NpqgfD5Aqm5Crs+Hm0VOH/i9J2+nxYbc= -golang.org/x/tools v0.3.0/go.mod h1:/rWhSS2+zyEVwoJf8YAX6L2f0ntZ7Kn/mGgAWcipA5k= golang.org/x/tools v0.6.0/go.mod h1:Xwgl3UAJ/d3gWutnCtw505GrjyAbvKui8lOU390QaIU= -golang.org/x/tools v0.7.0/go.mod h1:4pg6aUX35JBAogB10C9AtvVL+qowtN4pT3CGSQex14s= golang.org/x/tools v0.13.0/go.mod h1:HvlwmtVNQAhOuCjW7xxvovg8wbNq7LwfXh/k7wXUl58= golang.org/x/tools v0.14.0/go.mod h1:uYBEerGOWcJyEORxN+Ek8+TT266gXkNlHdJBwexUsBg= -golang.org/x/tools v0.33.0 h1:4qz2S3zmRxbGIhDIAgjxvFutSvH5EfnsYrRBj0UI0bc= -golang.org/x/tools v0.33.0/go.mod h1:CIJMaWEY88juyUfo7UbgPqbC8rU2OqfAV1h2Qp0oMYI= +golang.org/x/tools v0.37.0 h1:DVSRzp7FwePZW356yEAChSdNcQo6Nsp+fex1SUW09lE= +golang.org/x/tools v0.37.0/go.mod h1:MBN5QPQtLMHVdvsbtarmTNukZDdgwdwlO5qGacAzF0w= +golang.org/x/tools/go/expect v0.1.1-deprecated h1:jpBZDwmgPhXsKZC6WhL20P4b/wmnpsEAGHaNy0n/rJM= +golang.org/x/tools/go/expect v0.1.1-deprecated/go.mod h1:eihoPOH+FgIqa3FpoTwguz/bVUSGBlGQU67vpBeOrBY= +golang.org/x/tools/go/packages/packagestest v0.1.1-deprecated h1:1h2MnaIAIXISqTFKdENegdpAgUXz6NrPEsbIeWaBRvM= +golang.org/x/tools/go/packages/packagestest v0.1.1-deprecated/go.mod h1:RVAQXBGNv1ib0J382/DPCRS/BPnsGebyM1Gj5VSDpG8= golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= golang.org/x/xerrors v0.0.0-20191011141410-1b5146add898/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= @@ -574,14 +576,13 @@ gopkg.in/check.v1 v1.0.0-20190902080502-41f04d3bba15/go.mod h1:Co6ibVJAznAaIkqp8 gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c h1:Hei/4ADfdWqJk1ZMxUNpqntNwaWcugrBjAiHlqqRiVk= gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c/go.mod h1:JHkPIbrfpd72SG/EVd6muEfDQjcINNoR0C8j2r3qZ4Q= gopkg.in/yaml.v2 v2.2.2/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= -gopkg.in/yaml.v2 v2.4.0 h1:D8xgwECY7CYvx+Y2n4sBz93Jn9JRvxdiyyo8CTfuKaY= gopkg.in/yaml.v2 v2.4.0/go.mod h1:RDklbk79AGWmwhnvt/jBztapEOGDOx6ZbXqjP6csGnQ= gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA= gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= honnef.co/go/tools v0.6.1 h1:R094WgE8K4JirYjBaOpz/AvTyUu/3wbmAoskKN/pxTI= honnef.co/go/tools v0.6.1/go.mod h1:3puzxxljPCe8RGJX7BIy1plGbxEOZni5mR2aXe3/uk4= -mvdan.cc/gofumpt v0.7.0 h1:bg91ttqXmi9y2xawvkuMXyvAA/1ZGJqYAEGjXuP0JXU= -mvdan.cc/gofumpt v0.7.0/go.mod h1:txVFJy/Sc/mvaycET54pV8SW8gWxTlUuGHVEcncmNUo= +mvdan.cc/gofumpt v0.9.1 h1:p5YT2NfFWsYyTieYgwcQ8aKV3xRvFH4uuN/zB2gBbMQ= +mvdan.cc/gofumpt v0.9.1/go.mod h1:3xYtNemnKiXaTh6R4VtlqDATFwBbdXI8lJvH/4qk7mw= mvdan.cc/unparam v0.0.0-20250301125049-0df0534333a4 h1:WjUu4yQoT5BHT1w8Zu56SP8367OuBV5jvo+4Ulppyf8= mvdan.cc/unparam v0.0.0-20250301125049-0df0534333a4/go.mod h1:rthT7OuvRbaGcd5ginj6dA2oLE7YNlta9qhBNNdCaLE= diff --git a/.citools/src/jb/go.mod b/.citools/src/jb/go.mod index 05208bc103e..3533fc96a13 100644 --- a/.citools/src/jb/go.mod +++ b/.citools/src/jb/go.mod @@ -1,6 +1,6 @@ module jb -go 1.24.5 +go 1.25.2 tool github.com/jsonnet-bundler/jsonnet-bundler/cmd/jb @@ -15,6 +15,6 @@ require ( github.com/pkg/errors v0.9.1 // indirect github.com/pmezard/go-difflib v1.0.1-0.20181226105442-5d4384ee4fb2 // indirect github.com/stretchr/testify v1.10.0 // indirect - golang.org/x/sys v0.33.0 // indirect + golang.org/x/sys v0.36.0 // indirect gopkg.in/alecthomas/kingpin.v2 v2.2.6 // indirect ) diff --git a/.citools/src/jb/go.sum b/.citools/src/jb/go.sum index 90354357864..82c114a77ad 100644 --- a/.citools/src/jb/go.sum +++ b/.citools/src/jb/go.sum @@ -54,8 +54,8 @@ golang.org/x/sys v0.0.0-20210630005230-0f9fa26af87c/go.mod h1:oPkhp1MJrh7nUepCBc golang.org/x/sys v0.0.0-20210927094055-39ccf1dd6fa6/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20220615213510-4f61da869c0c/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.6.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= -golang.org/x/sys v0.33.0 h1:q3i8TbbEz+JRD9ywIRlyRAQbM0qF7hu24q3teo2hbuw= -golang.org/x/sys v0.33.0/go.mod h1:BJP2sWEmIv4KK5OTEluFJCKSidICx8ciO85XgH3Ak8k= +golang.org/x/sys v0.36.0 h1:KVRy2GtZBrk1cBYA7MKu5bEZFxQk4NIDV6RLVcC8o0k= +golang.org/x/sys v0.36.0/go.mod h1:OgkHotnGiDImocRcuBABYBEXf8A9a87e/uXjp9XT3ks= gopkg.in/alecthomas/kingpin.v2 v2.2.6 h1:jMFz6MfLP0/4fUyZle81rXUoxOBFi19VUFKVDOQfozc= gopkg.in/alecthomas/kingpin.v2 v2.2.6/go.mod h1:FMv+mEhP44yOT+4EoQTLFTRgOQ1FBLkstjWtayDeSgw= gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= diff --git a/.citools/src/lefthook/go.mod b/.citools/src/lefthook/go.mod index e45942a7dc1..c24d4836ed7 100644 --- a/.citools/src/lefthook/go.mod +++ b/.citools/src/lefthook/go.mod @@ -1,6 +1,6 @@ module lefthook -go 1.24.5 +go 1.25.2 tool github.com/evilmartians/lefthook @@ -43,10 +43,9 @@ require ( github.com/xo/terminfo v0.0.0-20220910002029-abceb7e1c41e // indirect go.uber.org/multierr v1.11.0 // indirect golang.org/x/exp v0.0.0-20250305212735-054e65f0b394 // indirect - golang.org/x/sys v0.33.0 // indirect - golang.org/x/term v0.32.0 // indirect - golang.org/x/text v0.25.0 // indirect - golang.org/x/tools v0.33.0 // indirect + golang.org/x/sys v0.36.0 // indirect + golang.org/x/term v0.35.0 // indirect + golang.org/x/text v0.29.0 // indirect gopkg.in/alessio/shellescape.v1 v1.0.0-20170105083845-52074bc9df61 // indirect gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c // indirect gopkg.in/yaml.v3 v3.0.1 // indirect diff --git a/.citools/src/lefthook/go.sum b/.citools/src/lefthook/go.sum index b6b9533af75..c0e796f4ae9 100644 --- a/.citools/src/lefthook/go.sum +++ b/.citools/src/lefthook/go.sum @@ -91,14 +91,14 @@ go.uber.org/multierr v1.11.0/go.mod h1:20+QtiLqy0Nd6FdQB9TLXag12DsQkrbs3htMFfDN8 golang.org/x/exp v0.0.0-20250305212735-054e65f0b394 h1:nDVHiLt8aIbd/VzvPWN6kSOPE7+F/fNFDSXLVYkE/Iw= golang.org/x/exp v0.0.0-20250305212735-054e65f0b394/go.mod h1:sIifuuw/Yco/y6yb6+bDNfyeQ/MdPUy/hKEMYQV17cM= golang.org/x/sys v0.6.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= -golang.org/x/sys v0.33.0 h1:q3i8TbbEz+JRD9ywIRlyRAQbM0qF7hu24q3teo2hbuw= -golang.org/x/sys v0.33.0/go.mod h1:BJP2sWEmIv4KK5OTEluFJCKSidICx8ciO85XgH3Ak8k= -golang.org/x/term v0.32.0 h1:DR4lr0TjUs3epypdhTOkMmuF5CDFJ/8pOnbzMZPQ7bg= -golang.org/x/term v0.32.0/go.mod h1:uZG1FhGx848Sqfsq4/DlJr3xGGsYMu/L5GW4abiaEPQ= -golang.org/x/text v0.25.0 h1:qVyWApTSYLk/drJRO5mDlNYskwQznZmkpV2c8q9zls4= -golang.org/x/text v0.25.0/go.mod h1:WEdwpYrmk1qmdHvhkSTNPm3app7v4rsT8F2UD6+VHIA= -golang.org/x/tools v0.33.0 h1:4qz2S3zmRxbGIhDIAgjxvFutSvH5EfnsYrRBj0UI0bc= -golang.org/x/tools v0.33.0/go.mod h1:CIJMaWEY88juyUfo7UbgPqbC8rU2OqfAV1h2Qp0oMYI= +golang.org/x/sys v0.36.0 h1:KVRy2GtZBrk1cBYA7MKu5bEZFxQk4NIDV6RLVcC8o0k= +golang.org/x/sys v0.36.0/go.mod h1:OgkHotnGiDImocRcuBABYBEXf8A9a87e/uXjp9XT3ks= +golang.org/x/term v0.35.0 h1:bZBVKBudEyhRcajGcNc3jIfWPqV4y/Kt2XcoigOWtDQ= +golang.org/x/term v0.35.0/go.mod h1:TPGtkTLesOwf2DE8CgVYiZinHAOuy5AYUYT1lENIZnA= +golang.org/x/text v0.29.0 h1:1neNs90w9YzJ9BocxfsQNHKuAT4pkghyXc4nhZ6sJvk= +golang.org/x/text v0.29.0/go.mod h1:7MhJOA9CD2qZyOKYazxdYMF85OwPdEr9jTtBpO7ydH4= +golang.org/x/tools v0.36.0 h1:kWS0uv/zsvHEle1LbV5LE8QujrxB3wfQyxHfhOk0Qkg= +golang.org/x/tools v0.36.0/go.mod h1:WBDiHKJK8YgLHlcQPYQzNCkUxUypCaa5ZegCVutKm+s= gopkg.in/alessio/shellescape.v1 v1.0.0-20170105083845-52074bc9df61 h1:8ajkpB4hXVftY5ko905id+dOnmorcS2CHNxxHLLDcFM= gopkg.in/alessio/shellescape.v1 v1.0.0-20170105083845-52074bc9df61/go.mod h1:IfMagxm39Ys4ybJrDb7W3Ob8RwxftP0Yy+or/NVz1O8= gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= diff --git a/.citools/src/swagger/go.mod b/.citools/src/swagger/go.mod index 4d169e867f2..17d62a9db09 100644 --- a/.citools/src/swagger/go.mod +++ b/.citools/src/swagger/go.mod @@ -1,6 +1,6 @@ module swagger -go 1.24.5 +go 1.25.2 tool github.com/go-swagger/go-swagger/cmd/swagger @@ -51,12 +51,12 @@ require ( github.com/toqueteos/webbrowser v1.2.0 // indirect go.mongodb.org/mongo-driver v1.16.1 // indirect go.uber.org/multierr v1.11.0 // indirect - golang.org/x/crypto v0.38.0 // indirect - golang.org/x/mod v0.24.0 // indirect - golang.org/x/sync v0.14.0 // indirect - golang.org/x/sys v0.33.0 // indirect - golang.org/x/text v0.25.0 // indirect - golang.org/x/tools v0.33.0 // indirect + golang.org/x/crypto v0.42.0 // indirect + golang.org/x/mod v0.27.0 // indirect + golang.org/x/sync v0.17.0 // indirect + golang.org/x/sys v0.36.0 // indirect + golang.org/x/text v0.29.0 // indirect + golang.org/x/tools v0.36.0 // indirect gopkg.in/yaml.v2 v2.4.0 // indirect gopkg.in/yaml.v3 v3.0.1 // indirect ) diff --git a/.citools/src/swagger/go.sum b/.citools/src/swagger/go.sum index f5342e8f329..dff45a5ee81 100644 --- a/.citools/src/swagger/go.sum +++ b/.citools/src/swagger/go.sum @@ -101,19 +101,19 @@ go.mongodb.org/mongo-driver v1.16.1 h1:rIVLL3q0IHM39dvE+z2ulZLp9ENZKThVfuvN/IiN4 go.mongodb.org/mongo-driver v1.16.1/go.mod h1:oB6AhJQvFQL4LEHyXi6aJzQJtBiTQHiAd83l0GdFaiw= go.uber.org/multierr v1.11.0 h1:blXXJkSxSSfBVBlC76pxqeO+LN3aDfLQo+309xJstO0= go.uber.org/multierr v1.11.0/go.mod h1:20+QtiLqy0Nd6FdQB9TLXag12DsQkrbs3htMFfDN80Y= -golang.org/x/crypto v0.38.0 h1:jt+WWG8IZlBnVbomuhg2Mdq0+BBQaHbtqHEFEigjUV8= -golang.org/x/crypto v0.38.0/go.mod h1:MvrbAqul58NNYPKnOra203SB9vpuZW0e+RRZV+Ggqjw= -golang.org/x/mod v0.24.0 h1:ZfthKaKaT4NrhGVZHO1/WDTwGES4De8KtWO0SIbNJMU= -golang.org/x/mod v0.24.0/go.mod h1:IXM97Txy2VM4PJ3gI61r1YEk/gAj6zAHN3AdZt6S9Ww= -golang.org/x/sync v0.14.0 h1:woo0S4Yywslg6hp4eUFjTVOyKt0RookbpAHG4c1HmhQ= -golang.org/x/sync v0.14.0/go.mod h1:1dzgHSNfp02xaA81J2MS99Qcpr2w7fw1gpm99rleRqA= +golang.org/x/crypto v0.42.0 h1:chiH31gIWm57EkTXpwnqf8qeuMUi0yekh6mT2AvFlqI= +golang.org/x/crypto v0.42.0/go.mod h1:4+rDnOTJhQCx2q7/j6rAN5XDw8kPjeaXEUR2eL94ix8= +golang.org/x/mod v0.27.0 h1:kb+q2PyFnEADO2IEF935ehFUXlWiNjJWtRNgBLSfbxQ= +golang.org/x/mod v0.27.0/go.mod h1:rWI627Fq0DEoudcK+MBkNkCe0EetEaDSwJJkCcjpazc= +golang.org/x/sync v0.17.0 h1:l60nONMj9l5drqw6jlhIELNv9I0A4OFgRsG9k2oT9Ug= +golang.org/x/sync v0.17.0/go.mod h1:9KTHXmSnoGruLpwFjVSX0lNNA75CykiMECbovNTZqGI= golang.org/x/sys v0.0.0-20210320140829-1e4c9ba3b0c4/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.33.0 h1:q3i8TbbEz+JRD9ywIRlyRAQbM0qF7hu24q3teo2hbuw= -golang.org/x/sys v0.33.0/go.mod h1:BJP2sWEmIv4KK5OTEluFJCKSidICx8ciO85XgH3Ak8k= -golang.org/x/text v0.25.0 h1:qVyWApTSYLk/drJRO5mDlNYskwQznZmkpV2c8q9zls4= -golang.org/x/text v0.25.0/go.mod h1:WEdwpYrmk1qmdHvhkSTNPm3app7v4rsT8F2UD6+VHIA= -golang.org/x/tools v0.33.0 h1:4qz2S3zmRxbGIhDIAgjxvFutSvH5EfnsYrRBj0UI0bc= -golang.org/x/tools v0.33.0/go.mod h1:CIJMaWEY88juyUfo7UbgPqbC8rU2OqfAV1h2Qp0oMYI= +golang.org/x/sys v0.36.0 h1:KVRy2GtZBrk1cBYA7MKu5bEZFxQk4NIDV6RLVcC8o0k= +golang.org/x/sys v0.36.0/go.mod h1:OgkHotnGiDImocRcuBABYBEXf8A9a87e/uXjp9XT3ks= +golang.org/x/text v0.29.0 h1:1neNs90w9YzJ9BocxfsQNHKuAT4pkghyXc4nhZ6sJvk= +golang.org/x/text v0.29.0/go.mod h1:7MhJOA9CD2qZyOKYazxdYMF85OwPdEr9jTtBpO7ydH4= +golang.org/x/tools v0.36.0 h1:kWS0uv/zsvHEle1LbV5LE8QujrxB3wfQyxHfhOk0Qkg= +golang.org/x/tools v0.36.0/go.mod h1:WBDiHKJK8YgLHlcQPYQzNCkUxUypCaa5ZegCVutKm+s= gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c h1:Hei/4ADfdWqJk1ZMxUNpqntNwaWcugrBjAiHlqqRiVk= gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c/go.mod h1:JHkPIbrfpd72SG/EVd6muEfDQjcINNoR0C8j2r3qZ4Q= diff --git a/.github/workflows/go-lint.yml b/.github/workflows/go-lint.yml index 28cddc1e87a..cdce15360a4 100644 --- a/.github/workflows/go-lint.yml +++ b/.github/workflows/go-lint.yml @@ -68,7 +68,7 @@ jobs: - name: golangci-lint uses: golangci/golangci-lint-action@1481404843c368bc19ca9406f87d6e0fc97bdcfd with: - version: v2.0.2 + version: v2.5.0 args: | --verbose $(go list -m -f '{{.Dir}}' | xargs -I{} sh -c 'test ! -f {}/.nolint && echo {}/...') install-mode: binary diff --git a/Dockerfile b/Dockerfile index a5121873b10..b1ec3d36c1d 100644 --- a/Dockerfile +++ b/Dockerfile @@ -16,7 +16,7 @@ ARG JS_SRC=js-builder # By using FROM instructions we can delegate dependency updates to dependabot FROM alpine:3.21.3 AS alpine-base FROM ubuntu:22.04 AS ubuntu-base -FROM golang:1.24.6-alpine AS go-builder-base +FROM golang:1.25.2-alpine AS go-builder-base FROM --platform=${JS_PLATFORM} node:22-alpine AS js-builder-base # Javascript build stage FROM --platform=${JS_PLATFORM} ${JS_IMAGE} AS js-builder diff --git a/Makefile b/Makefile index e76e5a18940..5a8b35f73b5 100644 --- a/Makefile +++ b/Makefile @@ -8,7 +8,7 @@ WIRE_TAGS = "oss" include .citools/Variables.mk GO = go -GO_VERSION = 1.24.6 +GO_VERSION = 1.25.2 GO_LINT_FILES ?= $(shell ./scripts/go-workspace/golangci-lint-includes.sh) GO_TEST_FILES ?= $(shell ./scripts/go-workspace/test-includes.sh) SH_FILES ?= $(shell find ./scripts -name *.sh) diff --git a/apps/advisor/go.mod b/apps/advisor/go.mod index 3f21822f5a4..0a080eca7e2 100644 --- a/apps/advisor/go.mod +++ b/apps/advisor/go.mod @@ -1,6 +1,6 @@ module github.com/grafana/grafana/apps/advisor -go 1.24.6 +go 1.25.2 require ( github.com/Masterminds/semver/v3 v3.4.0 diff --git a/apps/alerting/alertenrichment/go.mod b/apps/alerting/alertenrichment/go.mod index 48c93f1aa1e..1fec7e5890a 100644 --- a/apps/alerting/alertenrichment/go.mod +++ b/apps/alerting/alertenrichment/go.mod @@ -1,6 +1,6 @@ module github.com/grafana/grafana/apps/alerting/alertenrichment -go 1.24.6 +go 1.25.2 require ( github.com/grafana/grafana-app-sdk v0.46.0 @@ -26,7 +26,7 @@ require ( github.com/x448/float16 v0.8.4 // indirect go.yaml.in/yaml/v2 v2.4.2 // indirect go.yaml.in/yaml/v3 v3.0.4 // indirect - golang.org/x/net v0.44.0 // indirect + golang.org/x/net v0.45.0 // indirect golang.org/x/text v0.29.0 // indirect google.golang.org/protobuf v1.36.9 // indirect gopkg.in/inf.v0 v0.9.1 // indirect diff --git a/apps/alerting/alertenrichment/go.sum b/apps/alerting/alertenrichment/go.sum index ead26b4a7b0..96dc13e50a2 100644 --- a/apps/alerting/alertenrichment/go.sum +++ b/apps/alerting/alertenrichment/go.sum @@ -71,8 +71,8 @@ golang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3/go.mod h1:t9HGtf8HONx5eT2rtn golang.org/x/net v0.0.0-20190620200207-3b0461eec859/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= golang.org/x/net v0.0.0-20200226121028-0de0cce0169b/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= golang.org/x/net v0.0.0-20201021035429-f5854403a974/go.mod h1:sp8m0HH+o8qH0wwXwYZr8TS3Oi6o0r6Gce1SSxlDquU= -golang.org/x/net v0.44.0 h1:evd8IRDyfNBMBTTY5XRF1vaZlD+EmWx6x8PkhR04H/I= -golang.org/x/net v0.44.0/go.mod h1:ECOoLqd5U3Lhyeyo/QDCEVQ4sNgYsqvCZ722XogGieY= +golang.org/x/net v0.45.0 h1:RLBg5JKixCy82FtLJpeNlVM0nrSqpCRYzVU1n8kj0tM= +golang.org/x/net v0.45.0/go.mod h1:ECOoLqd5U3Lhyeyo/QDCEVQ4sNgYsqvCZ722XogGieY= golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.0.0-20190911185100-cd5d95a43a6e/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.0.0-20201020160332-67f06af15bc9/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= diff --git a/apps/alerting/notifications/go.mod b/apps/alerting/notifications/go.mod index f811c12d2f8..7f2690bae14 100644 --- a/apps/alerting/notifications/go.mod +++ b/apps/alerting/notifications/go.mod @@ -1,6 +1,6 @@ module github.com/grafana/grafana/apps/alerting/notifications -go 1.24.6 +go 1.25.2 require ( github.com/grafana/grafana-app-sdk v0.46.0 @@ -83,7 +83,7 @@ require ( go.uber.org/zap v1.27.0 // indirect go.yaml.in/yaml/v2 v2.4.2 // indirect go.yaml.in/yaml/v3 v3.0.4 // indirect - golang.org/x/net v0.44.0 // indirect + golang.org/x/net v0.45.0 // indirect golang.org/x/oauth2 v0.30.0 // indirect golang.org/x/sync v0.17.0 // indirect golang.org/x/sys v0.36.0 // indirect diff --git a/apps/alerting/notifications/go.sum b/apps/alerting/notifications/go.sum index 9166be2eae7..b613313ed23 100644 --- a/apps/alerting/notifications/go.sum +++ b/apps/alerting/notifications/go.sum @@ -234,8 +234,8 @@ golang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3/go.mod h1:t9HGtf8HONx5eT2rtn golang.org/x/net v0.0.0-20190620200207-3b0461eec859/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= golang.org/x/net v0.0.0-20200226121028-0de0cce0169b/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= golang.org/x/net v0.0.0-20201021035429-f5854403a974/go.mod h1:sp8m0HH+o8qH0wwXwYZr8TS3Oi6o0r6Gce1SSxlDquU= -golang.org/x/net v0.44.0 h1:evd8IRDyfNBMBTTY5XRF1vaZlD+EmWx6x8PkhR04H/I= -golang.org/x/net v0.44.0/go.mod h1:ECOoLqd5U3Lhyeyo/QDCEVQ4sNgYsqvCZ722XogGieY= +golang.org/x/net v0.45.0 h1:RLBg5JKixCy82FtLJpeNlVM0nrSqpCRYzVU1n8kj0tM= +golang.org/x/net v0.45.0/go.mod h1:ECOoLqd5U3Lhyeyo/QDCEVQ4sNgYsqvCZ722XogGieY= golang.org/x/oauth2 v0.0.0-20180821212333-d2e6202438be/go.mod h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U= golang.org/x/oauth2 v0.30.0 h1:dnDm7JmhM45NNpd8FDDeLhK6FwqbOf4MLCM9zb1BOHI= golang.org/x/oauth2 v0.30.0/go.mod h1:B++QgG3ZKulg6sRPGD/mqlHQs5rB3Ml9erfeDY7xKlU= diff --git a/apps/alerting/rules/go.mod b/apps/alerting/rules/go.mod index 7678f821f3a..d098e930fe5 100644 --- a/apps/alerting/rules/go.mod +++ b/apps/alerting/rules/go.mod @@ -1,6 +1,6 @@ module github.com/grafana/grafana/apps/alerting/rules -go 1.24.4 +go 1.25.2 require ( github.com/grafana/grafana-app-sdk v0.46.0 @@ -66,7 +66,7 @@ require ( go.opentelemetry.io/proto/otlp v1.7.1 // indirect go.yaml.in/yaml/v2 v2.4.2 // indirect go.yaml.in/yaml/v3 v3.0.4 // indirect - golang.org/x/net v0.44.0 // indirect + golang.org/x/net v0.45.0 // indirect golang.org/x/oauth2 v0.30.0 // indirect golang.org/x/sync v0.17.0 // indirect golang.org/x/sys v0.36.0 // indirect diff --git a/apps/alerting/rules/go.sum b/apps/alerting/rules/go.sum index dadd72070fd..ff48e3193e0 100644 --- a/apps/alerting/rules/go.sum +++ b/apps/alerting/rules/go.sum @@ -161,8 +161,8 @@ golang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3/go.mod h1:t9HGtf8HONx5eT2rtn golang.org/x/net v0.0.0-20190620200207-3b0461eec859/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= golang.org/x/net v0.0.0-20200226121028-0de0cce0169b/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= golang.org/x/net v0.0.0-20201021035429-f5854403a974/go.mod h1:sp8m0HH+o8qH0wwXwYZr8TS3Oi6o0r6Gce1SSxlDquU= -golang.org/x/net v0.44.0 h1:evd8IRDyfNBMBTTY5XRF1vaZlD+EmWx6x8PkhR04H/I= -golang.org/x/net v0.44.0/go.mod h1:ECOoLqd5U3Lhyeyo/QDCEVQ4sNgYsqvCZ722XogGieY= +golang.org/x/net v0.45.0 h1:RLBg5JKixCy82FtLJpeNlVM0nrSqpCRYzVU1n8kj0tM= +golang.org/x/net v0.45.0/go.mod h1:ECOoLqd5U3Lhyeyo/QDCEVQ4sNgYsqvCZ722XogGieY= golang.org/x/oauth2 v0.30.0 h1:dnDm7JmhM45NNpd8FDDeLhK6FwqbOf4MLCM9zb1BOHI= golang.org/x/oauth2 v0.30.0/go.mod h1:B++QgG3ZKulg6sRPGD/mqlHQs5rB3Ml9erfeDY7xKlU= golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= diff --git a/apps/correlations/go.mod b/apps/correlations/go.mod index 2f6538b879d..990bcd8ead6 100644 --- a/apps/correlations/go.mod +++ b/apps/correlations/go.mod @@ -1,8 +1,6 @@ module github.com/grafana/grafana/apps/correlations -go 1.24.0 - -toolchain go1.24.6 +go 1.25.2 require ( github.com/grafana/grafana-app-sdk v0.46.0 @@ -68,7 +66,7 @@ require ( go.opentelemetry.io/proto/otlp v1.7.1 // indirect go.yaml.in/yaml/v2 v2.4.2 // indirect go.yaml.in/yaml/v3 v3.0.4 // indirect - golang.org/x/net v0.44.0 // indirect + golang.org/x/net v0.45.0 // indirect golang.org/x/oauth2 v0.30.0 // indirect golang.org/x/sync v0.17.0 // indirect golang.org/x/sys v0.36.0 // indirect diff --git a/apps/correlations/go.sum b/apps/correlations/go.sum index dadd72070fd..ff48e3193e0 100644 --- a/apps/correlations/go.sum +++ b/apps/correlations/go.sum @@ -161,8 +161,8 @@ golang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3/go.mod h1:t9HGtf8HONx5eT2rtn golang.org/x/net v0.0.0-20190620200207-3b0461eec859/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= golang.org/x/net v0.0.0-20200226121028-0de0cce0169b/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= golang.org/x/net v0.0.0-20201021035429-f5854403a974/go.mod h1:sp8m0HH+o8qH0wwXwYZr8TS3Oi6o0r6Gce1SSxlDquU= -golang.org/x/net v0.44.0 h1:evd8IRDyfNBMBTTY5XRF1vaZlD+EmWx6x8PkhR04H/I= -golang.org/x/net v0.44.0/go.mod h1:ECOoLqd5U3Lhyeyo/QDCEVQ4sNgYsqvCZ722XogGieY= +golang.org/x/net v0.45.0 h1:RLBg5JKixCy82FtLJpeNlVM0nrSqpCRYzVU1n8kj0tM= +golang.org/x/net v0.45.0/go.mod h1:ECOoLqd5U3Lhyeyo/QDCEVQ4sNgYsqvCZ722XogGieY= golang.org/x/oauth2 v0.30.0 h1:dnDm7JmhM45NNpd8FDDeLhK6FwqbOf4MLCM9zb1BOHI= golang.org/x/oauth2 v0.30.0/go.mod h1:B++QgG3ZKulg6sRPGD/mqlHQs5rB3Ml9erfeDY7xKlU= golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= diff --git a/apps/dashboard/go.mod b/apps/dashboard/go.mod index adfae61e8d1..26c53cd3ee3 100644 --- a/apps/dashboard/go.mod +++ b/apps/dashboard/go.mod @@ -1,6 +1,6 @@ module github.com/grafana/grafana/apps/dashboard -go 1.24.6 +go 1.25.2 require ( cuelang.org/go v0.11.1 @@ -11,7 +11,7 @@ require ( github.com/grafana/grafana/pkg/apimachinery v0.0.0-20250514132646-acbc7b54ed9e github.com/prometheus/client_golang v1.23.2 github.com/stretchr/testify v1.11.1 - golang.org/x/net v0.44.0 + golang.org/x/net v0.45.0 k8s.io/apimachinery v0.34.1 k8s.io/apiserver v0.34.1 k8s.io/kube-openapi v0.0.0-20250710124328-f3f2b991d03b diff --git a/apps/dashboard/go.sum b/apps/dashboard/go.sum index 4358d7d815a..9f18f73b002 100644 --- a/apps/dashboard/go.sum +++ b/apps/dashboard/go.sum @@ -334,8 +334,8 @@ golang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3/go.mod h1:t9HGtf8HONx5eT2rtn golang.org/x/net v0.0.0-20190620200207-3b0461eec859/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= golang.org/x/net v0.0.0-20200226121028-0de0cce0169b/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= golang.org/x/net v0.0.0-20201021035429-f5854403a974/go.mod h1:sp8m0HH+o8qH0wwXwYZr8TS3Oi6o0r6Gce1SSxlDquU= -golang.org/x/net v0.44.0 h1:evd8IRDyfNBMBTTY5XRF1vaZlD+EmWx6x8PkhR04H/I= -golang.org/x/net v0.44.0/go.mod h1:ECOoLqd5U3Lhyeyo/QDCEVQ4sNgYsqvCZ722XogGieY= +golang.org/x/net v0.45.0 h1:RLBg5JKixCy82FtLJpeNlVM0nrSqpCRYzVU1n8kj0tM= +golang.org/x/net v0.45.0/go.mod h1:ECOoLqd5U3Lhyeyo/QDCEVQ4sNgYsqvCZ722XogGieY= golang.org/x/oauth2 v0.30.0 h1:dnDm7JmhM45NNpd8FDDeLhK6FwqbOf4MLCM9zb1BOHI= golang.org/x/oauth2 v0.30.0/go.mod h1:B++QgG3ZKulg6sRPGD/mqlHQs5rB3Ml9erfeDY7xKlU= golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= diff --git a/apps/folder/go.mod b/apps/folder/go.mod index 6db44655eeb..681428f2cb5 100644 --- a/apps/folder/go.mod +++ b/apps/folder/go.mod @@ -1,6 +1,6 @@ module github.com/grafana/grafana/apps/folder -go 1.24.6 +go 1.25.2 require ( github.com/grafana/grafana-app-sdk v0.46.0 @@ -49,7 +49,7 @@ require ( go.opentelemetry.io/otel/trace v1.38.0 // indirect go.yaml.in/yaml/v2 v2.4.2 // indirect go.yaml.in/yaml/v3 v3.0.4 // indirect - golang.org/x/net v0.44.0 // indirect + golang.org/x/net v0.45.0 // indirect golang.org/x/oauth2 v0.30.0 // indirect golang.org/x/sys v0.36.0 // indirect golang.org/x/term v0.35.0 // indirect diff --git a/apps/folder/go.sum b/apps/folder/go.sum index ab3cbcecbb1..68909c968ec 100644 --- a/apps/folder/go.sum +++ b/apps/folder/go.sum @@ -124,8 +124,8 @@ golang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3/go.mod h1:t9HGtf8HONx5eT2rtn golang.org/x/net v0.0.0-20190620200207-3b0461eec859/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= golang.org/x/net v0.0.0-20200226121028-0de0cce0169b/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= golang.org/x/net v0.0.0-20201021035429-f5854403a974/go.mod h1:sp8m0HH+o8qH0wwXwYZr8TS3Oi6o0r6Gce1SSxlDquU= -golang.org/x/net v0.44.0 h1:evd8IRDyfNBMBTTY5XRF1vaZlD+EmWx6x8PkhR04H/I= -golang.org/x/net v0.44.0/go.mod h1:ECOoLqd5U3Lhyeyo/QDCEVQ4sNgYsqvCZ722XogGieY= +golang.org/x/net v0.45.0 h1:RLBg5JKixCy82FtLJpeNlVM0nrSqpCRYzVU1n8kj0tM= +golang.org/x/net v0.45.0/go.mod h1:ECOoLqd5U3Lhyeyo/QDCEVQ4sNgYsqvCZ722XogGieY= golang.org/x/oauth2 v0.30.0 h1:dnDm7JmhM45NNpd8FDDeLhK6FwqbOf4MLCM9zb1BOHI= golang.org/x/oauth2 v0.30.0/go.mod h1:B++QgG3ZKulg6sRPGD/mqlHQs5rB3Ml9erfeDY7xKlU= golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= diff --git a/apps/iam/go.mod b/apps/iam/go.mod index 30fa2ce8562..b8d572ae542 100644 --- a/apps/iam/go.mod +++ b/apps/iam/go.mod @@ -1,6 +1,6 @@ module github.com/grafana/grafana/apps/iam -go 1.24.6 +go 1.25.2 replace github.com/grafana/grafana => ../../ @@ -405,7 +405,7 @@ require ( golang.org/x/crypto v0.42.0 // indirect golang.org/x/exp v0.0.0-20250911091902-df9299821621 // indirect golang.org/x/mod v0.28.0 // indirect - golang.org/x/net v0.44.0 // indirect + golang.org/x/net v0.45.0 // indirect golang.org/x/oauth2 v0.30.0 // indirect golang.org/x/sync v0.17.0 // indirect golang.org/x/sys v0.36.0 // indirect diff --git a/apps/iam/go.sum b/apps/iam/go.sum index 8761accef79..685e0ec1133 100644 --- a/apps/iam/go.sum +++ b/apps/iam/go.sum @@ -1567,8 +1567,8 @@ golang.org/x/net v0.0.0-20220325170049-de3da57026de/go.mod h1:CfG3xpIq0wQ8r1q4Su golang.org/x/net v0.0.0-20220412020605-290c469a71a5/go.mod h1:CfG3xpIq0wQ8r1q4Su4UZFWDARRcnwPjda9FqA0JpMk= golang.org/x/net v0.0.0-20220425223048-2871e0cb64e4/go.mod h1:CfG3xpIq0wQ8r1q4Su4UZFWDARRcnwPjda9FqA0JpMk= golang.org/x/net v0.0.0-20220520000938-2e3eb7b945c2/go.mod h1:CfG3xpIq0wQ8r1q4Su4UZFWDARRcnwPjda9FqA0JpMk= -golang.org/x/net v0.44.0 h1:evd8IRDyfNBMBTTY5XRF1vaZlD+EmWx6x8PkhR04H/I= -golang.org/x/net v0.44.0/go.mod h1:ECOoLqd5U3Lhyeyo/QDCEVQ4sNgYsqvCZ722XogGieY= +golang.org/x/net v0.45.0 h1:RLBg5JKixCy82FtLJpeNlVM0nrSqpCRYzVU1n8kj0tM= +golang.org/x/net v0.45.0/go.mod h1:ECOoLqd5U3Lhyeyo/QDCEVQ4sNgYsqvCZ722XogGieY= golang.org/x/oauth2 v0.0.0-20180821212333-d2e6202438be/go.mod h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U= golang.org/x/oauth2 v0.0.0-20181106182150-f42d05182288/go.mod h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U= golang.org/x/oauth2 v0.0.0-20190226205417-e64efc72b421/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw= diff --git a/apps/iam/local/Dockerfile b/apps/iam/local/Dockerfile index 28df71c2384..be918c62817 100644 --- a/apps/iam/local/Dockerfile +++ b/apps/iam/local/Dockerfile @@ -1,5 +1,5 @@ # Build stage -FROM golang:1.24.6-alpine AS builder +FROM golang:1.25.2-alpine AS builder # Set working directory WORKDIR /app diff --git a/apps/investigations/go.mod b/apps/investigations/go.mod index 1652941eba8..de188adf52c 100644 --- a/apps/investigations/go.mod +++ b/apps/investigations/go.mod @@ -1,6 +1,6 @@ module github.com/grafana/grafana/apps/investigations -go 1.24.6 +go 1.25.2 require ( github.com/grafana/grafana-app-sdk v0.46.0 @@ -67,7 +67,7 @@ require ( go.opentelemetry.io/proto/otlp v1.7.1 // indirect go.yaml.in/yaml/v2 v2.4.2 // indirect go.yaml.in/yaml/v3 v3.0.4 // indirect - golang.org/x/net v0.44.0 // indirect + golang.org/x/net v0.45.0 // indirect golang.org/x/oauth2 v0.30.0 // indirect golang.org/x/sync v0.17.0 // indirect golang.org/x/sys v0.36.0 // indirect diff --git a/apps/investigations/go.sum b/apps/investigations/go.sum index dadd72070fd..ff48e3193e0 100644 --- a/apps/investigations/go.sum +++ b/apps/investigations/go.sum @@ -161,8 +161,8 @@ golang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3/go.mod h1:t9HGtf8HONx5eT2rtn golang.org/x/net v0.0.0-20190620200207-3b0461eec859/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= golang.org/x/net v0.0.0-20200226121028-0de0cce0169b/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= golang.org/x/net v0.0.0-20201021035429-f5854403a974/go.mod h1:sp8m0HH+o8qH0wwXwYZr8TS3Oi6o0r6Gce1SSxlDquU= -golang.org/x/net v0.44.0 h1:evd8IRDyfNBMBTTY5XRF1vaZlD+EmWx6x8PkhR04H/I= -golang.org/x/net v0.44.0/go.mod h1:ECOoLqd5U3Lhyeyo/QDCEVQ4sNgYsqvCZ722XogGieY= +golang.org/x/net v0.45.0 h1:RLBg5JKixCy82FtLJpeNlVM0nrSqpCRYzVU1n8kj0tM= +golang.org/x/net v0.45.0/go.mod h1:ECOoLqd5U3Lhyeyo/QDCEVQ4sNgYsqvCZ722XogGieY= golang.org/x/oauth2 v0.30.0 h1:dnDm7JmhM45NNpd8FDDeLhK6FwqbOf4MLCM9zb1BOHI= golang.org/x/oauth2 v0.30.0/go.mod h1:B++QgG3ZKulg6sRPGD/mqlHQs5rB3Ml9erfeDY7xKlU= golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= diff --git a/apps/playlist/go.mod b/apps/playlist/go.mod index 4419785540a..de98ec73e19 100644 --- a/apps/playlist/go.mod +++ b/apps/playlist/go.mod @@ -1,6 +1,6 @@ module github.com/grafana/grafana/apps/playlist -go 1.24.6 +go 1.25.2 require ( github.com/grafana/grafana-app-sdk v0.46.0 @@ -68,7 +68,7 @@ require ( go.opentelemetry.io/proto/otlp v1.7.1 // indirect go.yaml.in/yaml/v2 v2.4.2 // indirect go.yaml.in/yaml/v3 v3.0.4 // indirect - golang.org/x/net v0.44.0 // indirect + golang.org/x/net v0.45.0 // indirect golang.org/x/oauth2 v0.30.0 // indirect golang.org/x/sync v0.17.0 // indirect golang.org/x/sys v0.36.0 // indirect diff --git a/apps/playlist/go.sum b/apps/playlist/go.sum index dadd72070fd..ff48e3193e0 100644 --- a/apps/playlist/go.sum +++ b/apps/playlist/go.sum @@ -161,8 +161,8 @@ golang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3/go.mod h1:t9HGtf8HONx5eT2rtn golang.org/x/net v0.0.0-20190620200207-3b0461eec859/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= golang.org/x/net v0.0.0-20200226121028-0de0cce0169b/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= golang.org/x/net v0.0.0-20201021035429-f5854403a974/go.mod h1:sp8m0HH+o8qH0wwXwYZr8TS3Oi6o0r6Gce1SSxlDquU= -golang.org/x/net v0.44.0 h1:evd8IRDyfNBMBTTY5XRF1vaZlD+EmWx6x8PkhR04H/I= -golang.org/x/net v0.44.0/go.mod h1:ECOoLqd5U3Lhyeyo/QDCEVQ4sNgYsqvCZ722XogGieY= +golang.org/x/net v0.45.0 h1:RLBg5JKixCy82FtLJpeNlVM0nrSqpCRYzVU1n8kj0tM= +golang.org/x/net v0.45.0/go.mod h1:ECOoLqd5U3Lhyeyo/QDCEVQ4sNgYsqvCZ722XogGieY= golang.org/x/oauth2 v0.30.0 h1:dnDm7JmhM45NNpd8FDDeLhK6FwqbOf4MLCM9zb1BOHI= golang.org/x/oauth2 v0.30.0/go.mod h1:B++QgG3ZKulg6sRPGD/mqlHQs5rB3Ml9erfeDY7xKlU= golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= diff --git a/apps/plugins/go.mod b/apps/plugins/go.mod index 35ed57d995b..45c6fc9cbe0 100644 --- a/apps/plugins/go.mod +++ b/apps/plugins/go.mod @@ -1,6 +1,6 @@ module github.com/grafana/grafana/apps/plugins -go 1.24.4 +go 1.25.2 require ( github.com/grafana/authlib/types v0.0.0-20250926065801-df98203cff37 @@ -75,7 +75,7 @@ require ( go.yaml.in/yaml/v2 v2.4.2 // indirect go.yaml.in/yaml/v3 v3.0.4 // indirect golang.org/x/crypto v0.42.0 // indirect - golang.org/x/net v0.44.0 // indirect + golang.org/x/net v0.45.0 // indirect golang.org/x/oauth2 v0.30.0 // indirect golang.org/x/sync v0.17.0 // indirect golang.org/x/sys v0.36.0 // indirect diff --git a/apps/plugins/go.sum b/apps/plugins/go.sum index ca929862af5..16411aeecb3 100644 --- a/apps/plugins/go.sum +++ b/apps/plugins/go.sum @@ -177,8 +177,8 @@ golang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3/go.mod h1:t9HGtf8HONx5eT2rtn golang.org/x/net v0.0.0-20190620200207-3b0461eec859/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= golang.org/x/net v0.0.0-20200226121028-0de0cce0169b/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= golang.org/x/net v0.0.0-20201021035429-f5854403a974/go.mod h1:sp8m0HH+o8qH0wwXwYZr8TS3Oi6o0r6Gce1SSxlDquU= -golang.org/x/net v0.44.0 h1:evd8IRDyfNBMBTTY5XRF1vaZlD+EmWx6x8PkhR04H/I= -golang.org/x/net v0.44.0/go.mod h1:ECOoLqd5U3Lhyeyo/QDCEVQ4sNgYsqvCZ722XogGieY= +golang.org/x/net v0.45.0 h1:RLBg5JKixCy82FtLJpeNlVM0nrSqpCRYzVU1n8kj0tM= +golang.org/x/net v0.45.0/go.mod h1:ECOoLqd5U3Lhyeyo/QDCEVQ4sNgYsqvCZ722XogGieY= golang.org/x/oauth2 v0.30.0 h1:dnDm7JmhM45NNpd8FDDeLhK6FwqbOf4MLCM9zb1BOHI= golang.org/x/oauth2 v0.30.0/go.mod h1:B++QgG3ZKulg6sRPGD/mqlHQs5rB3Ml9erfeDY7xKlU= golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= diff --git a/apps/preferences/go.mod b/apps/preferences/go.mod index 3c4b1af454b..2036e163b6a 100644 --- a/apps/preferences/go.mod +++ b/apps/preferences/go.mod @@ -1,6 +1,6 @@ module github.com/grafana/grafana/apps/preferences -go 1.24.6 +go 1.25.2 require ( github.com/grafana/grafana-app-sdk v0.46.0 @@ -49,7 +49,7 @@ require ( go.opentelemetry.io/otel/trace v1.38.0 // indirect go.yaml.in/yaml/v2 v2.4.2 // indirect go.yaml.in/yaml/v3 v3.0.4 // indirect - golang.org/x/net v0.44.0 // indirect + golang.org/x/net v0.45.0 // indirect golang.org/x/oauth2 v0.30.0 // indirect golang.org/x/sys v0.36.0 // indirect golang.org/x/term v0.35.0 // indirect diff --git a/apps/preferences/go.sum b/apps/preferences/go.sum index 112d7cb18ab..0bca000ff09 100644 --- a/apps/preferences/go.sum +++ b/apps/preferences/go.sum @@ -124,8 +124,8 @@ golang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3/go.mod h1:t9HGtf8HONx5eT2rtn golang.org/x/net v0.0.0-20190620200207-3b0461eec859/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= golang.org/x/net v0.0.0-20200226121028-0de0cce0169b/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= golang.org/x/net v0.0.0-20201021035429-f5854403a974/go.mod h1:sp8m0HH+o8qH0wwXwYZr8TS3Oi6o0r6Gce1SSxlDquU= -golang.org/x/net v0.44.0 h1:evd8IRDyfNBMBTTY5XRF1vaZlD+EmWx6x8PkhR04H/I= -golang.org/x/net v0.44.0/go.mod h1:ECOoLqd5U3Lhyeyo/QDCEVQ4sNgYsqvCZ722XogGieY= +golang.org/x/net v0.45.0 h1:RLBg5JKixCy82FtLJpeNlVM0nrSqpCRYzVU1n8kj0tM= +golang.org/x/net v0.45.0/go.mod h1:ECOoLqd5U3Lhyeyo/QDCEVQ4sNgYsqvCZ722XogGieY= golang.org/x/oauth2 v0.30.0 h1:dnDm7JmhM45NNpd8FDDeLhK6FwqbOf4MLCM9zb1BOHI= golang.org/x/oauth2 v0.30.0/go.mod h1:B++QgG3ZKulg6sRPGD/mqlHQs5rB3Ml9erfeDY7xKlU= golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= diff --git a/apps/provisioning/go.mod b/apps/provisioning/go.mod index 9a3cd99c066..f8a84dca296 100644 --- a/apps/provisioning/go.mod +++ b/apps/provisioning/go.mod @@ -1,6 +1,6 @@ module github.com/grafana/grafana/apps/provisioning -go 1.24.6 +go 1.25.2 require ( github.com/google/go-github/v70 v70.0.0 @@ -67,7 +67,7 @@ require ( go.yaml.in/yaml/v2 v2.4.2 // indirect go.yaml.in/yaml/v3 v3.0.4 // indirect golang.org/x/crypto v0.42.0 // indirect - golang.org/x/net v0.44.0 // indirect + golang.org/x/net v0.45.0 // indirect golang.org/x/sync v0.17.0 // indirect golang.org/x/sys v0.36.0 // indirect golang.org/x/term v0.35.0 // indirect diff --git a/apps/provisioning/go.sum b/apps/provisioning/go.sum index 93819002c42..a2f85bf8eda 100644 --- a/apps/provisioning/go.sum +++ b/apps/provisioning/go.sum @@ -166,8 +166,8 @@ golang.org/x/net v0.0.0-20210226172049-e18ecbb05110/go.mod h1:m0MpNAwzfU5UDzcl9v golang.org/x/net v0.0.0-20220722155237-a158d28d115b/go.mod h1:XRhObCWvk6IyKnWLug+ECip1KBveYUHfp+8e9klMJ9c= golang.org/x/net v0.6.0/go.mod h1:2Tu9+aMcznHK/AK1HMvgo6xiTLG5rD5rZLDS+rp2Bjs= golang.org/x/net v0.10.0/go.mod h1:0qNGK6F8kojg2nk9dLZ2mShWaEBan6FAoqfSigmmuDg= -golang.org/x/net v0.44.0 h1:evd8IRDyfNBMBTTY5XRF1vaZlD+EmWx6x8PkhR04H/I= -golang.org/x/net v0.44.0/go.mod h1:ECOoLqd5U3Lhyeyo/QDCEVQ4sNgYsqvCZ722XogGieY= +golang.org/x/net v0.45.0 h1:RLBg5JKixCy82FtLJpeNlVM0nrSqpCRYzVU1n8kj0tM= +golang.org/x/net v0.45.0/go.mod h1:ECOoLqd5U3Lhyeyo/QDCEVQ4sNgYsqvCZ722XogGieY= golang.org/x/oauth2 v0.30.0 h1:dnDm7JmhM45NNpd8FDDeLhK6FwqbOf4MLCM9zb1BOHI= golang.org/x/oauth2 v0.30.0/go.mod h1:B++QgG3ZKulg6sRPGD/mqlHQs5rB3Ml9erfeDY7xKlU= golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= diff --git a/apps/scope/go.mod b/apps/scope/go.mod index 435e401adba..d3fc97ccaef 100644 --- a/apps/scope/go.mod +++ b/apps/scope/go.mod @@ -1,6 +1,6 @@ module github.com/grafana/grafana/apps/scope -go 1.24.6 +go 1.25.2 require ( github.com/grafana/grafana/pkg/apimachinery v0.0.0-20251007081214-26e147d01f0a @@ -29,7 +29,7 @@ require ( github.com/x448/float16 v0.8.4 // indirect go.yaml.in/yaml/v2 v2.4.2 // indirect go.yaml.in/yaml/v3 v3.0.4 // indirect - golang.org/x/net v0.44.0 // indirect + golang.org/x/net v0.45.0 // indirect golang.org/x/text v0.29.0 // indirect google.golang.org/protobuf v1.36.9 // indirect gopkg.in/inf.v0 v0.9.1 // indirect diff --git a/apps/scope/go.sum b/apps/scope/go.sum index 25bae6f532a..7bf603654c8 100644 --- a/apps/scope/go.sum +++ b/apps/scope/go.sum @@ -71,8 +71,8 @@ golang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3/go.mod h1:t9HGtf8HONx5eT2rtn golang.org/x/net v0.0.0-20190620200207-3b0461eec859/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= golang.org/x/net v0.0.0-20200226121028-0de0cce0169b/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= golang.org/x/net v0.0.0-20201021035429-f5854403a974/go.mod h1:sp8m0HH+o8qH0wwXwYZr8TS3Oi6o0r6Gce1SSxlDquU= -golang.org/x/net v0.44.0 h1:evd8IRDyfNBMBTTY5XRF1vaZlD+EmWx6x8PkhR04H/I= -golang.org/x/net v0.44.0/go.mod h1:ECOoLqd5U3Lhyeyo/QDCEVQ4sNgYsqvCZ722XogGieY= +golang.org/x/net v0.45.0 h1:RLBg5JKixCy82FtLJpeNlVM0nrSqpCRYzVU1n8kj0tM= +golang.org/x/net v0.45.0/go.mod h1:ECOoLqd5U3Lhyeyo/QDCEVQ4sNgYsqvCZ722XogGieY= golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.0.0-20190911185100-cd5d95a43a6e/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.0.0-20201020160332-67f06af15bc9/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= diff --git a/apps/secret/go.mod b/apps/secret/go.mod index 1a6b9ecc824..a89305b0308 100644 --- a/apps/secret/go.mod +++ b/apps/secret/go.mod @@ -1,6 +1,6 @@ module github.com/grafana/grafana/apps/secret -go 1.24.6 +go 1.25.2 require ( github.com/grafana/grafana-app-sdk v0.46.0 @@ -54,7 +54,7 @@ require ( go.opentelemetry.io/otel/trace v1.38.0 // indirect go.yaml.in/yaml/v2 v2.4.2 // indirect go.yaml.in/yaml/v3 v3.0.4 // indirect - golang.org/x/net v0.44.0 // indirect + golang.org/x/net v0.45.0 // indirect golang.org/x/oauth2 v0.30.0 // indirect golang.org/x/sys v0.36.0 // indirect golang.org/x/term v0.35.0 // indirect diff --git a/apps/secret/go.sum b/apps/secret/go.sum index 712a7a12807..54152dfd80d 100644 --- a/apps/secret/go.sum +++ b/apps/secret/go.sum @@ -136,8 +136,8 @@ golang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3/go.mod h1:t9HGtf8HONx5eT2rtn golang.org/x/net v0.0.0-20190620200207-3b0461eec859/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= golang.org/x/net v0.0.0-20200226121028-0de0cce0169b/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= golang.org/x/net v0.0.0-20201021035429-f5854403a974/go.mod h1:sp8m0HH+o8qH0wwXwYZr8TS3Oi6o0r6Gce1SSxlDquU= -golang.org/x/net v0.44.0 h1:evd8IRDyfNBMBTTY5XRF1vaZlD+EmWx6x8PkhR04H/I= -golang.org/x/net v0.44.0/go.mod h1:ECOoLqd5U3Lhyeyo/QDCEVQ4sNgYsqvCZ722XogGieY= +golang.org/x/net v0.45.0 h1:RLBg5JKixCy82FtLJpeNlVM0nrSqpCRYzVU1n8kj0tM= +golang.org/x/net v0.45.0/go.mod h1:ECOoLqd5U3Lhyeyo/QDCEVQ4sNgYsqvCZ722XogGieY= golang.org/x/oauth2 v0.30.0 h1:dnDm7JmhM45NNpd8FDDeLhK6FwqbOf4MLCM9zb1BOHI= golang.org/x/oauth2 v0.30.0/go.mod h1:B++QgG3ZKulg6sRPGD/mqlHQs5rB3Ml9erfeDY7xKlU= golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= diff --git a/apps/shorturl/go.mod b/apps/shorturl/go.mod index 7eb91409ca0..ef5a60748af 100644 --- a/apps/shorturl/go.mod +++ b/apps/shorturl/go.mod @@ -1,6 +1,6 @@ module github.com/grafana/grafana/apps/shorturl -go 1.24.6 +go 1.25.2 require ( github.com/grafana/grafana-app-sdk v0.46.0 @@ -73,7 +73,7 @@ require ( go.yaml.in/yaml/v2 v2.4.2 // indirect go.yaml.in/yaml/v3 v3.0.4 // indirect golang.org/x/crypto v0.42.0 // indirect - golang.org/x/net v0.44.0 // indirect + golang.org/x/net v0.45.0 // indirect golang.org/x/oauth2 v0.30.0 // indirect golang.org/x/sync v0.17.0 // indirect golang.org/x/sys v0.36.0 // indirect diff --git a/apps/shorturl/go.sum b/apps/shorturl/go.sum index 06c31439b6e..5b7d01fbcd9 100644 --- a/apps/shorturl/go.sum +++ b/apps/shorturl/go.sum @@ -175,8 +175,8 @@ golang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3/go.mod h1:t9HGtf8HONx5eT2rtn golang.org/x/net v0.0.0-20190620200207-3b0461eec859/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= golang.org/x/net v0.0.0-20200226121028-0de0cce0169b/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= golang.org/x/net v0.0.0-20201021035429-f5854403a974/go.mod h1:sp8m0HH+o8qH0wwXwYZr8TS3Oi6o0r6Gce1SSxlDquU= -golang.org/x/net v0.44.0 h1:evd8IRDyfNBMBTTY5XRF1vaZlD+EmWx6x8PkhR04H/I= -golang.org/x/net v0.44.0/go.mod h1:ECOoLqd5U3Lhyeyo/QDCEVQ4sNgYsqvCZ722XogGieY= +golang.org/x/net v0.45.0 h1:RLBg5JKixCy82FtLJpeNlVM0nrSqpCRYzVU1n8kj0tM= +golang.org/x/net v0.45.0/go.mod h1:ECOoLqd5U3Lhyeyo/QDCEVQ4sNgYsqvCZ722XogGieY= golang.org/x/oauth2 v0.30.0 h1:dnDm7JmhM45NNpd8FDDeLhK6FwqbOf4MLCM9zb1BOHI= golang.org/x/oauth2 v0.30.0/go.mod h1:B++QgG3ZKulg6sRPGD/mqlHQs5rB3Ml9erfeDY7xKlU= golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= diff --git a/devenv/docker/blocks/prometheus_high_card/go.mod b/devenv/docker/blocks/prometheus_high_card/go.mod index a6d2b78e072..3a094a9ba37 100644 --- a/devenv/docker/blocks/prometheus_high_card/go.mod +++ b/devenv/docker/blocks/prometheus_high_card/go.mod @@ -1,6 +1,6 @@ module high-card -go 1.24.6 +go 1.25.2 require github.com/prometheus/client_golang v1.22.0 @@ -13,6 +13,6 @@ require ( github.com/prometheus/client_model v0.6.1 // indirect github.com/prometheus/common v0.62.0 // indirect github.com/prometheus/procfs v0.16.1 // indirect - golang.org/x/sys v0.33.0 // indirect + golang.org/x/sys v0.36.0 // indirect google.golang.org/protobuf v1.36.6 // indirect ) diff --git a/devenv/docker/blocks/prometheus_high_card/go.sum b/devenv/docker/blocks/prometheus_high_card/go.sum index ebbd18eaeef..d2e136dfd38 100644 --- a/devenv/docker/blocks/prometheus_high_card/go.sum +++ b/devenv/docker/blocks/prometheus_high_card/go.sum @@ -24,8 +24,8 @@ github.com/prometheus/procfs v0.16.1 h1:hZ15bTNuirocR6u0JZ6BAHHmwS1p8B4P6MRqxtzM github.com/prometheus/procfs v0.16.1/go.mod h1:teAbpZRB1iIAJYREa1LsoWUXykVXA1KlTmWl8x/U+Is= github.com/stretchr/testify v1.10.0 h1:Xv5erBjTwe/5IxqUQTdXv5kgmIvbHo3QQyRwhJsOfJA= github.com/stretchr/testify v1.10.0/go.mod h1:r2ic/lqez/lEtzL7wO/rwa5dbSLXVDPFyf8C91i36aY= -golang.org/x/sys v0.33.0 h1:q3i8TbbEz+JRD9ywIRlyRAQbM0qF7hu24q3teo2hbuw= -golang.org/x/sys v0.33.0/go.mod h1:BJP2sWEmIv4KK5OTEluFJCKSidICx8ciO85XgH3Ak8k= +golang.org/x/sys v0.36.0 h1:KVRy2GtZBrk1cBYA7MKu5bEZFxQk4NIDV6RLVcC8o0k= +golang.org/x/sys v0.36.0/go.mod h1:OgkHotnGiDImocRcuBABYBEXf8A9a87e/uXjp9XT3ks= google.golang.org/protobuf v1.36.6 h1:z1NpPI8ku2WgiWnf+t9wTPsn6eP1L7ksHUlkfLvd9xY= google.golang.org/protobuf v1.36.6/go.mod h1:jduwjTPXsFjZGTmRluh+L6NjiWu7pchiJ2/5YcXBHnY= gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA= diff --git a/devenv/docker/blocks/prometheus_utf8/go.mod b/devenv/docker/blocks/prometheus_utf8/go.mod index 7f7bb1f3b44..ef02ece875b 100644 --- a/devenv/docker/blocks/prometheus_utf8/go.mod +++ b/devenv/docker/blocks/prometheus_utf8/go.mod @@ -1,6 +1,6 @@ module utf8-support -go 1.24.6 +go 1.25.2 require ( github.com/prometheus/client_golang v1.22.0 @@ -15,6 +15,6 @@ require ( github.com/pmezard/go-difflib v1.0.1-0.20181226105442-5d4384ee4fb2 // indirect github.com/prometheus/client_model v0.6.1 // indirect github.com/prometheus/procfs v0.16.1 // indirect - golang.org/x/sys v0.33.0 // indirect + golang.org/x/sys v0.36.0 // indirect google.golang.org/protobuf v1.36.6 // indirect ) diff --git a/devenv/docker/blocks/prometheus_utf8/go.sum b/devenv/docker/blocks/prometheus_utf8/go.sum index ebbd18eaeef..d2e136dfd38 100644 --- a/devenv/docker/blocks/prometheus_utf8/go.sum +++ b/devenv/docker/blocks/prometheus_utf8/go.sum @@ -24,8 +24,8 @@ github.com/prometheus/procfs v0.16.1 h1:hZ15bTNuirocR6u0JZ6BAHHmwS1p8B4P6MRqxtzM github.com/prometheus/procfs v0.16.1/go.mod h1:teAbpZRB1iIAJYREa1LsoWUXykVXA1KlTmWl8x/U+Is= github.com/stretchr/testify v1.10.0 h1:Xv5erBjTwe/5IxqUQTdXv5kgmIvbHo3QQyRwhJsOfJA= github.com/stretchr/testify v1.10.0/go.mod h1:r2ic/lqez/lEtzL7wO/rwa5dbSLXVDPFyf8C91i36aY= -golang.org/x/sys v0.33.0 h1:q3i8TbbEz+JRD9ywIRlyRAQbM0qF7hu24q3teo2hbuw= -golang.org/x/sys v0.33.0/go.mod h1:BJP2sWEmIv4KK5OTEluFJCKSidICx8ciO85XgH3Ak8k= +golang.org/x/sys v0.36.0 h1:KVRy2GtZBrk1cBYA7MKu5bEZFxQk4NIDV6RLVcC8o0k= +golang.org/x/sys v0.36.0/go.mod h1:OgkHotnGiDImocRcuBABYBEXf8A9a87e/uXjp9XT3ks= google.golang.org/protobuf v1.36.6 h1:z1NpPI8ku2WgiWnf+t9wTPsn6eP1L7ksHUlkfLvd9xY= google.golang.org/protobuf v1.36.6/go.mod h1:jduwjTPXsFjZGTmRluh+L6NjiWu7pchiJ2/5YcXBHnY= gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA= diff --git a/devenv/docker/blocks/stateful_webhook/Dockerfile b/devenv/docker/blocks/stateful_webhook/Dockerfile index 258eec3d891..accd1f1a8ca 100644 --- a/devenv/docker/blocks/stateful_webhook/Dockerfile +++ b/devenv/docker/blocks/stateful_webhook/Dockerfile @@ -1,4 +1,4 @@ -FROM golang:1.24.6 +FROM golang:1.25.2 ADD main.go /go/src/webhook/main.go diff --git a/go.mod b/go.mod index 5010002e3a8..bd2255d8002 100644 --- a/go.mod +++ b/go.mod @@ -1,6 +1,6 @@ module github.com/grafana/grafana -go 1.24.6 +go 1.25.2 require ( buf.build/gen/go/parca-dev/parca/connectrpc/go v1.18.1-20250703125925-3f0fcf4bff96.1 // @grafana/observability-traces-and-profiling @@ -202,7 +202,7 @@ require ( golang.org/x/crypto v0.42.0 // @grafana/grafana-backend-group golang.org/x/exp v0.0.0-20250911091902-df9299821621 // @grafana/alerting-backend golang.org/x/mod v0.28.0 // indirect; @grafana/grafana-backend-group - golang.org/x/net v0.44.0 // @grafana/oss-big-tent @grafana/partner-datasources + golang.org/x/net v0.45.0 // @grafana/oss-big-tent @grafana/partner-datasources golang.org/x/oauth2 v0.30.0 // @grafana/identity-access-team golang.org/x/sync v0.17.0 // @grafana/alerting-backend golang.org/x/text v0.29.0 // @grafana/grafana-backend-group diff --git a/go.sum b/go.sum index a752393996b..97fc36d91e2 100644 --- a/go.sum +++ b/go.sum @@ -2875,8 +2875,8 @@ golang.org/x/net v0.21.0/go.mod h1:bIjVDfnllIU7BJ2DNgfnXvpSvtn8VRwhlsaeUTyUS44= golang.org/x/net v0.25.0/go.mod h1:JkAGAh7GEvH74S6FOH42FLoXpXbE/aqXSrIQjXgsiwM= golang.org/x/net v0.26.0/go.mod h1:5YKkiSynbBIh3p6iOc/vibscux0x38BZDkn8sCUPxHE= golang.org/x/net v0.29.0/go.mod h1:gLkgy8jTGERgjzMic6DS9+SP0ajcu6Xu3Orq/SpETg0= -golang.org/x/net v0.44.0 h1:evd8IRDyfNBMBTTY5XRF1vaZlD+EmWx6x8PkhR04H/I= -golang.org/x/net v0.44.0/go.mod h1:ECOoLqd5U3Lhyeyo/QDCEVQ4sNgYsqvCZ722XogGieY= +golang.org/x/net v0.45.0 h1:RLBg5JKixCy82FtLJpeNlVM0nrSqpCRYzVU1n8kj0tM= +golang.org/x/net v0.45.0/go.mod h1:ECOoLqd5U3Lhyeyo/QDCEVQ4sNgYsqvCZ722XogGieY= golang.org/x/oauth2 v0.0.0-20180821212333-d2e6202438be/go.mod h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U= golang.org/x/oauth2 v0.0.0-20181106182150-f42d05182288/go.mod h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U= golang.org/x/oauth2 v0.0.0-20190226205417-e64efc72b421/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw= diff --git a/go.work b/go.work index efba709a41b..a4bc036f04e 100644 --- a/go.work +++ b/go.work @@ -1,4 +1,4 @@ -go 1.24.6 +go 1.25.2 // The `skip:golangci-lint` comment tag is used to exclude the package from the `golangci-lint` GitHub Action. // The module at the root of the repo (`.`) is excluded because ./pkg/... is included manually in the `golangci-lint` configuration. diff --git a/go.work.sum b/go.work.sum index 00ee74f2be3..72a4ec31fc1 100644 --- a/go.work.sum +++ b/go.work.sum @@ -1059,7 +1059,6 @@ github.com/grafana/cog v0.0.37/go.mod h1:UDstzYqMdgIROmbfkHL8fB9XWQO2lnf5z+4W/eJ github.com/grafana/cog v0.0.38 h1:V7gRRn/mh7Bg1ptrCxo0bv6K0SnG9TiDZk+3Ppftn6s= github.com/grafana/cog v0.0.38/go.mod h1:UDstzYqMdgIROmbfkHL8fB9XWQO2lnf5z+4W/eJo4Dc= github.com/grafana/cog v0.0.40/go.mod h1:TDunc7TYF7EfzjwFOlC5AkMe3To/U2KqyyG3QVvrF38= -github.com/grafana/cog v0.0.41/go.mod h1:TDunc7TYF7EfzjwFOlC5AkMe3To/U2KqyyG3QVvrF38= github.com/grafana/dskit v0.0.0-20250818234656-8ff9c6532e85/go.mod h1:kImsvJ1xnmeT9Z6StK+RdEKLzlpzBsKwJbEQfmBJdFs= github.com/grafana/go-gelf/v2 v2.0.1 h1:BOChP0h/jLeD+7F9mL7tq10xVkDG15he3T1zHuQaWak= github.com/grafana/go-gelf/v2 v2.0.1/go.mod h1:lexHie0xzYGwCgiRGcvZ723bSNyNI8ZRD4s0CLobh90= @@ -1983,6 +1982,7 @@ golang.org/x/net v0.39.0/go.mod h1:X7NRbYVEA+ewNkCNyJ513WmMdQ3BineSwVtN2zD/d+E= golang.org/x/net v0.40.0/go.mod h1:y0hY0exeL2Pku80/zKK7tpntoX23cqL3Oa6njdgRtds= golang.org/x/net v0.41.0/go.mod h1:B/K4NNqkfmg07DQYrbwvSluqCJOOXwUjeb/5lOisjbA= golang.org/x/net v0.43.0/go.mod h1:vhO1fvI4dGsIjh73sWfUVjj3N7CA9WkKJNQm2svM6Jg= +golang.org/x/net v0.44.0/go.mod h1:ECOoLqd5U3Lhyeyo/QDCEVQ4sNgYsqvCZ722XogGieY= golang.org/x/oauth2 v0.19.0/go.mod h1:vYi7skDa1x015PmRRYZ7+s1cWyPgrPiSYRe4rnsexc8= golang.org/x/oauth2 v0.22.0/go.mod h1:XYTD2NtWslqkgxebSiOHnXEap4TF09sJSc7H1sXbhtI= golang.org/x/oauth2 v0.24.0/go.mod h1:XYTD2NtWslqkgxebSiOHnXEap4TF09sJSc7H1sXbhtI= diff --git a/hack/go.mod b/hack/go.mod index ba694313e1f..330d10c8cff 100644 --- a/hack/go.mod +++ b/hack/go.mod @@ -1,16 +1,16 @@ module github.com/grafana/grafana/hack -go 1.24.6 +go 1.25.2 require k8s.io/code-generator v0.34.1 require ( github.com/go-logr/logr v1.4.2 // indirect github.com/spf13/pflag v1.0.6 // indirect - golang.org/x/mod v0.24.0 // indirect - golang.org/x/sync v0.14.0 // indirect - golang.org/x/text v0.25.0 // indirect - golang.org/x/tools v0.33.0 // indirect + golang.org/x/mod v0.27.0 // indirect + golang.org/x/sync v0.17.0 // indirect + golang.org/x/text v0.29.0 // indirect + golang.org/x/tools v0.36.0 // indirect k8s.io/gengo/v2 v2.0.0-20250604051438-85fd79dbfd9f // indirect k8s.io/klog/v2 v2.130.1 // indirect ) diff --git a/hack/go.sum b/hack/go.sum index 60d1ab0595e..b370b15f40f 100644 --- a/hack/go.sum +++ b/hack/go.sum @@ -4,14 +4,14 @@ github.com/google/go-cmp v0.7.0 h1:wk8382ETsv4JYUZwIsn6YpYiWiBsYLSJiTsyBybVuN8= github.com/google/go-cmp v0.7.0/go.mod h1:pXiqmnSA92OHEEa9HXL2W4E7lf9JzCmGVUdgjX3N/iU= github.com/spf13/pflag v1.0.6 h1:jFzHGLGAlb3ruxLB8MhbI6A8+AQX/2eW4qeyNZXNp2o= github.com/spf13/pflag v1.0.6/go.mod h1:McXfInJRrz4CZXVZOBLb0bTZqETkiAhM9Iw0y3An2Bg= -golang.org/x/mod v0.24.0 h1:ZfthKaKaT4NrhGVZHO1/WDTwGES4De8KtWO0SIbNJMU= -golang.org/x/mod v0.24.0/go.mod h1:IXM97Txy2VM4PJ3gI61r1YEk/gAj6zAHN3AdZt6S9Ww= -golang.org/x/sync v0.14.0 h1:woo0S4Yywslg6hp4eUFjTVOyKt0RookbpAHG4c1HmhQ= -golang.org/x/sync v0.14.0/go.mod h1:1dzgHSNfp02xaA81J2MS99Qcpr2w7fw1gpm99rleRqA= -golang.org/x/text v0.25.0 h1:qVyWApTSYLk/drJRO5mDlNYskwQznZmkpV2c8q9zls4= -golang.org/x/text v0.25.0/go.mod h1:WEdwpYrmk1qmdHvhkSTNPm3app7v4rsT8F2UD6+VHIA= -golang.org/x/tools v0.33.0 h1:4qz2S3zmRxbGIhDIAgjxvFutSvH5EfnsYrRBj0UI0bc= -golang.org/x/tools v0.33.0/go.mod h1:CIJMaWEY88juyUfo7UbgPqbC8rU2OqfAV1h2Qp0oMYI= +golang.org/x/mod v0.27.0 h1:kb+q2PyFnEADO2IEF935ehFUXlWiNjJWtRNgBLSfbxQ= +golang.org/x/mod v0.27.0/go.mod h1:rWI627Fq0DEoudcK+MBkNkCe0EetEaDSwJJkCcjpazc= +golang.org/x/sync v0.17.0 h1:l60nONMj9l5drqw6jlhIELNv9I0A4OFgRsG9k2oT9Ug= +golang.org/x/sync v0.17.0/go.mod h1:9KTHXmSnoGruLpwFjVSX0lNNA75CykiMECbovNTZqGI= +golang.org/x/text v0.29.0 h1:1neNs90w9YzJ9BocxfsQNHKuAT4pkghyXc4nhZ6sJvk= +golang.org/x/text v0.29.0/go.mod h1:7MhJOA9CD2qZyOKYazxdYMF85OwPdEr9jTtBpO7ydH4= +golang.org/x/tools v0.36.0 h1:kWS0uv/zsvHEle1LbV5LE8QujrxB3wfQyxHfhOk0Qkg= +golang.org/x/tools v0.36.0/go.mod h1:WBDiHKJK8YgLHlcQPYQzNCkUxUypCaa5ZegCVutKm+s= k8s.io/code-generator v0.34.1 h1:WpphT26E+j7tEgIUfFr5WfbJrktCGzB3JoJH9149xYc= k8s.io/code-generator v0.34.1/go.mod h1:DeWjekbDnJWRwpw3s0Jat87c+e0TgkxoR4ar608yqvg= k8s.io/gengo/v2 v2.0.0-20250604051438-85fd79dbfd9f h1:SLb+kxmzfA87x4E4brQzB33VBbT2+x7Zq9ROIHmGn9Q= diff --git a/pkg/aggregator/go.mod b/pkg/aggregator/go.mod index 543ace09235..2e8f955f64e 100644 --- a/pkg/aggregator/go.mod +++ b/pkg/aggregator/go.mod @@ -1,6 +1,6 @@ module github.com/grafana/grafana/pkg/aggregator -go 1.24.6 +go 1.25.2 require ( github.com/emicklei/go-restful/v3 v3.13.0 @@ -140,7 +140,7 @@ require ( golang.org/x/crypto v0.42.0 // indirect golang.org/x/exp v0.0.0-20250911091902-df9299821621 // indirect golang.org/x/mod v0.28.0 // indirect - golang.org/x/net v0.44.0 // indirect + golang.org/x/net v0.45.0 // indirect golang.org/x/oauth2 v0.30.0 // indirect golang.org/x/sync v0.17.0 // indirect golang.org/x/sys v0.36.0 // indirect diff --git a/pkg/aggregator/go.sum b/pkg/aggregator/go.sum index 2f6756ed544..385d20d5cc7 100644 --- a/pkg/aggregator/go.sum +++ b/pkg/aggregator/go.sum @@ -385,8 +385,8 @@ golang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3/go.mod h1:t9HGtf8HONx5eT2rtn golang.org/x/net v0.0.0-20190620200207-3b0461eec859/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= golang.org/x/net v0.0.0-20200226121028-0de0cce0169b/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= golang.org/x/net v0.0.0-20201021035429-f5854403a974/go.mod h1:sp8m0HH+o8qH0wwXwYZr8TS3Oi6o0r6Gce1SSxlDquU= -golang.org/x/net v0.44.0 h1:evd8IRDyfNBMBTTY5XRF1vaZlD+EmWx6x8PkhR04H/I= -golang.org/x/net v0.44.0/go.mod h1:ECOoLqd5U3Lhyeyo/QDCEVQ4sNgYsqvCZ722XogGieY= +golang.org/x/net v0.45.0 h1:RLBg5JKixCy82FtLJpeNlVM0nrSqpCRYzVU1n8kj0tM= +golang.org/x/net v0.45.0/go.mod h1:ECOoLqd5U3Lhyeyo/QDCEVQ4sNgYsqvCZ722XogGieY= golang.org/x/oauth2 v0.0.0-20180821212333-d2e6202438be/go.mod h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U= golang.org/x/oauth2 v0.30.0 h1:dnDm7JmhM45NNpd8FDDeLhK6FwqbOf4MLCM9zb1BOHI= golang.org/x/oauth2 v0.30.0/go.mod h1:B++QgG3ZKulg6sRPGD/mqlHQs5rB3Ml9erfeDY7xKlU= diff --git a/pkg/apimachinery/go.mod b/pkg/apimachinery/go.mod index fe9637801e2..d111a57eb09 100644 --- a/pkg/apimachinery/go.mod +++ b/pkg/apimachinery/go.mod @@ -1,6 +1,6 @@ module github.com/grafana/grafana/pkg/apimachinery -go 1.24.6 +go 1.25.2 require ( github.com/grafana/authlib v0.0.0-20250930082137-a40e2c2b094f // @grafana/identity-access-team @@ -45,7 +45,7 @@ require ( go.yaml.in/yaml/v2 v2.4.2 // indirect go.yaml.in/yaml/v3 v3.0.4 // indirect golang.org/x/crypto v0.42.0 // indirect - golang.org/x/net v0.44.0 // indirect + golang.org/x/net v0.45.0 // indirect golang.org/x/sync v0.17.0 // indirect golang.org/x/sys v0.36.0 // indirect golang.org/x/text v0.29.0 // indirect diff --git a/pkg/apimachinery/go.sum b/pkg/apimachinery/go.sum index 11948a2d6ef..8cae112de9f 100644 --- a/pkg/apimachinery/go.sum +++ b/pkg/apimachinery/go.sum @@ -102,8 +102,8 @@ golang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3/go.mod h1:t9HGtf8HONx5eT2rtn golang.org/x/net v0.0.0-20190620200207-3b0461eec859/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= golang.org/x/net v0.0.0-20200226121028-0de0cce0169b/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= golang.org/x/net v0.0.0-20201021035429-f5854403a974/go.mod h1:sp8m0HH+o8qH0wwXwYZr8TS3Oi6o0r6Gce1SSxlDquU= -golang.org/x/net v0.44.0 h1:evd8IRDyfNBMBTTY5XRF1vaZlD+EmWx6x8PkhR04H/I= -golang.org/x/net v0.44.0/go.mod h1:ECOoLqd5U3Lhyeyo/QDCEVQ4sNgYsqvCZ722XogGieY= +golang.org/x/net v0.45.0 h1:RLBg5JKixCy82FtLJpeNlVM0nrSqpCRYzVU1n8kj0tM= +golang.org/x/net v0.45.0/go.mod h1:ECOoLqd5U3Lhyeyo/QDCEVQ4sNgYsqvCZ722XogGieY= golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.0.0-20190911185100-cd5d95a43a6e/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.0.0-20201020160332-67f06af15bc9/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= diff --git a/pkg/apiserver/go.mod b/pkg/apiserver/go.mod index a6e649494bb..4c75e55fe1f 100644 --- a/pkg/apiserver/go.mod +++ b/pkg/apiserver/go.mod @@ -1,6 +1,6 @@ module github.com/grafana/grafana/pkg/apiserver -go 1.24.6 +go 1.25.2 require ( github.com/google/go-cmp v0.7.0 @@ -84,7 +84,7 @@ require ( go.yaml.in/yaml/v2 v2.4.2 // indirect go.yaml.in/yaml/v3 v3.0.4 // indirect golang.org/x/crypto v0.42.0 // indirect - golang.org/x/net v0.44.0 // indirect + golang.org/x/net v0.45.0 // indirect golang.org/x/oauth2 v0.30.0 // indirect golang.org/x/sync v0.17.0 // indirect golang.org/x/sys v0.36.0 // indirect diff --git a/pkg/apiserver/go.sum b/pkg/apiserver/go.sum index 1025c416476..a5fb4f6048d 100644 --- a/pkg/apiserver/go.sum +++ b/pkg/apiserver/go.sum @@ -217,8 +217,8 @@ golang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3/go.mod h1:t9HGtf8HONx5eT2rtn golang.org/x/net v0.0.0-20190620200207-3b0461eec859/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= golang.org/x/net v0.0.0-20200226121028-0de0cce0169b/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= golang.org/x/net v0.0.0-20201021035429-f5854403a974/go.mod h1:sp8m0HH+o8qH0wwXwYZr8TS3Oi6o0r6Gce1SSxlDquU= -golang.org/x/net v0.44.0 h1:evd8IRDyfNBMBTTY5XRF1vaZlD+EmWx6x8PkhR04H/I= -golang.org/x/net v0.44.0/go.mod h1:ECOoLqd5U3Lhyeyo/QDCEVQ4sNgYsqvCZ722XogGieY= +golang.org/x/net v0.45.0 h1:RLBg5JKixCy82FtLJpeNlVM0nrSqpCRYzVU1n8kj0tM= +golang.org/x/net v0.45.0/go.mod h1:ECOoLqd5U3Lhyeyo/QDCEVQ4sNgYsqvCZ722XogGieY= golang.org/x/oauth2 v0.0.0-20180821212333-d2e6202438be/go.mod h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U= golang.org/x/oauth2 v0.30.0 h1:dnDm7JmhM45NNpd8FDDeLhK6FwqbOf4MLCM9zb1BOHI= golang.org/x/oauth2 v0.30.0/go.mod h1:B++QgG3ZKulg6sRPGD/mqlHQs5rB3Ml9erfeDY7xKlU= diff --git a/pkg/build/go.mod b/pkg/build/go.mod index 66435b14493..4574d2cb541 100644 --- a/pkg/build/go.mod +++ b/pkg/build/go.mod @@ -1,6 +1,6 @@ module github.com/grafana/grafana/pkg/build -go 1.24.6 +go 1.25.2 // Override docker/docker to avoid: // go: github.com/drone-runners/drone-runner-docker@v1.8.2 requires @@ -13,7 +13,7 @@ require ( go.opentelemetry.io/otel v1.38.0 // indirect; @grafana/grafana-backend-group go.opentelemetry.io/otel/sdk v1.38.0 // indirect; @grafana/grafana-backend-group go.opentelemetry.io/otel/trace v1.38.0 // indirect; @grafana/grafana-backend-group - golang.org/x/net v0.44.0 // indirect; @grafana/oss-big-tent @grafana/partner-datasources + golang.org/x/net v0.45.0 // indirect; @grafana/oss-big-tent @grafana/partner-datasources golang.org/x/sync v0.17.0 // @grafana/alerting-backend golang.org/x/text v0.29.0 // indirect; @grafana/grafana-backend-group google.golang.org/grpc v1.75.1 // indirect; @grafana/plugins-platform-backend diff --git a/pkg/build/go.sum b/pkg/build/go.sum index 646c193b2c7..881acfbb52d 100644 --- a/pkg/build/go.sum +++ b/pkg/build/go.sum @@ -95,8 +95,8 @@ go.opentelemetry.io/proto/otlp v1.7.1 h1:gTOMpGDb0WTBOP8JaO72iL3auEZhVmAQg4ipjOV go.opentelemetry.io/proto/otlp v1.7.1/go.mod h1:b2rVh6rfI/s2pHWNlB7ILJcRALpcNDzKhACevjI+ZnE= go.uber.org/goleak v1.3.0 h1:2K3zAYmnTNqV73imy9J1T3WC+gmCePx2hEGkimedGto= go.uber.org/goleak v1.3.0/go.mod h1:CoHD4mav9JJNrW/WLlf7HGZPjdw8EucARQHekz1X6bE= -golang.org/x/net v0.44.0 h1:evd8IRDyfNBMBTTY5XRF1vaZlD+EmWx6x8PkhR04H/I= -golang.org/x/net v0.44.0/go.mod h1:ECOoLqd5U3Lhyeyo/QDCEVQ4sNgYsqvCZ722XogGieY= +golang.org/x/net v0.45.0 h1:RLBg5JKixCy82FtLJpeNlVM0nrSqpCRYzVU1n8kj0tM= +golang.org/x/net v0.45.0/go.mod h1:ECOoLqd5U3Lhyeyo/QDCEVQ4sNgYsqvCZ722XogGieY= golang.org/x/sync v0.17.0 h1:l60nONMj9l5drqw6jlhIELNv9I0A4OFgRsG9k2oT9Ug= golang.org/x/sync v0.17.0/go.mod h1:9KTHXmSnoGruLpwFjVSX0lNNA75CykiMECbovNTZqGI= golang.org/x/sys v0.36.0 h1:KVRy2GtZBrk1cBYA7MKu5bEZFxQk4NIDV6RLVcC8o0k= diff --git a/pkg/build/wire/go.mod b/pkg/build/wire/go.mod index e6276f37657..b662f2b2ea6 100644 --- a/pkg/build/wire/go.mod +++ b/pkg/build/wire/go.mod @@ -1,6 +1,6 @@ module github.com/grafana/grafana/pkg/build/wire -go 1.24.6 +go 1.25.2 require ( github.com/google/go-cmp v0.7.0 diff --git a/pkg/codegen/go.mod b/pkg/codegen/go.mod index 91b8f538f49..ec68f51317b 100644 --- a/pkg/codegen/go.mod +++ b/pkg/codegen/go.mod @@ -1,6 +1,6 @@ module github.com/grafana/grafana/pkg/codegen -go 1.24.6 +go 1.25.2 require ( cuelang.org/go v0.11.1 @@ -48,7 +48,7 @@ require ( github.com/xlab/treeprint v1.2.0 // indirect github.com/yalue/merged_fs v1.3.0 // indirect golang.org/x/mod v0.28.0 // indirect - golang.org/x/net v0.44.0 // indirect + golang.org/x/net v0.45.0 // indirect golang.org/x/sync v0.17.0 // indirect golang.org/x/text v0.29.0 // indirect gopkg.in/yaml.v3 v3.0.1 // indirect diff --git a/pkg/codegen/go.sum b/pkg/codegen/go.sum index 5fd86180488..0e5105a308e 100644 --- a/pkg/codegen/go.sum +++ b/pkg/codegen/go.sum @@ -102,8 +102,8 @@ github.com/yalue/merged_fs v1.3.0 h1:qCeh9tMPNy/i8cwDsQTJ5bLr6IRxbs6meakNE5O+wyY github.com/yalue/merged_fs v1.3.0/go.mod h1:WqqchfVYQyclV2tnR7wtRhBddzBvLVR83Cjw9BKQw0M= golang.org/x/mod v0.28.0 h1:gQBtGhjxykdjY9YhZpSlZIsbnaE2+PgjfLWUQTnoZ1U= golang.org/x/mod v0.28.0/go.mod h1:yfB/L0NOf/kmEbXjzCPOx1iK1fRutOydrCMsqRhEBxI= -golang.org/x/net v0.44.0 h1:evd8IRDyfNBMBTTY5XRF1vaZlD+EmWx6x8PkhR04H/I= -golang.org/x/net v0.44.0/go.mod h1:ECOoLqd5U3Lhyeyo/QDCEVQ4sNgYsqvCZ722XogGieY= +golang.org/x/net v0.45.0 h1:RLBg5JKixCy82FtLJpeNlVM0nrSqpCRYzVU1n8kj0tM= +golang.org/x/net v0.45.0/go.mod h1:ECOoLqd5U3Lhyeyo/QDCEVQ4sNgYsqvCZ722XogGieY= golang.org/x/sync v0.17.0 h1:l60nONMj9l5drqw6jlhIELNv9I0A4OFgRsG9k2oT9Ug= golang.org/x/sync v0.17.0/go.mod h1:9KTHXmSnoGruLpwFjVSX0lNNA75CykiMECbovNTZqGI= golang.org/x/text v0.29.0 h1:1neNs90w9YzJ9BocxfsQNHKuAT4pkghyXc4nhZ6sJvk= diff --git a/pkg/plugins/codegen/go.mod b/pkg/plugins/codegen/go.mod index 6f7a75648fe..0ea55ab38ce 100644 --- a/pkg/plugins/codegen/go.mod +++ b/pkg/plugins/codegen/go.mod @@ -1,6 +1,6 @@ module github.com/grafana/grafana/pkg/plugins/codegen -go 1.24.6 +go 1.25.2 replace github.com/grafana/grafana/pkg/codegen => ../../codegen @@ -44,7 +44,7 @@ require ( github.com/xlab/treeprint v1.2.0 // indirect github.com/yalue/merged_fs v1.3.0 // indirect golang.org/x/mod v0.28.0 // indirect - golang.org/x/net v0.44.0 // indirect + golang.org/x/net v0.45.0 // indirect golang.org/x/oauth2 v0.27.0 // indirect golang.org/x/sync v0.17.0 // indirect golang.org/x/text v0.29.0 // indirect diff --git a/pkg/plugins/codegen/go.sum b/pkg/plugins/codegen/go.sum index c8fff7e7441..d0de427e679 100644 --- a/pkg/plugins/codegen/go.sum +++ b/pkg/plugins/codegen/go.sum @@ -96,8 +96,8 @@ github.com/yalue/merged_fs v1.3.0 h1:qCeh9tMPNy/i8cwDsQTJ5bLr6IRxbs6meakNE5O+wyY github.com/yalue/merged_fs v1.3.0/go.mod h1:WqqchfVYQyclV2tnR7wtRhBddzBvLVR83Cjw9BKQw0M= golang.org/x/mod v0.28.0 h1:gQBtGhjxykdjY9YhZpSlZIsbnaE2+PgjfLWUQTnoZ1U= golang.org/x/mod v0.28.0/go.mod h1:yfB/L0NOf/kmEbXjzCPOx1iK1fRutOydrCMsqRhEBxI= -golang.org/x/net v0.44.0 h1:evd8IRDyfNBMBTTY5XRF1vaZlD+EmWx6x8PkhR04H/I= -golang.org/x/net v0.44.0/go.mod h1:ECOoLqd5U3Lhyeyo/QDCEVQ4sNgYsqvCZ722XogGieY= +golang.org/x/net v0.45.0 h1:RLBg5JKixCy82FtLJpeNlVM0nrSqpCRYzVU1n8kj0tM= +golang.org/x/net v0.45.0/go.mod h1:ECOoLqd5U3Lhyeyo/QDCEVQ4sNgYsqvCZ722XogGieY= golang.org/x/oauth2 v0.27.0 h1:da9Vo7/tDv5RH/7nZDz1eMGS/q1Vv1N/7FCrBhI9I3M= golang.org/x/oauth2 v0.27.0/go.mod h1:onh5ek6nERTohokkhCD/y2cV4Do3fxFHFuAejCkRWT8= golang.org/x/sync v0.17.0 h1:l60nONMj9l5drqw6jlhIELNv9I0A4OFgRsG9k2oT9Ug= diff --git a/pkg/promlib/go.mod b/pkg/promlib/go.mod index 6b046cd9a75..e28af53a841 100644 --- a/pkg/promlib/go.mod +++ b/pkg/promlib/go.mod @@ -1,6 +1,6 @@ module github.com/grafana/grafana/pkg/promlib -go 1.24.6 +go 1.25.2 require ( github.com/grafana/dskit v0.0.0-20250908063411-6b6da59b5cc4 @@ -121,7 +121,7 @@ require ( go.yaml.in/yaml/v3 v3.0.4 // indirect golang.org/x/exp v0.0.0-20250911091902-df9299821621 // indirect golang.org/x/mod v0.28.0 // indirect - golang.org/x/net v0.44.0 // indirect + golang.org/x/net v0.45.0 // indirect golang.org/x/sync v0.17.0 // indirect golang.org/x/sys v0.36.0 // indirect golang.org/x/telemetry v0.0.0-20250908211612-aef8a434d053 // indirect diff --git a/pkg/promlib/go.sum b/pkg/promlib/go.sum index 7c97cf52cdc..59f48deaaba 100644 --- a/pkg/promlib/go.sum +++ b/pkg/promlib/go.sum @@ -368,8 +368,8 @@ golang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3/go.mod h1:t9HGtf8HONx5eT2rtn golang.org/x/net v0.0.0-20190620200207-3b0461eec859/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= golang.org/x/net v0.0.0-20200226121028-0de0cce0169b/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= golang.org/x/net v0.0.0-20201021035429-f5854403a974/go.mod h1:sp8m0HH+o8qH0wwXwYZr8TS3Oi6o0r6Gce1SSxlDquU= -golang.org/x/net v0.44.0 h1:evd8IRDyfNBMBTTY5XRF1vaZlD+EmWx6x8PkhR04H/I= -golang.org/x/net v0.44.0/go.mod h1:ECOoLqd5U3Lhyeyo/QDCEVQ4sNgYsqvCZ722XogGieY= +golang.org/x/net v0.45.0 h1:RLBg5JKixCy82FtLJpeNlVM0nrSqpCRYzVU1n8kj0tM= +golang.org/x/net v0.45.0/go.mod h1:ECOoLqd5U3Lhyeyo/QDCEVQ4sNgYsqvCZ722XogGieY= golang.org/x/oauth2 v0.30.0 h1:dnDm7JmhM45NNpd8FDDeLhK6FwqbOf4MLCM9zb1BOHI= golang.org/x/oauth2 v0.30.0/go.mod h1:B++QgG3ZKulg6sRPGD/mqlHQs5rB3Ml9erfeDY7xKlU= golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= diff --git a/pkg/semconv/go.mod b/pkg/semconv/go.mod index 2821de78bab..bdd04023e6e 100644 --- a/pkg/semconv/go.mod +++ b/pkg/semconv/go.mod @@ -1,6 +1,6 @@ module github.com/grafana/grafana/pkg/semconv -go 1.24.6 +go 1.25.2 require go.opentelemetry.io/otel v1.38.0 diff --git a/scripts/go-workspace/go.mod b/scripts/go-workspace/go.mod index de53d21c261..99c57c5852b 100644 --- a/scripts/go-workspace/go.mod +++ b/scripts/go-workspace/go.mod @@ -1,5 +1,5 @@ module github.com/grafana/grafana/scripts/go-workspace -go 1.24.6 +go 1.25.2 require golang.org/x/mod v0.24.0 diff --git a/scripts/modowners/go.mod b/scripts/modowners/go.mod index 42d4536abb0..49aa68ef77f 100644 --- a/scripts/modowners/go.mod +++ b/scripts/modowners/go.mod @@ -1,5 +1,5 @@ module github.com/grafana/grafana/scripts/modowners -go 1.24.6 +go 1.25.2 require golang.org/x/mod v0.24.0 From 3f4c9879c9df430a07843fb52a5cd046574d2e66 Mon Sep 17 00:00:00 2001 From: Santiago Date: Wed, 8 Oct 2025 13:13:02 +0200 Subject: [PATCH 004/112] Remote Alertmanager: Add timeout to the remoteClient (#112157) --- pkg/services/ngalert/remote/alertmanager.go | 1 + pkg/services/ngalert/remote/client/mimir.go | 5 ++++- 2 files changed, 5 insertions(+), 1 deletion(-) diff --git a/pkg/services/ngalert/remote/alertmanager.go b/pkg/services/ngalert/remote/alertmanager.go index 8cde9ee198f..78fbbfe3a5b 100644 --- a/pkg/services/ngalert/remote/alertmanager.go +++ b/pkg/services/ngalert/remote/alertmanager.go @@ -139,6 +139,7 @@ func NewAlertmanager(ctx context.Context, cfg AlertmanagerConfig, store stateSto Logger: logger, Password: cfg.BasicAuthPassword, TenantID: cfg.TenantID, + Timeout: cfg.Timeout, URL: u, } mc, err := remoteClient.New(mcCfg, metrics, tracer) diff --git a/pkg/services/ngalert/remote/client/mimir.go b/pkg/services/ngalert/remote/client/mimir.go index 6e19d5eb65f..c2e89aa1c53 100644 --- a/pkg/services/ngalert/remote/client/mimir.go +++ b/pkg/services/ngalert/remote/client/mimir.go @@ -11,6 +11,7 @@ import ( "net/url" "path" "strings" + "time" alertingNotify "github.com/grafana/alerting/notify" @@ -65,7 +66,8 @@ type Config struct { TenantID string Password string - Logger log.Logger + Logger log.Logger + Timeout time.Duration } // successResponse represents a successful response from the Mimir API. @@ -98,6 +100,7 @@ func New(cfg *Config, metrics *metrics.RemoteAlertmanager, tracer tracing.Tracer c := &http.Client{ Transport: rt, + Timeout: cfg.Timeout, } tc := alertingInstrument.NewTimedClient(c, metrics.RequestLatency) trc := alertingInstrument.NewTracedClient(tc, tracer, "remote.alertmanager.client") From 40dde1d4aa039ed08dac10270b940a7565352a1f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jean-Philippe=20Qu=C3=A9m=C3=A9ner?= Date: Wed, 8 Oct 2025 13:22:29 +0200 Subject: [PATCH 005/112] feat(grpc): add client keepalive config (#112158) --- pkg/services/apiserver/options/storage.go | 4 +++ pkg/storage/unified/client.go | 36 +++++++++++++++-------- 2 files changed, 27 insertions(+), 13 deletions(-) diff --git a/pkg/services/apiserver/options/storage.go b/pkg/services/apiserver/options/storage.go index e6979e05ee1..97b95a45b26 100644 --- a/pkg/services/apiserver/options/storage.go +++ b/pkg/services/apiserver/options/storage.go @@ -4,6 +4,7 @@ import ( "context" "fmt" "net" + "time" "github.com/spf13/pflag" "go.opentelemetry.io/contrib/instrumentation/google.golang.org/grpc/otelgrpc" @@ -52,6 +53,7 @@ type StorageOptions struct { GrpcClientAuthenticationTokenExchangeURL string GrpcClientAuthenticationTokenNamespace string GrpcClientAuthenticationAllowInsecure bool + GrpcClientKeepaliveTime time.Duration // Secrets Manager Configuration for InlineSecureValueSupport SecretsManagerGrpcClientEnable bool @@ -90,6 +92,7 @@ func NewStorageOptions() *StorageOptions { Address: "localhost:10000", GrpcClientAuthenticationTokenNamespace: "*", GrpcClientAuthenticationAllowInsecure: false, + GrpcClientKeepaliveTime: 0, BlobThresholdBytes: BlobThresholdDefault, } } @@ -103,6 +106,7 @@ func (o *StorageOptions) AddFlags(fs *pflag.FlagSet) { fs.StringVar(&o.GrpcClientAuthenticationTokenExchangeURL, "grpc-client-authentication-token-exchange-url", o.GrpcClientAuthenticationTokenExchangeURL, "Token exchange url for grpc client authentication") fs.StringVar(&o.GrpcClientAuthenticationTokenNamespace, "grpc-client-authentication-token-namespace", o.GrpcClientAuthenticationTokenNamespace, "Token namespace for grpc client authentication") fs.BoolVar(&o.GrpcClientAuthenticationAllowInsecure, "grpc-client-authentication-allow-insecure", o.GrpcClientAuthenticationAllowInsecure, "Allow insecure grpc client authentication") + fs.DurationVar(&o.GrpcClientKeepaliveTime, "grpc-client-keepalive-time", o.GrpcClientKeepaliveTime, "gRPC client keep-alive ping interval (e.g., 6m).") // Secrets Manager Configuration flags fs.BoolVar(&o.SecretsManagerGrpcClientEnable, "grafana.secrets-manager.grpc-client-enable", false, "Enable gRPC client for secrets manager") diff --git a/pkg/storage/unified/client.go b/pkg/storage/unified/client.go index 03cab8c6df4..be6efdd0b37 100644 --- a/pkg/storage/unified/client.go +++ b/pkg/storage/unified/client.go @@ -14,6 +14,7 @@ import ( "gocloud.dev/blob/fileblob" "google.golang.org/grpc" "google.golang.org/grpc/credentials/insecure" + "google.golang.org/grpc/keepalive" "github.com/grafana/authlib/types" "github.com/grafana/dskit/flagext" @@ -59,12 +60,13 @@ func ProvideUnifiedStorageClient(opts *Options, // See: apiserver.applyAPIServerConfig(cfg, features, o) apiserverCfg := opts.Cfg.SectionWithEnvOverrides("grafana-apiserver") client, err := newClient(options.StorageOptions{ - StorageType: options.StorageType(apiserverCfg.Key("storage_type").MustString(string(options.StorageTypeUnified))), - DataPath: apiserverCfg.Key("storage_path").MustString(filepath.Join(opts.Cfg.DataPath, "grafana-apiserver")), - Address: apiserverCfg.Key("address").MustString(""), - SearchServerAddress: apiserverCfg.Key("search_server_address").MustString(""), - BlobStoreURL: apiserverCfg.Key("blob_url").MustString(""), - BlobThresholdBytes: apiserverCfg.Key("blob_threshold_bytes").MustInt(options.BlobThresholdDefault), + StorageType: options.StorageType(apiserverCfg.Key("storage_type").MustString(string(options.StorageTypeUnified))), + DataPath: apiserverCfg.Key("storage_path").MustString(filepath.Join(opts.Cfg.DataPath, "grafana-apiserver")), + Address: apiserverCfg.Key("address").MustString(""), + SearchServerAddress: apiserverCfg.Key("search_server_address").MustString(""), + BlobStoreURL: apiserverCfg.Key("blob_url").MustString(""), + BlobThresholdBytes: apiserverCfg.Key("blob_threshold_bytes").MustInt(options.BlobThresholdDefault), + GrpcClientKeepaliveTime: apiserverCfg.Key("grpc_client_keepalive_time").MustDuration(0), }, opts.Cfg, opts.Features, opts.DB, opts.Tracer, opts.Reg, opts.Authzc, opts.Docs, storageMetrics, indexMetrics, opts.SecureValues) if err == nil { // Decide whether to disable SQL fallback stats per resource in Mode 5. @@ -145,13 +147,13 @@ func newClient(opts options.StorageOptions, metrics = newClientMetrics(reg) ) - conn, err = newGrpcConn(opts.Address, metrics, features) + conn, err = newGrpcConn(opts.Address, metrics, features, opts.GrpcClientKeepaliveTime) if err != nil { return nil, err } if opts.SearchServerAddress != "" { - indexConn, err = newGrpcConn(opts.SearchServerAddress, metrics, features) + indexConn, err = newGrpcConn(opts.SearchServerAddress, metrics, features, opts.GrpcClientKeepaliveTime) if err != nil { return nil, err @@ -219,7 +221,7 @@ func newClient(opts options.StorageOptions, } } -func newGrpcConn(address string, metrics *clientMetrics, features featuremgmt.FeatureToggles) (grpc.ClientConnInterface, error) { +func newGrpcConn(address string, metrics *clientMetrics, features featuremgmt.FeatureToggles, clientKeepaliveTime time.Duration) (grpc.ClientConnInterface, error) { // Create either a connection pool or a single connection. // The connection pool __can__ be useful when connection to // server side load balancers like kube-proxy. @@ -229,7 +231,7 @@ func newGrpcConn(address string, metrics *clientMetrics, features featuremgmt.Fe maxCapacity: 6, idleTimeout: time.Minute, factory: func() (*grpc.ClientConn, error) { - return grpcConn(address, metrics) + return grpcConn(address, metrics, clientKeepaliveTime) }, }) if err != nil { @@ -239,7 +241,7 @@ func newGrpcConn(address string, metrics *clientMetrics, features featuremgmt.Fe return conn, nil } - conn, err := grpcConn(address, metrics) + conn, err := grpcConn(address, metrics, clientKeepaliveTime) if err != nil { return nil, err } @@ -248,7 +250,7 @@ func newGrpcConn(address string, metrics *clientMetrics, features featuremgmt.Fe } // grpcConn creates a new gRPC connection to the provided address. -func grpcConn(address string, metrics *clientMetrics) (*grpc.ClientConn, error) { +func grpcConn(address string, metrics *clientMetrics, clientKeepaliveTime time.Duration) (*grpc.ClientConn, error) { // Report gRPC status code errors as labels. unary, stream := instrument(metrics.requestDuration, middleware.ReportGRPCStatusOption) @@ -281,14 +283,22 @@ func grpcConn(address string, metrics *clientMetrics) (*grpc.ClientConn, error) // This reduces the number of requests made to the DNS servers. opts = append(opts, grpc.WithDisableServiceConfig()) + if clientKeepaliveTime > 0 { + opts = append(opts, grpc.WithKeepaliveParams(keepalive.ClientParameters{ + Time: clientKeepaliveTime, + Timeout: 10 * time.Second, + PermitWithoutStream: true, + })) + } // Create a connection to the gRPC server return grpc.NewClient(address, opts...) } // GrpcConn is the public constructor that can be used for testing. +// TODO: also use grpc_client_keepalive_time here. func GrpcConn(address string, reg prometheus.Registerer) (*grpc.ClientConn, error) { metrics := newClientMetrics(reg) - return grpcConn(address, metrics) + return grpcConn(address, metrics, 0) } // instrument is the same as grpcclient.Instrument but without the middleware.ClientUserHeaderInterceptor From 047c51be01cc4aa95f8b6b4445df1408bde7446e Mon Sep 17 00:00:00 2001 From: Stephanie Hingtgen Date: Wed, 8 Oct 2025 05:25:57 -0600 Subject: [PATCH 006/112] Provisioning: Do full sync on resync period when needed (#112144) --- .../pkg/repository/github/webhook.go | 24 ++------ apps/provisioning/pkg/repository/workflows.go | 32 +++++++++++ .../pkg/repository/workflows_test.go | 55 +++++++++++++++++++ .../provisioning/controller/repository.go | 29 +++++++++- .../controller/repository_test.go | 35 ++++++++++++ 5 files changed, 152 insertions(+), 23 deletions(-) diff --git a/apps/provisioning/pkg/repository/github/webhook.go b/apps/provisioning/pkg/repository/github/webhook.go index 40f87ddc93d..0da826fe9de 100644 --- a/apps/provisioning/pkg/repository/github/webhook.go +++ b/apps/provisioning/pkg/repository/github/webhook.go @@ -7,7 +7,6 @@ import ( "log/slog" "net/http" "slices" - "strings" "github.com/google/go-github/v70/github" "github.com/google/uuid" @@ -16,7 +15,6 @@ import ( "github.com/grafana/grafana-app-sdk/logging" provisioning "github.com/grafana/grafana/apps/provisioning/pkg/apis/provisioning/v0alpha1" "github.com/grafana/grafana/apps/provisioning/pkg/repository" - "github.com/grafana/grafana/apps/provisioning/pkg/safepath" common "github.com/grafana/grafana/pkg/apimachinery/apis/common/v0alpha1" ) @@ -125,27 +123,13 @@ func (r *githubWebhookRepository) parsePushEvent(event *github.PushEvent) (*prov // however, if we get an event where just a .keep file is being deleted, and no other files in the folder // are being deleted, the folder could be gone from git, but not from grafana and we do not have a way // to get the grafana uid to delete the folder. so, instead, we will queue a full sync to clean things up. - dirsWithKeepDeletes := make(map[string]struct{}) - dirsWithOtherDeletes := make(map[string]struct{}) + var deletedPaths []string for _, change := range event.GetCommits() { - for _, removedFile := range change.Removed { - dir := safepath.Dir(removedFile) - if strings.HasSuffix(removedFile, ".keep") { - dirsWithKeepDeletes[dir] = struct{}{} - } else { - dirsWithOtherDeletes[dir] = struct{}{} - } - } - } - // if there are any keep files deleted that do not have other files deleted in the same folder, we need to queue a full sync - incremental := true - for dir := range dirsWithKeepDeletes { - if _, exists := dirsWithOtherDeletes[dir]; !exists { - incremental = false - break - } + deletedPaths = append(deletedPaths, change.Removed...) } + incremental := repository.CanUseIncrementalSync(deletedPaths) + return &provisioning.WebhookResponse{ Code: http.StatusAccepted, Job: &provisioning.JobSpec{ diff --git a/apps/provisioning/pkg/repository/workflows.go b/apps/provisioning/pkg/repository/workflows.go index 48ba1319a62..5657de9dc5e 100644 --- a/apps/provisioning/pkg/repository/workflows.go +++ b/apps/provisioning/pkg/repository/workflows.go @@ -1,9 +1,12 @@ package repository import ( + "strings" + apierrors "k8s.io/apimachinery/pkg/api/errors" provisioning "github.com/grafana/grafana/apps/provisioning/pkg/apis/provisioning/v0alpha1" + "github.com/grafana/grafana/apps/provisioning/pkg/safepath" ) func IsWriteAllowed(repo *provisioning.Repository, ref string) error { @@ -40,3 +43,32 @@ func IsWriteAllowed(repo *provisioning.Repository, ref string) error { return nil } } + +// CanUseIncrementalSync checks if an incremental sync can be performed or if a full sync is needed, +// given a list of deleted file paths. It will return true if a .keep file is deleted without +// other files being deleted in the same directory. This is because the folder will not be a part of the +// deleted files, and the .keep file is not a resource in grafana, so we can't get the folder uid. +// A full sync will clean that up. +func CanUseIncrementalSync(deletedPaths []string) bool { + dirsWithKeepDeletes := make(map[string]struct{}) + dirsWithOtherDeletes := make(map[string]struct{}) + + for _, path := range deletedPaths { + dir := safepath.Dir(path) + if strings.HasSuffix(path, ".keep") { + dirsWithKeepDeletes[dir] = struct{}{} + } else { + dirsWithOtherDeletes[dir] = struct{}{} + } + } + + // if there are any .keep files deleted that don't have other files deleted in the same folder, + // we need to do a full sync + for dir := range dirsWithKeepDeletes { + if _, exists := dirsWithOtherDeletes[dir]; !exists { + return false + } + } + + return true +} diff --git a/apps/provisioning/pkg/repository/workflows_test.go b/apps/provisioning/pkg/repository/workflows_test.go index 995c85e0eb5..91a69daf35a 100644 --- a/apps/provisioning/pkg/repository/workflows_test.go +++ b/apps/provisioning/pkg/repository/workflows_test.go @@ -345,3 +345,58 @@ func TestIsWriteAllowed(t *testing.T) { }) } } + +func TestCanUseIncrementalSync(t *testing.T) { + tests := []struct { + name string + deletedPaths []string + want bool + }{ + { + name: "no deleted paths", + deletedPaths: []string{}, + want: true, + }, + { + name: "no keep file deletions", + deletedPaths: []string{"test.json"}, + want: true, + }, + { + name: "keep file deletion at root without other deletions", + deletedPaths: []string{".keep"}, + want: false, + }, + { + name: "keep file deletion with other deletions in same folder", + deletedPaths: []string{"test/.keep", "test/test.json"}, + want: true, + }, + { + name: "multiple keep files in different folders without other deletions", + deletedPaths: []string{"folder1/.keep", "folder2/.keep"}, + want: false, + }, + { + name: "nested folder with only keep file deleted", + deletedPaths: []string{"parent/child/.keep"}, + want: false, + }, + { + name: "some folders with only keep, some with other files", + deletedPaths: []string{"folder1/.keep", "folder2/.keep", "folder2/dashboard.json"}, + want: false, + }, + { + name: "only regular files deleted from multiple folders", + deletedPaths: []string{"folder1/file1.json", "folder2/file2.json", "folder3/file3.json"}, + want: true, + }, + } + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + got := CanUseIncrementalSync(tt.deletedPaths) + require.Equal(t, tt.want, got) + }) + } +} diff --git a/pkg/registry/apis/provisioning/controller/repository.go b/pkg/registry/apis/provisioning/controller/repository.go index b8301f784df..70e5c3b4a87 100644 --- a/pkg/registry/apis/provisioning/controller/repository.go +++ b/pkg/registry/apis/provisioning/controller/repository.go @@ -340,7 +340,6 @@ func (rc *RepositoryController) determineSyncStrategy(ctx context.Context, obj * logger.Info("full sync on interval for non-versioned repository") return &provisioning.SyncJobOptions{} } - latestRef, err := versioned.LatestRef(ctx) if err != nil { logger.Warn("incremental sync on interval without knowing if ref has actually changed", "error", err) @@ -353,13 +352,37 @@ func (rc *RepositoryController) determineSyncStrategy(ctx context.Context, obj * return nil } - logger.Info("incremental sync on interval") - return &provisioning.SyncJobOptions{Incremental: true} + // Whenever possible, we try to keep it as an incremental sync to keep things performant. + // However, if there are any .keep file deletions inside a folder with no other deletions, we need + // to do a full sync to see if the folder was deleted as well in git. + incremental, err := shouldUseIncrementalSync(ctx, versioned, obj, latestRef) + if err != nil { + logger.Warn("unable to compare files for incremental sync, doing full sync", "error", err) + return &provisioning.SyncJobOptions{} + } + + logger.Info("sync on interval", "incremental", incremental) + return &provisioning.SyncJobOptions{Incremental: incremental} default: return nil } } +func shouldUseIncrementalSync(ctx context.Context, versioned repository.Versioned, obj *provisioning.Repository, latestRef string) (bool, error) { + changes, err := versioned.CompareFiles(ctx, obj.Status.Sync.LastRef, latestRef) + if err != nil { + return false, err + } + var deletedPaths []string + for _, change := range changes { + if change.Action == repository.FileActionDeleted { + deletedPaths = append(deletedPaths, change.Path) + } + } + + return repository.CanUseIncrementalSync(deletedPaths), nil +} + func (rc *RepositoryController) addSyncJob(ctx context.Context, obj *provisioning.Repository, syncOptions *provisioning.SyncJobOptions) error { job, err := rc.jobs.Insert(ctx, obj.Namespace, provisioning.JobSpec{ Repository: obj.GetName(), diff --git a/pkg/registry/apis/provisioning/controller/repository_test.go b/pkg/registry/apis/provisioning/controller/repository_test.go index 69f4275fe9e..e5789672e9d 100644 --- a/pkg/registry/apis/provisioning/controller/repository_test.go +++ b/pkg/registry/apis/provisioning/controller/repository_test.go @@ -303,3 +303,38 @@ func TestRepositoryController_handleDelete(t *testing.T) { }) } } + +func TestShouldUseIncrementalSync(t *testing.T) { + versioned := repository.NewMockVersioned(t) + obj := &provisioning.Repository{ + Status: provisioning.RepositoryStatus{ + Sync: provisioning.SyncStatus{ + LastRef: "123", + }, + }, + } + latestRef := "456" + t.Run("should use incremental sync", func(t *testing.T) { + versioned.On("CompareFiles", context.Background(), obj.Status.Sync.LastRef, latestRef).Return([]repository.VersionedFileChange{ + { + Action: repository.FileActionDeleted, + Path: "test.json", + }, + }, nil).Once() + got, err := shouldUseIncrementalSync(context.Background(), versioned, obj, latestRef) + assert.NoError(t, err) + assert.True(t, got) + }) + + t.Run("should not use incremental sync", func(t *testing.T) { + versioned.On("CompareFiles", context.Background(), obj.Status.Sync.LastRef, latestRef).Return([]repository.VersionedFileChange{ + { + Action: repository.FileActionDeleted, + Path: "test/.keep", + }, + }, nil).Once() + got, err := shouldUseIncrementalSync(context.Background(), versioned, obj, latestRef) + assert.NoError(t, err) + assert.False(t, got) + }) +} From 13a3c46079345d38a8bb4e067b9fa63b5636d7e0 Mon Sep 17 00:00:00 2001 From: Ryan McKinley Date: Wed, 8 Oct 2025 14:36:24 +0300 Subject: [PATCH 007/112] DataSource: Fix openapi spec (#112150) --- pkg/apis/datasource/v0alpha1/unstructured.go | 4 + pkg/registry/apis/datasource/register.go | 9 +- .../apis/datasource/sub_query_test.go | 3 - pkg/services/apiserver/builder/openapi.go | 5 +- ...tdata.datasource.grafana.app-v0alpha1.json | 1179 +++++++++++++++++ pkg/tests/apis/openapi_test.go | 3 + 6 files changed, 1191 insertions(+), 12 deletions(-) create mode 100644 pkg/tests/apis/openapi_snapshots/testdata.datasource.grafana.app-v0alpha1.json diff --git a/pkg/apis/datasource/v0alpha1/unstructured.go b/pkg/apis/datasource/v0alpha1/unstructured.go index 67bd2b14530..3b27e47225b 100644 --- a/pkg/apis/datasource/v0alpha1/unstructured.go +++ b/pkg/apis/datasource/v0alpha1/unstructured.go @@ -136,6 +136,10 @@ func (UnstructuredSpec) OpenAPIDefinition() openapi.OpenAPIDefinition { s := schema_pkg_apis_datasource_v0alpha1_GenericDataSourceSpec(func(path string) spec.Ref { return spec.MustCreateRef(path) }) + jsonData := spec.MapProperty(nil) + jsonData.AdditionalProperties = &spec.SchemaOrBool{Allows: true} + jsonData.Extensions = map[string]any{"x-kubernetes-preserve-unknown-fields": true} + s.Schema.Properties["jsonData"] = *jsonData s.Schema.AdditionalProperties = &spec.SchemaOrBool{ Allows: true, } diff --git a/pkg/registry/apis/datasource/register.go b/pkg/registry/apis/datasource/register.go index 454e760a3a3..c48fa6978fb 100644 --- a/pkg/registry/apis/datasource/register.go +++ b/pkg/registry/apis/datasource/register.go @@ -24,7 +24,6 @@ import ( queryV0 "github.com/grafana/grafana/pkg/apis/query/v0alpha1" grafanaregistry "github.com/grafana/grafana/pkg/apiserver/registry/generic" "github.com/grafana/grafana/pkg/configprovider" - "github.com/grafana/grafana/pkg/infra/log" "github.com/grafana/grafana/pkg/plugins" "github.com/grafana/grafana/pkg/plugins/manager/sources" "github.com/grafana/grafana/pkg/promlib/models" @@ -50,7 +49,6 @@ type DataSourceAPIBuilder struct { contextProvider PluginContextWrapper accessControl accesscontrol.AccessControl queryTypes *queryV0.QueryTypeDefinitionList - log log.Logger configCrudUseNewApis bool } @@ -65,10 +63,10 @@ func RegisterAPIService( reg prometheus.Registerer, ) (*DataSourceAPIBuilder, error) { // We want to expose just a limited set of plugins - explictPluginList := features.IsEnabledGlobally(featuremgmt.FlagDatasourceAPIServers) + explicitPluginList := features.IsEnabledGlobally(featuremgmt.FlagDatasourceAPIServers) // This requires devmode! - if !explictPluginList && !features.IsEnabledGlobally(featuremgmt.FlagGrafanaAPIServerWithExperimentalAPIs) { + if !explicitPluginList && !features.IsEnabledGlobally(featuremgmt.FlagGrafanaAPIServerWithExperimentalAPIs) { return nil, nil // skip registration unless opting into experimental apis } @@ -91,7 +89,7 @@ func RegisterAPIService( } for _, pluginJSON := range pluginJSONs { - if explictPluginList && !slices.Contains(ids, pluginJSON.ID) { + if explicitPluginList && !slices.Contains(ids, pluginJSON.ID) { continue // skip this one } @@ -155,7 +153,6 @@ func NewDataSourceAPIBuilder( datasources: datasources, contextProvider: contextProvider, accessControl: accessControl, - log: log.New("grafana-apiserver.datasource"), configCrudUseNewApis: configCrudUseNewApis, } if loadQueryTypes { diff --git a/pkg/registry/apis/datasource/sub_query_test.go b/pkg/registry/apis/datasource/sub_query_test.go index 57aa7626aba..a3871ae954f 100644 --- a/pkg/registry/apis/datasource/sub_query_test.go +++ b/pkg/registry/apis/datasource/sub_query_test.go @@ -15,7 +15,6 @@ import ( "github.com/grafana/grafana-plugin-sdk-go/backend" "github.com/grafana/grafana/pkg/apis/datasource/v0alpha1" queryV0 "github.com/grafana/grafana/pkg/apis/query/v0alpha1" - "github.com/grafana/grafana/pkg/infra/log" "github.com/grafana/grafana/pkg/services/datasources" "github.com/grafana/grafana/pkg/services/ngalert/models" ) @@ -28,7 +27,6 @@ func TestSubQueryConnect(t *testing.T) { }, datasources: mockDatasources{}, contextProvider: mockContextProvider{}, - log: log.NewNopLogger(), }, } @@ -73,7 +71,6 @@ func TestSubQueryConnectWhenDatasourceNotFound(t *testing.T) { }, datasources: mockDatasources{}, contextProvider: mockContextProvider{}, - log: log.NewNopLogger(), }, } diff --git a/pkg/services/apiserver/builder/openapi.go b/pkg/services/apiserver/builder/openapi.go index 5326ea071e2..9cb7cc17f7c 100644 --- a/pkg/services/apiserver/builder/openapi.go +++ b/pkg/services/apiserver/builder/openapi.go @@ -201,10 +201,9 @@ func getOpenAPIPostProcessor(version string, builders []APIGroupBuilder, gvs []s keep := make([]map[string]any, 0, len(gvks)) for _, val := range gvks { gvk, ok := val.(map[string]any) - if ok && gvk["version"] == "__internal" { - continue + if ok && gvk["group"] == gv.Group && gvk["version"] != "__internal" { + keep = append(keep, gvk) // only expose real versions in the same group } - keep = append(keep, gvk) } v.Extensions["x-kubernetes-group-version-kind"] = keep } diff --git a/pkg/tests/apis/openapi_snapshots/testdata.datasource.grafana.app-v0alpha1.json b/pkg/tests/apis/openapi_snapshots/testdata.datasource.grafana.app-v0alpha1.json new file mode 100644 index 00000000000..4249cf16db0 --- /dev/null +++ b/pkg/tests/apis/openapi_snapshots/testdata.datasource.grafana.app-v0alpha1.json @@ -0,0 +1,1179 @@ +{ + "openapi": "3.0.0", + "info": { + "description": "Generates test data in different forms", + "title": "testdata.datasource.grafana.app/v0alpha1" + }, + "paths": { + "/apis/testdata.datasource.grafana.app/v0alpha1/": { + "get": { + "tags": [ + "API Discovery" + ], + "description": "Describe the available kubernetes resources", + "operationId": "getAPIResources", + "responses": { + "200": { + "description": "OK", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/io.k8s.apimachinery.pkg.apis.meta.v1.APIResourceList" + } + }, + "application/vnd.kubernetes.protobuf": { + "schema": { + "$ref": "#/components/schemas/io.k8s.apimachinery.pkg.apis.meta.v1.APIResourceList" + } + }, + "application/yaml": { + "schema": { + "$ref": "#/components/schemas/io.k8s.apimachinery.pkg.apis.meta.v1.APIResourceList" + } + } + } + } + } + } + }, + "/apis/testdata.datasource.grafana.app/v0alpha1/namespaces/{namespace}/connections/{name}/query": { + "post": { + "tags": [ + "Connections (deprecated)" + ], + "description": "connect POST requests to query of QueryDataResponse", + "operationId": "createQueryDataResponseQuery", + "requestBody": { + "content": { + "application/json": { + "schema": { + "type": "object", + "additionalProperties": true + } + } + } + }, + "responses": { + "200": { + "description": "OK", + "content": { + "*/*": { + "schema": { + "$ref": "#/components/schemas/com.github.grafana.grafana.pkg.apis.query.v0alpha1.QueryDataResponse" + } + } + } + } + }, + "deprecated": true, + "x-kubernetes-action": "connect", + "x-kubernetes-group-version-kind": { + "group": "testdata.datasource.grafana.app", + "version": "v0alpha1", + "kind": "QueryDataResponse" + } + }, + "parameters": [ + { + "name": "name", + "in": "path", + "description": "name of the QueryDataResponse", + "required": true, + "schema": { + "type": "string", + "uniqueItems": true + } + }, + { + "name": "namespace", + "in": "path", + "description": "object name and auth scope, such as for teams and projects", + "required": true, + "schema": { + "type": "string", + "uniqueItems": true + } + } + ] + }, + "/apis/testdata.datasource.grafana.app/v0alpha1/namespaces/{namespace}/datasources": { + "get": { + "tags": [ + "DataSource" + ], + "description": "list objects of kind DataSource", + "operationId": "listDataSource", + "responses": { + "200": { + "description": "OK", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/com.github.grafana.grafana.pkg.apis.datasource.v0alpha1.DataSourceList" + } + }, + "application/json;stream=watch": { + "schema": { + "$ref": "#/components/schemas/com.github.grafana.grafana.pkg.apis.datasource.v0alpha1.DataSourceList" + } + }, + "application/vnd.kubernetes.protobuf": { + "schema": { + "$ref": "#/components/schemas/com.github.grafana.grafana.pkg.apis.datasource.v0alpha1.DataSourceList" + } + }, + "application/vnd.kubernetes.protobuf;stream=watch": { + "schema": { + "$ref": "#/components/schemas/com.github.grafana.grafana.pkg.apis.datasource.v0alpha1.DataSourceList" + } + }, + "application/yaml": { + "schema": { + "$ref": "#/components/schemas/com.github.grafana.grafana.pkg.apis.datasource.v0alpha1.DataSourceList" + } + } + } + } + }, + "x-kubernetes-action": "list", + "x-kubernetes-group-version-kind": { + "group": "testdata.datasource.grafana.app", + "version": "v0alpha1", + "kind": "DataSource" + } + }, + "parameters": [ + { + "name": "allowWatchBookmarks", + "in": "query", + "description": "allowWatchBookmarks requests watch events with type \"BOOKMARK\". Servers that do not implement bookmarks may ignore this flag and bookmarks are sent at the server's discretion. Clients should not assume bookmarks are returned at any specific interval, nor may they assume the server will send any BOOKMARK event during a session. If this is not a watch, this field is ignored.", + "schema": { + "type": "boolean", + "uniqueItems": true + } + }, + { + "name": "continue", + "in": "query", + "description": "The continue option should be set when retrieving more results from the server. Since this value is server defined, clients may only use the continue value from a previous query result with identical query parameters (except for the value of continue) and the server may reject a continue value it does not recognize. If the specified continue value is no longer valid whether due to expiration (generally five to fifteen minutes) or a configuration change on the server, the server will respond with a 410 ResourceExpired error together with a continue token. If the client needs a consistent list, it must restart their list without the continue field. Otherwise, the client may send another list request with the token received with the 410 error, the server will respond with a list starting from the next key, but from the latest snapshot, which is inconsistent from the previous list results - objects that are created, modified, or deleted after the first list request will be included in the response, as long as their keys are after the \"next key\".\n\nThis field is not supported when watch is true. Clients may start a watch from the last resourceVersion value returned by the server and not miss any modifications.", + "schema": { + "type": "string", + "uniqueItems": true + } + }, + { + "name": "fieldSelector", + "in": "query", + "description": "A selector to restrict the list of returned objects by their fields. Defaults to everything.", + "schema": { + "type": "string", + "uniqueItems": true + } + }, + { + "name": "labelSelector", + "in": "query", + "description": "A selector to restrict the list of returned objects by their labels. Defaults to everything.", + "schema": { + "type": "string", + "uniqueItems": true + } + }, + { + "name": "limit", + "in": "query", + "description": "limit is a maximum number of responses to return for a list call. If more items exist, the server will set the `continue` field on the list metadata to a value that can be used with the same initial query to retrieve the next set of results. Setting a limit may return fewer than the requested amount of items (up to zero items) in the event all requested objects are filtered out and clients should only use the presence of the continue field to determine whether more results are available. Servers may choose not to support the limit argument and will return all of the available results. If limit is specified and the continue field is empty, clients may assume that no more results are available. This field is not supported if watch is true.\n\nThe server guarantees that the objects returned when using continue will be identical to issuing a single list call without a limit - that is, no objects created, modified, or deleted after the first request is issued will be included in any subsequent continued requests. This is sometimes referred to as a consistent snapshot, and ensures that a client that is using limit to receive smaller chunks of a very large result can ensure they see all possible objects. If objects are updated during a chunked list the version of the object that was present at the time the first list result was calculated is returned.", + "schema": { + "type": "integer", + "uniqueItems": true + } + }, + { + "name": "namespace", + "in": "path", + "description": "object name and auth scope, such as for teams and projects", + "required": true, + "schema": { + "type": "string", + "uniqueItems": true + } + }, + { + "name": "pretty", + "in": "query", + "description": "If 'true', then the output is pretty printed. Defaults to 'false' unless the user-agent indicates a browser or command-line HTTP tool (curl and wget).", + "schema": { + "type": "string", + "uniqueItems": true + } + }, + { + "name": "resourceVersion", + "in": "query", + "description": "resourceVersion sets a constraint on what resource versions a request may be served from. See https://kubernetes.io/docs/reference/using-api/api-concepts/#resource-versions for details.\n\nDefaults to unset", + "schema": { + "type": "string", + "uniqueItems": true + } + }, + { + "name": "resourceVersionMatch", + "in": "query", + "description": "resourceVersionMatch determines how resourceVersion is applied to list calls. It is highly recommended that resourceVersionMatch be set for list calls where resourceVersion is set See https://kubernetes.io/docs/reference/using-api/api-concepts/#resource-versions for details.\n\nDefaults to unset", + "schema": { + "type": "string", + "uniqueItems": true + } + }, + { + "name": "sendInitialEvents", + "in": "query", + "description": "`sendInitialEvents=true` may be set together with `watch=true`. In that case, the watch stream will begin with synthetic events to produce the current state of objects in the collection. Once all such events have been sent, a synthetic \"Bookmark\" event will be sent. The bookmark will report the ResourceVersion (RV) corresponding to the set of objects, and be marked with `\"k8s.io/initial-events-end\": \"true\"` annotation. Afterwards, the watch stream will proceed as usual, sending watch events corresponding to changes (subsequent to the RV) to objects watched.\n\nWhen `sendInitialEvents` option is set, we require `resourceVersionMatch` option to also be set. The semantic of the watch request is as following: - `resourceVersionMatch` = NotOlderThan\n is interpreted as \"data at least as new as the provided `resourceVersion`\"\n and the bookmark event is send when the state is synced\n to a `resourceVersion` at least as fresh as the one provided by the ListOptions.\n If `resourceVersion` is unset, this is interpreted as \"consistent read\" and the\n bookmark event is send when the state is synced at least to the moment\n when request started being processed.\n- `resourceVersionMatch` set to any other value or unset\n Invalid error is returned.\n\nDefaults to true if `resourceVersion=\"\"` or `resourceVersion=\"0\"` (for backward compatibility reasons) and to false otherwise.", + "schema": { + "type": "boolean", + "uniqueItems": true + } + }, + { + "name": "timeoutSeconds", + "in": "query", + "description": "Timeout for the list/watch call. This limits the duration of the call, regardless of any activity or inactivity.", + "schema": { + "type": "integer", + "uniqueItems": true + } + }, + { + "name": "watch", + "in": "query", + "description": "Watch for changes to the described resources and return them as a stream of add, update, and remove notifications. Specify resourceVersion.", + "schema": { + "type": "boolean", + "uniqueItems": true + } + } + ] + }, + "/apis/testdata.datasource.grafana.app/v0alpha1/namespaces/{namespace}/datasources/{name}": { + "get": { + "tags": [ + "DataSource" + ], + "description": "read the specified DataSource", + "operationId": "getDataSource", + "responses": { + "200": { + "description": "OK", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/com.github.grafana.grafana.pkg.apis.datasource.v0alpha1.DataSource" + } + }, + "application/vnd.kubernetes.protobuf": { + "schema": { + "$ref": "#/components/schemas/com.github.grafana.grafana.pkg.apis.datasource.v0alpha1.DataSource" + } + }, + "application/yaml": { + "schema": { + "$ref": "#/components/schemas/com.github.grafana.grafana.pkg.apis.datasource.v0alpha1.DataSource" + } + } + } + } + }, + "x-kubernetes-action": "get", + "x-kubernetes-group-version-kind": { + "group": "testdata.datasource.grafana.app", + "version": "v0alpha1", + "kind": "DataSource" + } + }, + "parameters": [ + { + "name": "name", + "in": "path", + "description": "name of the DataSource", + "required": true, + "schema": { + "type": "string", + "uniqueItems": true + } + }, + { + "name": "namespace", + "in": "path", + "description": "object name and auth scope, such as for teams and projects", + "required": true, + "schema": { + "type": "string", + "uniqueItems": true + } + }, + { + "name": "pretty", + "in": "query", + "description": "If 'true', then the output is pretty printed. Defaults to 'false' unless the user-agent indicates a browser or command-line HTTP tool (curl and wget).", + "schema": { + "type": "string", + "uniqueItems": true + } + } + ] + }, + "/apis/testdata.datasource.grafana.app/v0alpha1/namespaces/{namespace}/datasources/{name}/health": { + "get": { + "tags": [ + "DataSource" + ], + "description": "connect GET requests to health of DataSource", + "operationId": "getDataSourceHealth", + "responses": { + "200": { + "description": "OK", + "content": { + "*/*": { + "schema": { + "$ref": "#/components/schemas/com.github.grafana.grafana.pkg.apis.datasource.v0alpha1.HealthCheckResult" + } + } + } + } + }, + "x-kubernetes-action": "connect", + "x-kubernetes-group-version-kind": { + "group": "testdata.datasource.grafana.app", + "version": "v0alpha1", + "kind": "HealthCheckResult" + } + }, + "parameters": [ + { + "name": "name", + "in": "path", + "description": "name of the HealthCheckResult", + "required": true, + "schema": { + "type": "string", + "uniqueItems": true + } + }, + { + "name": "namespace", + "in": "path", + "description": "object name and auth scope, such as for teams and projects", + "required": true, + "schema": { + "type": "string", + "uniqueItems": true + } + } + ] + }, + "/apis/testdata.datasource.grafana.app/v0alpha1/namespaces/{namespace}/datasources/{name}/query": { + "post": { + "tags": [ + "DataSource" + ], + "description": "Query the TestData datasources", + "operationId": "createDataSourceQuery", + "requestBody": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/QueryRequestSchema" + } + } + } + }, + "responses": { + "200": { + "description": "OK", + "content": { + "*/*": { + "schema": { + "$ref": "#/components/schemas/com.github.grafana.grafana.pkg.apis.query.v0alpha1.QueryDataResponse" + } + } + } + } + }, + "x-kubernetes-action": "connect", + "x-kubernetes-group-version-kind": { + "group": "testdata.datasource.grafana.app", + "version": "v0alpha1", + "kind": "QueryDataResponse" + } + }, + "parameters": [ + { + "name": "name", + "in": "path", + "description": "name of the QueryDataResponse", + "required": true, + "schema": { + "type": "string", + "uniqueItems": true + } + }, + { + "name": "namespace", + "in": "path", + "description": "object name and auth scope, such as for teams and projects", + "required": true, + "schema": { + "type": "string", + "uniqueItems": true + } + } + ] + }, + "/apis/testdata.datasource.grafana.app/v0alpha1/namespaces/{namespace}/datasources/{name}/resource": { + "get": { + "tags": [ + "DataSource" + ], + "description": "Get resources in the datasource plugin. NOTE, additional routes may exist, but are not exposed via OpenAPI", + "operationId": "getDataSourceResource", + "responses": { + "200": { + "description": "OK", + "content": { + "*/*": { + "schema": { + "type": "string" + } + } + } + } + }, + "x-kubernetes-action": "connect", + "x-kubernetes-group-version-kind": { + "group": "testdata.datasource.grafana.app", + "version": "v0alpha1", + "kind": "Status" + } + }, + "parameters": [ + { + "name": "name", + "in": "path", + "description": "name of the Status", + "required": true, + "schema": { + "type": "string", + "uniqueItems": true + } + }, + { + "name": "namespace", + "in": "path", + "description": "object name and auth scope, such as for teams and projects", + "required": true, + "schema": { + "type": "string", + "uniqueItems": true + } + } + ] + }, + "/apis/testdata.datasource.grafana.app/v0alpha1/namespaces/{namespace}/queryconvert/{name}": { + "post": { + "tags": [ + "QueryDataRequest" + ], + "description": "connect POST requests to QueryDataRequest", + "operationId": "createQueryDataRequest", + "responses": { + "200": { + "description": "OK", + "content": { + "*/*": { + "schema": { + "type": "string" + } + } + } + } + }, + "x-kubernetes-action": "connect", + "x-kubernetes-group-version-kind": { + "group": "testdata.datasource.grafana.app", + "version": "v0alpha1", + "kind": "QueryDataRequest" + } + }, + "parameters": [ + { + "name": "name", + "in": "path", + "description": "name of the QueryDataRequest", + "required": true, + "schema": { + "type": "string", + "uniqueItems": true + } + }, + { + "name": "namespace", + "in": "path", + "description": "object name and auth scope, such as for teams and projects", + "required": true, + "schema": { + "type": "string", + "uniqueItems": true + } + } + ] + } + }, + "components": { + "schemas": { + "QueryRequestSchema": { + "description": "Schema for a set of queries sent to the query method", + "type": "object", + "required": [ + "queries" + ], + "properties": { + "$schema": { + "description": "helper", + "type": "string" + }, + "debug": { + "type": "boolean" + }, + "from": { + "description": "From Start time in epoch timestamps in milliseconds or relative using Grafana time units.", + "type": "string" + }, + "queries": { + "type": "array", + "items": { + "type": "object" + } + }, + "to": { + "description": "To end time in epoch timestamps in milliseconds or relative using Grafana time units.", + "type": "string" + } + }, + "additionalProperties": false + }, + "com.github.grafana.grafana-plugin-sdk-go.backend.DataResponse": { + "description": "todo... improve schema", + "type": "object", + "additionalProperties": true + }, + "com.github.grafana.grafana.pkg.apimachinery.apis.common.v0alpha1.InlineSecureValue": { + "description": "Allow access to a secure value inside", + "oneOf": [ + { + "required": [ + "name" + ] + }, + { + "required": [ + "create" + ] + }, + { + "required": [ + "remove" + ] + } + ], + "properties": { + "create": { + "description": "Create a secure value -- this is only used for POST/PUT", + "type": "string", + "maxLength": 24576, + "minLength": 1 + }, + "name": { + "description": "Name in the secret service (reference)", + "type": "string", + "maxLength": 253, + "minLength": 1 + }, + "remove": { + "description": "Remove this value from the secure value map Values owned by this resource will be deleted if necessary", + "type": "boolean" + } + }, + "additionalProperties": false + }, + "com.github.grafana.grafana.pkg.apimachinery.apis.common.v0alpha1.Unstructured": { + "type": "object", + "additionalProperties": true, + "x-kubernetes-preserve-unknown-fields": true + }, + "com.github.grafana.grafana.pkg.apis.datasource.v0alpha1.DataSource": { + "type": "object", + "required": [ + "metadata", + "spec" + ], + "properties": { + "apiVersion": { + "type": "string", + "enum": [ + "testdata.datasource.grafana.app/v0alpha1" + ] + }, + "kind": { + "type": "string", + "enum": [ + "DataSource" + ] + }, + "metadata": { + "default": {}, + "allOf": [ + { + "$ref": "#/components/schemas/io.k8s.apimachinery.pkg.apis.meta.v1.ObjectMeta" + } + ] + }, + "secure": { + "description": "Secure values allows setting values that are never shown to users The returned properties are only the names of the configured values", + "type": "object", + "additionalProperties": { + "default": {}, + "allOf": [ + { + "$ref": "#/components/schemas/com.github.grafana.grafana.pkg.apimachinery.apis.common.v0alpha1.InlineSecureValue" + } + ] + } + }, + "spec": { + "description": "DataSource configuration -- these properties are all visible to anyone able to query the data source from their browser", + "allOf": [ + { + "$ref": "#/components/schemas/com.github.grafana.grafana.pkg.apis.datasource.v0alpha1.UnstructuredSpec" + } + ] + } + }, + "x-kubernetes-group-version-kind": [ + { + "group": "testdata.datasource.grafana.app", + "kind": "DataSource", + "version": "v0alpha1" + } + ] + }, + "com.github.grafana.grafana.pkg.apis.datasource.v0alpha1.DataSourceList": { + "type": "object", + "required": [ + "metadata", + "items" + ], + "properties": { + "apiVersion": { + "description": "APIVersion defines the versioned schema of this representation of an object. Servers should convert recognized schemas to the latest internal value, and may reject unrecognized values. More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#resources", + "type": "string" + }, + "items": { + "type": "array", + "items": { + "default": {}, + "allOf": [ + { + "$ref": "#/components/schemas/com.github.grafana.grafana.pkg.apis.datasource.v0alpha1.DataSource" + } + ] + } + }, + "kind": { + "description": "Kind is a string value representing the REST resource this object represents. Servers may infer this from the endpoint the client submits requests to. Cannot be updated. In CamelCase. More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#types-kinds", + "type": "string" + }, + "metadata": { + "default": {}, + "allOf": [ + { + "$ref": "#/components/schemas/io.k8s.apimachinery.pkg.apis.meta.v1.ListMeta" + } + ] + } + }, + "x-kubernetes-group-version-kind": [ + { + "group": "testdata.datasource.grafana.app", + "kind": "DataSourceList", + "version": "v0alpha1" + } + ] + }, + "com.github.grafana.grafana.pkg.apis.datasource.v0alpha1.HealthCheckResult": { + "type": "object", + "properties": { + "apiVersion": { + "description": "APIVersion defines the versioned schema of this representation of an object. Servers should convert recognized schemas to the latest internal value, and may reject unrecognized values. More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#resources", + "type": "string" + }, + "code": { + "description": "Explicit status code", + "type": "integer", + "format": "int32" + }, + "details": { + "description": "Spec depends on the plugin", + "allOf": [ + { + "$ref": "#/components/schemas/com.github.grafana.grafana.pkg.apimachinery.apis.common.v0alpha1.Unstructured" + } + ] + }, + "kind": { + "description": "Kind is a string value representing the REST resource this object represents. Servers may infer this from the endpoint the client submits requests to. Cannot be updated. In CamelCase. More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#types-kinds", + "type": "string" + }, + "message": { + "description": "Optional description for the data source", + "type": "string" + }, + "status": { + "description": "The string description", + "type": "string" + } + }, + "x-kubernetes-group-version-kind": [ + { + "group": "testdata.datasource.grafana.app", + "kind": "HealthCheckResult", + "version": "v0alpha1" + } + ] + }, + "com.github.grafana.grafana.pkg.apis.datasource.v0alpha1.UnstructuredSpec": { + "type": "object", + "required": [ + "title", + "jsonData" + ], + "properties": { + "access": { + "description": "Possible enum values:\n - `\"direct\"` The frontend can connect directly to the remote URL This method is discouraged\n - `\"proxy\"` Connect to the remote datasource through the grafana backend", + "type": "string", + "enum": [ + "direct", + "proxy" + ] + }, + "basicAuth": { + "type": "boolean" + }, + "basicAuthUser": { + "type": "string" + }, + "database": { + "type": "string" + }, + "isDefault": { + "type": "boolean" + }, + "jsonData": { + "type": "object", + "additionalProperties": true, + "x-kubernetes-preserve-unknown-fields": true + }, + "readOnly": { + "type": "boolean" + }, + "title": { + "description": "The display name (previously saved as the \"name\" property)", + "type": "string", + "default": "" + }, + "url": { + "description": "Server URL", + "type": "string" + }, + "user": { + "type": "string" + }, + "withCredentials": { + "type": "boolean" + } + }, + "additionalProperties": true + }, + "com.github.grafana.grafana.pkg.apis.query.v0alpha1.QueryDataResponse": { + "description": "Wraps backend.QueryDataResponse, however it includes TypeMeta and implements runtime.Object", + "type": "object", + "required": [ + "results" + ], + "properties": { + "apiVersion": { + "description": "APIVersion defines the versioned schema of this representation of an object. Servers should convert recognized schemas to the latest internal value, and may reject unrecognized values. More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#resources", + "type": "string" + }, + "kind": { + "description": "Kind is a string value representing the REST resource this object represents. Servers may infer this from the endpoint the client submits requests to. Cannot be updated. In CamelCase. More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#types-kinds", + "type": "string" + }, + "results": { + "description": "Responses is a map of RefIDs (Unique Query ID) to *DataResponse.", + "type": "object", + "additionalProperties": { + "default": {}, + "allOf": [ + { + "$ref": "#/components/schemas/com.github.grafana.grafana-plugin-sdk-go.backend.DataResponse" + } + ] + } + } + }, + "x-kubernetes-group-version-kind": [ + { + "group": "testdata.datasource.grafana.app", + "kind": "QueryDataResponse", + "version": "v0alpha1" + } + ] + }, + "io.k8s.apimachinery.pkg.apis.meta.v1.APIResource": { + "description": "APIResource specifies the name of a resource and whether it is namespaced.", + "type": "object", + "required": [ + "name", + "singularName", + "namespaced", + "kind", + "verbs" + ], + "properties": { + "categories": { + "description": "categories is a list of the grouped resources this resource belongs to (e.g. 'all')", + "type": "array", + "items": { + "type": "string", + "default": "" + }, + "x-kubernetes-list-type": "atomic" + }, + "group": { + "description": "group is the preferred group of the resource. Empty implies the group of the containing resource list. For subresources, this may have a different value, for example: Scale\".", + "type": "string" + }, + "kind": { + "description": "kind is the kind for the resource (e.g. 'Foo' is the kind for a resource 'foo')", + "type": "string", + "default": "" + }, + "name": { + "description": "name is the plural name of the resource.", + "type": "string", + "default": "" + }, + "namespaced": { + "description": "namespaced indicates if a resource is namespaced or not.", + "type": "boolean", + "default": false + }, + "shortNames": { + "description": "shortNames is a list of suggested short names of the resource.", + "type": "array", + "items": { + "type": "string", + "default": "" + }, + "x-kubernetes-list-type": "atomic" + }, + "singularName": { + "description": "singularName is the singular name of the resource. This allows clients to handle plural and singular opaquely. The singularName is more correct for reporting status on a single item and both singular and plural are allowed from the kubectl CLI interface.", + "type": "string", + "default": "" + }, + "storageVersionHash": { + "description": "The hash value of the storage version, the version this resource is converted to when written to the data store. Value must be treated as opaque by clients. Only equality comparison on the value is valid. This is an alpha feature and may change or be removed in the future. The field is populated by the apiserver only if the StorageVersionHash feature gate is enabled. This field will remain optional even if it graduates.", + "type": "string" + }, + "verbs": { + "description": "verbs is a list of supported kube verbs (this includes get, list, watch, create, update, patch, delete, deletecollection, and proxy)", + "type": "array", + "items": { + "type": "string", + "default": "" + } + }, + "version": { + "description": "version is the preferred version of the resource. Empty implies the version of the containing resource list For subresources, this may have a different value, for example: v1 (while inside a v1beta1 version of the core resource's group)\".", + "type": "string" + } + } + }, + "io.k8s.apimachinery.pkg.apis.meta.v1.APIResourceList": { + "description": "APIResourceList is a list of APIResource, it is used to expose the name of the resources supported in a specific group and version, and if the resource is namespaced.", + "type": "object", + "required": [ + "groupVersion", + "resources" + ], + "properties": { + "apiVersion": { + "description": "APIVersion defines the versioned schema of this representation of an object. Servers should convert recognized schemas to the latest internal value, and may reject unrecognized values. More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#resources", + "type": "string" + }, + "groupVersion": { + "description": "groupVersion is the group and version this APIResourceList is for.", + "type": "string", + "default": "" + }, + "kind": { + "description": "Kind is a string value representing the REST resource this object represents. Servers may infer this from the endpoint the client submits requests to. Cannot be updated. In CamelCase. More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#types-kinds", + "type": "string" + }, + "resources": { + "description": "resources contains the name of the resources and if they are namespaced.", + "type": "array", + "items": { + "default": {}, + "allOf": [ + { + "$ref": "#/components/schemas/io.k8s.apimachinery.pkg.apis.meta.v1.APIResource" + } + ] + }, + "x-kubernetes-list-type": "atomic" + } + } + }, + "io.k8s.apimachinery.pkg.apis.meta.v1.FieldsV1": { + "description": "FieldsV1 stores a set of fields in a data structure like a Trie, in JSON format.\n\nEach key is either a '.' representing the field itself, and will always map to an empty set, or a string representing a sub-field or item. The string will follow one of these four formats: 'f:\u003cname\u003e', where \u003cname\u003e is the name of a field in a struct, or key in a map 'v:\u003cvalue\u003e', where \u003cvalue\u003e is the exact json formatted value of a list item 'i:\u003cindex\u003e', where \u003cindex\u003e is position of a item in a list 'k:\u003ckeys\u003e', where \u003ckeys\u003e is a map of a list item's key fields to their unique values If a key maps to an empty Fields value, the field that key represents is part of the set.\n\nThe exact format is defined in sigs.k8s.io/structured-merge-diff", + "type": "object" + }, + "io.k8s.apimachinery.pkg.apis.meta.v1.ListMeta": { + "description": "ListMeta describes metadata that synthetic resources must have, including lists and various status objects. A resource may have only one of {ObjectMeta, ListMeta}.", + "type": "object", + "properties": { + "continue": { + "description": "continue may be set if the user set a limit on the number of items returned, and indicates that the server has more data available. The value is opaque and may be used to issue another request to the endpoint that served this list to retrieve the next set of available objects. Continuing a consistent list may not be possible if the server configuration has changed or more than a few minutes have passed. The resourceVersion field returned when using this continue value will be identical to the value in the first response, unless you have received this token from an error message.", + "type": "string" + }, + "remainingItemCount": { + "description": "remainingItemCount is the number of subsequent items in the list which are not included in this list response. If the list request contained label or field selectors, then the number of remaining items is unknown and the field will be left unset and omitted during serialization. If the list is complete (either because it is not chunking or because this is the last chunk), then there are no more remaining items and this field will be left unset and omitted during serialization. Servers older than v1.15 do not set this field. The intended use of the remainingItemCount is *estimating* the size of a collection. Clients should not rely on the remainingItemCount to be set or to be exact.", + "type": "integer", + "format": "int64" + }, + "resourceVersion": { + "description": "String that identifies the server's internal version of this object that can be used by clients to determine when objects have changed. Value must be treated as opaque by clients and passed unmodified back to the server. Populated by the system. Read-only. More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#concurrency-control-and-consistency", + "type": "string" + }, + "selfLink": { + "description": "Deprecated: selfLink is a legacy read-only field that is no longer populated by the system.", + "type": "string" + } + } + }, + "io.k8s.apimachinery.pkg.apis.meta.v1.ManagedFieldsEntry": { + "description": "ManagedFieldsEntry is a workflow-id, a FieldSet and the group version of the resource that the fieldset applies to.", + "type": "object", + "properties": { + "apiVersion": { + "description": "APIVersion defines the version of this resource that this field set applies to. The format is \"group/version\" just like the top-level APIVersion field. It is necessary to track the version of a field set because it cannot be automatically converted.", + "type": "string" + }, + "fieldsType": { + "description": "FieldsType is the discriminator for the different fields format and version. There is currently only one possible value: \"FieldsV1\"", + "type": "string" + }, + "fieldsV1": { + "description": "FieldsV1 holds the first JSON version format as described in the \"FieldsV1\" type.", + "allOf": [ + { + "$ref": "#/components/schemas/io.k8s.apimachinery.pkg.apis.meta.v1.FieldsV1" + } + ] + }, + "manager": { + "description": "Manager is an identifier of the workflow managing these fields.", + "type": "string" + }, + "operation": { + "description": "Operation is the type of operation which lead to this ManagedFieldsEntry being created. The only valid values for this field are 'Apply' and 'Update'.", + "type": "string" + }, + "subresource": { + "description": "Subresource is the name of the subresource used to update that object, or empty string if the object was updated through the main resource. The value of this field is used to distinguish between managers, even if they share the same name. For example, a status update will be distinct from a regular update using the same manager name. Note that the APIVersion field is not related to the Subresource field and it always corresponds to the version of the main resource.", + "type": "string" + }, + "time": { + "description": "Time is the timestamp of when the ManagedFields entry was added. The timestamp will also be updated if a field is added, the manager changes any of the owned fields value or removes a field. The timestamp does not update when a field is removed from the entry because another manager took it over.", + "allOf": [ + { + "$ref": "#/components/schemas/io.k8s.apimachinery.pkg.apis.meta.v1.Time" + } + ] + } + } + }, + "io.k8s.apimachinery.pkg.apis.meta.v1.ObjectMeta": { + "description": "ObjectMeta is metadata that all persisted resources must have, which includes all objects users must create.", + "type": "object", + "properties": { + "annotations": { + "description": "Annotations is an unstructured key value map stored with a resource that may be set by external tools to store and retrieve arbitrary metadata. They are not queryable and should be preserved when modifying objects. More info: https://kubernetes.io/docs/concepts/overview/working-with-objects/annotations", + "type": "object", + "additionalProperties": { + "type": "string", + "default": "" + } + }, + "creationTimestamp": { + "description": "CreationTimestamp is a timestamp representing the server time when this object was created. It is not guaranteed to be set in happens-before order across separate operations. Clients may not set this value. It is represented in RFC3339 form and is in UTC.\n\nPopulated by the system. Read-only. Null for lists. More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#metadata", + "allOf": [ + { + "$ref": "#/components/schemas/io.k8s.apimachinery.pkg.apis.meta.v1.Time" + } + ] + }, + "deletionGracePeriodSeconds": { + "description": "Number of seconds allowed for this object to gracefully terminate before it will be removed from the system. Only set when deletionTimestamp is also set. May only be shortened. Read-only.", + "type": "integer", + "format": "int64" + }, + "deletionTimestamp": { + "description": "DeletionTimestamp is RFC 3339 date and time at which this resource will be deleted. This field is set by the server when a graceful deletion is requested by the user, and is not directly settable by a client. The resource is expected to be deleted (no longer visible from resource lists, and not reachable by name) after the time in this field, once the finalizers list is empty. As long as the finalizers list contains items, deletion is blocked. Once the deletionTimestamp is set, this value may not be unset or be set further into the future, although it may be shortened or the resource may be deleted prior to this time. For example, a user may request that a pod is deleted in 30 seconds. The Kubelet will react by sending a graceful termination signal to the containers in the pod. After that 30 seconds, the Kubelet will send a hard termination signal (SIGKILL) to the container and after cleanup, remove the pod from the API. In the presence of network partitions, this object may still exist after this timestamp, until an administrator or automated process can determine the resource is fully terminated. If not set, graceful deletion of the object has not been requested.\n\nPopulated by the system when a graceful deletion is requested. Read-only. More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#metadata", + "allOf": [ + { + "$ref": "#/components/schemas/io.k8s.apimachinery.pkg.apis.meta.v1.Time" + } + ] + }, + "finalizers": { + "description": "Must be empty before the object is deleted from the registry. Each entry is an identifier for the responsible component that will remove the entry from the list. If the deletionTimestamp of the object is non-nil, entries in this list can only be removed. Finalizers may be processed and removed in any order. Order is NOT enforced because it introduces significant risk of stuck finalizers. finalizers is a shared field, any actor with permission can reorder it. If the finalizer list is processed in order, then this can lead to a situation in which the component responsible for the first finalizer in the list is waiting for a signal (field value, external system, or other) produced by a component responsible for a finalizer later in the list, resulting in a deadlock. Without enforced ordering finalizers are free to order amongst themselves and are not vulnerable to ordering changes in the list.", + "type": "array", + "items": { + "type": "string", + "default": "" + }, + "x-kubernetes-list-type": "set", + "x-kubernetes-patch-strategy": "merge" + }, + "generateName": { + "description": "GenerateName is an optional prefix, used by the server, to generate a unique name ONLY IF the Name field has not been provided. If this field is used, the name returned to the client will be different than the name passed. This value will also be combined with a unique suffix. The provided value has the same validation rules as the Name field, and may be truncated by the length of the suffix required to make the value unique on the server.\n\nIf this field is specified and the generated name exists, the server will return a 409.\n\nApplied only if Name is not specified. More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#idempotency", + "type": "string" + }, + "generation": { + "description": "A sequence number representing a specific generation of the desired state. Populated by the system. Read-only.", + "type": "integer", + "format": "int64" + }, + "labels": { + "description": "Map of string keys and values that can be used to organize and categorize (scope and select) objects. May match selectors of replication controllers and services. More info: https://kubernetes.io/docs/concepts/overview/working-with-objects/labels", + "type": "object", + "additionalProperties": { + "type": "string", + "default": "" + } + }, + "managedFields": { + "description": "ManagedFields maps workflow-id and version to the set of fields that are managed by that workflow. This is mostly for internal housekeeping, and users typically shouldn't need to set or understand this field. A workflow can be the user's name, a controller's name, or the name of a specific apply path like \"ci-cd\". The set of fields is always in the version that the workflow used when modifying the object.", + "type": "array", + "items": { + "default": {}, + "allOf": [ + { + "$ref": "#/components/schemas/io.k8s.apimachinery.pkg.apis.meta.v1.ManagedFieldsEntry" + } + ] + }, + "x-kubernetes-list-type": "atomic" + }, + "name": { + "description": "Name must be unique within a namespace. Is required when creating resources, although some resources may allow a client to request the generation of an appropriate name automatically. Name is primarily intended for creation idempotence and configuration definition. Cannot be updated. More info: https://kubernetes.io/docs/concepts/overview/working-with-objects/names#names", + "type": "string" + }, + "namespace": { + "description": "Namespace defines the space within which each name must be unique. An empty namespace is equivalent to the \"default\" namespace, but \"default\" is the canonical representation. Not all objects are required to be scoped to a namespace - the value of this field for those objects will be empty.\n\nMust be a DNS_LABEL. Cannot be updated. More info: https://kubernetes.io/docs/concepts/overview/working-with-objects/namespaces", + "type": "string" + }, + "ownerReferences": { + "description": "List of objects depended by this object. If ALL objects in the list have been deleted, this object will be garbage collected. If this object is managed by a controller, then an entry in this list will point to this controller, with the controller field set to true. There cannot be more than one managing controller.", + "type": "array", + "items": { + "default": {}, + "allOf": [ + { + "$ref": "#/components/schemas/io.k8s.apimachinery.pkg.apis.meta.v1.OwnerReference" + } + ] + }, + "x-kubernetes-list-map-keys": [ + "uid" + ], + "x-kubernetes-list-type": "map", + "x-kubernetes-patch-merge-key": "uid", + "x-kubernetes-patch-strategy": "merge" + }, + "resourceVersion": { + "description": "An opaque value that represents the internal version of this object that can be used by clients to determine when objects have changed. May be used for optimistic concurrency, change detection, and the watch operation on a resource or set of resources. Clients must treat these values as opaque and passed unmodified back to the server. They may only be valid for a particular resource or set of resources.\n\nPopulated by the system. Read-only. Value must be treated as opaque by clients and . More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#concurrency-control-and-consistency", + "type": "string" + }, + "selfLink": { + "description": "Deprecated: selfLink is a legacy read-only field that is no longer populated by the system.", + "type": "string" + }, + "uid": { + "description": "UID is the unique in time and space value for this object. It is typically generated by the server on successful creation of a resource and is not allowed to change on PUT operations.\n\nPopulated by the system. Read-only. More info: https://kubernetes.io/docs/concepts/overview/working-with-objects/names#uids", + "type": "string" + } + } + }, + "io.k8s.apimachinery.pkg.apis.meta.v1.OwnerReference": { + "description": "OwnerReference contains enough information to let you identify an owning object. An owning object must be in the same namespace as the dependent, or be cluster-scoped, so there is no namespace field.", + "type": "object", + "required": [ + "apiVersion", + "kind", + "name", + "uid" + ], + "properties": { + "apiVersion": { + "description": "API version of the referent.", + "type": "string", + "default": "" + }, + "blockOwnerDeletion": { + "description": "If true, AND if the owner has the \"foregroundDeletion\" finalizer, then the owner cannot be deleted from the key-value store until this reference is removed. See https://kubernetes.io/docs/concepts/architecture/garbage-collection/#foreground-deletion for how the garbage collector interacts with this field and enforces the foreground deletion. Defaults to false. To set this field, a user needs \"delete\" permission of the owner, otherwise 422 (Unprocessable Entity) will be returned.", + "type": "boolean" + }, + "controller": { + "description": "If true, this reference points to the managing controller.", + "type": "boolean" + }, + "kind": { + "description": "Kind of the referent. More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#types-kinds", + "type": "string", + "default": "" + }, + "name": { + "description": "Name of the referent. More info: https://kubernetes.io/docs/concepts/overview/working-with-objects/names#names", + "type": "string", + "default": "" + }, + "uid": { + "description": "UID of the referent. More info: https://kubernetes.io/docs/concepts/overview/working-with-objects/names#uids", + "type": "string", + "default": "" + } + }, + "x-kubernetes-map-type": "atomic" + }, + "io.k8s.apimachinery.pkg.apis.meta.v1.Time": { + "description": "Time is a wrapper around time.Time which supports correct marshaling to YAML and JSON. Wrappers are provided for many of the factory methods that the time package offers.", + "type": "string", + "format": "date-time" + } + } + } +} \ No newline at end of file diff --git a/pkg/tests/apis/openapi_test.go b/pkg/tests/apis/openapi_test.go index a221df08303..b12a9e76f18 100644 --- a/pkg/tests/apis/openapi_test.go +++ b/pkg/tests/apis/openapi_test.go @@ -105,6 +105,9 @@ func TestIntegrationOpenAPIs(t *testing.T) { }, { Group: "shorturl.grafana.app", Version: "v1alpha1", + }, { + Group: "testdata.datasource.grafana.app", + Version: "v0alpha1", }} for _, gv := range groups { VerifyOpenAPISnapshots(t, dir, gv, h) From 2bb5e6c2ecc0f68cffcb721976e755d23cb0ee42 Mon Sep 17 00:00:00 2001 From: Jack Baldry Date: Wed, 8 Oct 2025 13:05:58 +0100 Subject: [PATCH 008/112] Add support for the future rename of the docs plugin app to Grafana Pathfinder (#111950) --- .../AppChrome/ExtensionSidebar/ExtensionSidebarProvider.tsx | 3 +++ .../AppChrome/ExtensionSidebar/ExtensionToolbarItemButton.tsx | 3 +++ 2 files changed, 6 insertions(+) diff --git a/public/app/core/components/AppChrome/ExtensionSidebar/ExtensionSidebarProvider.tsx b/public/app/core/components/AppChrome/ExtensionSidebar/ExtensionSidebarProvider.tsx index 8b90bd24914..ca5ff926169 100644 --- a/public/app/core/components/AppChrome/ExtensionSidebar/ExtensionSidebarProvider.tsx +++ b/public/app/core/components/AppChrome/ExtensionSidebar/ExtensionSidebarProvider.tsx @@ -14,7 +14,10 @@ const PERMITTED_EXTENSION_SIDEBAR_PLUGINS = [ 'grafana-investigations-app', 'grafana-assistant-app', 'grafana-dash-app', + // The docs plugin ID is going to transition from grafana-grafanadocsplugin-app to grafana-pathfinder-app. + // Support both until that migration is complete. 'grafana-grafanadocsplugin-app', + 'grafana-pathfinder-app', ]; export type ExtensionSidebarContextType = { diff --git a/public/app/core/components/AppChrome/ExtensionSidebar/ExtensionToolbarItemButton.tsx b/public/app/core/components/AppChrome/ExtensionSidebar/ExtensionToolbarItemButton.tsx index 3dcc53730a7..0093a5dd0ef 100644 --- a/public/app/core/components/AppChrome/ExtensionSidebar/ExtensionToolbarItemButton.tsx +++ b/public/app/core/components/AppChrome/ExtensionSidebar/ExtensionToolbarItemButton.tsx @@ -14,7 +14,10 @@ interface ToolbarItemButtonProps { function getPluginIcon(pluginId?: string): string { switch (pluginId) { + // The docs plugin ID is going to transition from grafana-grafanadocsplugin-app to grafana-pathfinder-app. + // Support both until that migration is complete. case 'grafana-grafanadocsplugin-app': + case 'grafana-pathfinder-app': return 'book'; case 'grafana-investigations-app': return 'eye'; From 53180d5a398f6aa3841e54ea8d84e97ee0aba44d Mon Sep 17 00:00:00 2001 From: Stephanie Hingtgen Date: Wed, 8 Oct 2025 06:13:36 -0600 Subject: [PATCH 009/112] Dashboards: Add validation for manager property on parent folder (#112146) --- pkg/registry/apis/dashboard/register.go | 47 ++++++- pkg/registry/apis/dashboard/register_test.go | 133 +++++++++++++++++++ 2 files changed, 173 insertions(+), 7 deletions(-) diff --git a/pkg/registry/apis/dashboard/register.go b/pkg/registry/apis/dashboard/register.go index a8e5de42e39..309c46b4363 100644 --- a/pkg/registry/apis/dashboard/register.go +++ b/pkg/registry/apis/dashboard/register.go @@ -10,6 +10,7 @@ import ( "github.com/prometheus/client_golang/prometheus" apierrors "k8s.io/apimachinery/pkg/api/errors" metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" + "k8s.io/apimachinery/pkg/apis/meta/v1/unstructured" "k8s.io/apimachinery/pkg/runtime" "k8s.io/apimachinery/pkg/runtime/schema" "k8s.io/apiserver/pkg/admission" @@ -329,9 +330,14 @@ func (b *DashboardsAPIBuilder) validateCreate(ctx context.Context, a admission.A // Validate folder existence if specified if !a.IsDryRun() && accessor.GetFolder() != "" { - if err := b.validateFolderExists(ctx, accessor.GetFolder(), id.GetOrgID()); err != nil { + folder, err := b.validateFolderExists(ctx, accessor.GetFolder(), id.GetOrgID()) + if err != nil { return err } + + if err := b.validateFolderManagedBySameManager(folder, accessor); err != nil { + return apierrors.NewBadRequest(err.Error()) + } } // Validate quota @@ -398,9 +404,14 @@ func (b *DashboardsAPIBuilder) validateUpdate(ctx context.Context, a admission.A return err } - if err := b.validateFolderExists(ctx, newAccessor.GetFolder(), nsInfo.OrgID); err != nil { + folder, err := b.validateFolderExists(ctx, newAccessor.GetFolder(), nsInfo.OrgID) + if err != nil { return apierrors.NewNotFound(folders.FolderResourceInfo.GroupResource(), newAccessor.GetFolder()) } + + if err := b.validateFolderManagedBySameManager(folder, newAccessor); err != nil { + return err + } } // Validate refresh interval @@ -412,21 +423,43 @@ func (b *DashboardsAPIBuilder) validateUpdate(ctx context.Context, a admission.A } // validateFolderExists checks if a folder exists -func (b *DashboardsAPIBuilder) validateFolderExists(ctx context.Context, folderUID string, orgID int64) error { +func (b *DashboardsAPIBuilder) validateFolderExists(ctx context.Context, folderUID string, orgID int64) (*unstructured.Unstructured, error) { ns, err := request.NamespaceInfoFrom(ctx, false) if err != nil { - return err + return nil, err } folderClient := b.folderClientProvider.GetOrCreateHandler(ns.Value) - _, err = folderClient.Get(ctx, folderUID, orgID, metav1.GetOptions{}) + folder, err := folderClient.Get(ctx, folderUID, orgID, metav1.GetOptions{}) // Check if the error is a context deadline exceeded error if err != nil { // historically, we returned a more verbose error with folder name when its not found, below just keeps that behavior if apierrors.IsNotFound(err) { - return apierrors.NewNotFound(folders.FolderResourceInfo.GroupResource(), folderUID) + return nil, apierrors.NewNotFound(folders.FolderResourceInfo.GroupResource(), folderUID) } - return err + return nil, err + } + + return folder, nil +} + +// validation should fail if: +// 1. The parent folder is managed but this dashboard is not +// 2. The parent folder is managed by a different repository than this dashboard +func (b *DashboardsAPIBuilder) validateFolderManagedBySameManager(folder *unstructured.Unstructured, dashboardAccessor utils.GrafanaMetaAccessor) error { + folderAccessor, err := utils.MetaAccessor(folder) + if err != nil { + return fmt.Errorf("error getting meta accessor: %w", err) + } + + if folderManager, ok := folderAccessor.GetManagerProperties(); ok && folderManager.Kind == utils.ManagerKindRepo { + manager, ok := dashboardAccessor.GetManagerProperties() + if !ok { + return fmt.Errorf("folder is managed by a repository, but the dashboard is not managed") + } + if manager.Kind != utils.ManagerKindRepo || manager.Identity != folderManager.Identity { + return fmt.Errorf("folder is managed by a repository, but the dashboard is not managed by the same manager") + } } return nil diff --git a/pkg/registry/apis/dashboard/register_test.go b/pkg/registry/apis/dashboard/register_test.go index f788d5dbe67..b349975ccc4 100644 --- a/pkg/registry/apis/dashboard/register_test.go +++ b/pkg/registry/apis/dashboard/register_test.go @@ -8,6 +8,8 @@ import ( "github.com/stretchr/testify/mock" "github.com/stretchr/testify/require" metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" + "k8s.io/apimachinery/pkg/apis/meta/v1/unstructured" + "k8s.io/apimachinery/pkg/runtime" "k8s.io/apimachinery/pkg/runtime/schema" "k8s.io/apiserver/pkg/admission" @@ -15,7 +17,9 @@ import ( dashv1 "github.com/grafana/grafana/apps/dashboard/pkg/apis/dashboard/v1beta1" dashv2alpha1 "github.com/grafana/grafana/apps/dashboard/pkg/apis/dashboard/v2alpha1" dashv2beta1 "github.com/grafana/grafana/apps/dashboard/pkg/apis/dashboard/v2beta1" + folderv1 "github.com/grafana/grafana/apps/folder/pkg/apis/folder/v1beta1" common "github.com/grafana/grafana/pkg/apimachinery/apis/common/v0alpha1" + "github.com/grafana/grafana/pkg/apimachinery/utils" "github.com/grafana/grafana/pkg/services/dashboards" "github.com/grafana/grafana/pkg/services/featuremgmt" "github.com/grafana/grafana/pkg/services/user" @@ -257,3 +261,132 @@ func (m *mockFeatureToggles) GetEnabled(ctx context.Context) map[string]bool { return res } + +func TestDashboardAPIBuilder_validateFolderManagedBySameManager(t *testing.T) { + tests := []struct { + name string + folderManager *utils.ManagerProperties + dashboardManager *utils.ManagerProperties + expectedError bool + }{ + { + name: "folder not managed by repository", + folderManager: nil, + dashboardManager: nil, + expectedError: false, + }, + { + name: "folder not managed by repository, dashboard managed", + folderManager: nil, + dashboardManager: &utils.ManagerProperties{ + Kind: utils.ManagerKindRepo, + Identity: "repo-1", + }, + expectedError: false, + }, + { + name: "folder managed by plugin", + folderManager: &utils.ManagerProperties{ + Kind: utils.ManagerKindPlugin, + Identity: "plugin-1", + }, + dashboardManager: nil, + expectedError: false, + }, + { + name: "folder and dashboard managed by same repository", + folderManager: &utils.ManagerProperties{ + Kind: utils.ManagerKindRepo, + Identity: "repo-1", + }, + dashboardManager: &utils.ManagerProperties{ + Kind: utils.ManagerKindRepo, + Identity: "repo-1", + }, + expectedError: false, + }, + { + name: "folder managed by repository, dashboard is not managed", + folderManager: &utils.ManagerProperties{ + Kind: utils.ManagerKindRepo, + Identity: "repo-1", + }, + dashboardManager: nil, + expectedError: true, + }, + { + name: "folder and dashboard managed by different repositories", + folderManager: &utils.ManagerProperties{ + Kind: utils.ManagerKindRepo, + Identity: "repo-1", + }, + dashboardManager: &utils.ManagerProperties{ + Kind: utils.ManagerKindRepo, + Identity: "repo-2", + }, + expectedError: true, + }, + { + name: "folder managed by repository, dashboard managed by plugin", + folderManager: &utils.ManagerProperties{ + Kind: utils.ManagerKindRepo, + Identity: "repo-1", + }, + dashboardManager: &utils.ManagerProperties{ + Kind: utils.ManagerKindPlugin, + Identity: "plugin-1", + }, + expectedError: true, + }, + } + + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + folder := &folderv1.Folder{ + TypeMeta: metav1.TypeMeta{ + Kind: "Folder", + APIVersion: "folder.grafana.app/v1beta1", + }, + ObjectMeta: metav1.ObjectMeta{ + Name: "folder-1", + Namespace: "default", + }, + } + if tt.folderManager != nil { + folderAccessor, err := utils.MetaAccessor(folder) + require.NoError(t, err) + folderAccessor.SetManagerProperties(*tt.folderManager) + } + folderUnstructured := &unstructured.Unstructured{} + folderMap, err := runtime.DefaultUnstructuredConverter.ToUnstructured(folder) + require.NoError(t, err) + folderUnstructured.Object = folderMap + + dashboard := &dashv1.Dashboard{ + TypeMeta: metav1.TypeMeta{ + Kind: "Dashboard", + APIVersion: "dashboard.grafana.app/v1beta1", + }, + ObjectMeta: metav1.ObjectMeta{ + Name: "dashboard-1", + Namespace: "default", + }, + } + if tt.dashboardManager != nil { + dashboardAccessor, err := utils.MetaAccessor(dashboard) + require.NoError(t, err) + dashboardAccessor.SetManagerProperties(*tt.dashboardManager) + } + dashboardAccessor, err := utils.MetaAccessor(dashboard) + require.NoError(t, err) + + builder := &DashboardsAPIBuilder{} + err = builder.validateFolderManagedBySameManager(folderUnstructured, dashboardAccessor) + if tt.expectedError { + require.Error(t, err) + } else { + require.NoError(t, err) + } + }) + } +} From bb1d7d9070aeb01716eaf6679b54553781e40f30 Mon Sep 17 00:00:00 2001 From: Alex Khomenko Date: Wed, 8 Oct 2025 14:17:05 +0200 Subject: [PATCH 010/112] Provisioning: Add FE analytics (#112118) * Provisioning: Add FE analytics * Cleanup * More cleanup * Add missing workflow * Add more tracking --- .../provisioning/Config/ConfigForm.tsx | 10 +++++++- .../Repository/DeleteRepositoryButton.tsx | 10 ++++++++ .../Repository/RepositoryActions.tsx | 13 +++++++++- .../Repository/RepositoryCard.tsx | 14 ++++++++++- .../Repository/SyncRepository.tsx | 6 +++++ .../Shared/ConnectRepositoryButton.tsx | 4 ++- .../Wizard/ProvisioningWizard.tsx | 25 +++++++++++++++++-- .../BulkDeleteProvisionedResource.tsx | 13 +++++++++- .../BulkMoveProvisionedResource.tsx | 9 ++++++- .../DeleteProvisionedDashboardForm.tsx | 8 +++++- .../MoveProvisionedDashboardForm.tsx | 8 +++++- .../SaveProvisionedDashboardForm.tsx | 18 +++++++++++-- .../Folders/DeleteProvisionedFolderForm.tsx | 8 +++++- .../Folders/NewProvisionedFolderForm.tsx | 8 +++++- .../app/features/provisioning/utils/data.ts | 2 +- 15 files changed, 141 insertions(+), 15 deletions(-) diff --git a/public/app/features/provisioning/Config/ConfigForm.tsx b/public/app/features/provisioning/Config/ConfigForm.tsx index c0ba526533f..0ba33a4e5db 100644 --- a/public/app/features/provisioning/Config/ConfigForm.tsx +++ b/public/app/features/provisioning/Config/ConfigForm.tsx @@ -4,6 +4,7 @@ import { Controller, useForm } from 'react-hook-form'; import { useNavigate } from 'react-router-dom-v5-compat'; import { t } from '@grafana/i18n'; +import { reportInteraction } from '@grafana/runtime'; import { Button, Checkbox, @@ -118,12 +119,19 @@ export function ConfigForm({ data }: ConfigFormProps) { useEffect(() => { if (request.isSuccess) { const formData = getValues(); + + reportInteraction('grafana_provisioning_repository_updated', { + repositoryName: repositoryName ?? 'unknown', + repositoryType: formData.type, + target: formData.sync?.target ?? 'unknown', + }); + reset(formData); setTimeout(() => { navigate('/admin/provisioning'); }, 300); } - }, [request.isSuccess, reset, getValues, navigate]); + }, [request.isSuccess, reset, getValues, navigate, repositoryName]); const onSubmit = async (form: RepositoryFormData) => { setIsLoading(true); diff --git a/public/app/features/provisioning/Repository/DeleteRepositoryButton.tsx b/public/app/features/provisioning/Repository/DeleteRepositoryButton.tsx index 51186523d51..23ea3943268 100644 --- a/public/app/features/provisioning/Repository/DeleteRepositoryButton.tsx +++ b/public/app/features/provisioning/Repository/DeleteRepositoryButton.tsx @@ -2,6 +2,7 @@ import { useCallback, useEffect, useState } from 'react'; import { useNavigate } from 'react-router-dom-v5-compat'; import { t, Trans } from '@grafana/i18n'; +import { reportInteraction } from '@grafana/runtime'; import { Button, ConfirmModal, Dropdown, Icon, Menu, Stack } from '@grafana/ui'; import { Repository, @@ -44,6 +45,15 @@ export function DeleteRepositoryButton({ name, repository, redirectTo }: Props) }; await replaceRepository({ name, repository: updatedRepository }); } + + reportInteraction('grafana_provisioning_repository_deleted', { + repositoryName: name, + repositoryType: repository?.spec?.type ?? 'unknown', + deleteAction: selectedAction, + target: repository?.spec?.sync?.target ?? 'unknown', + workflows: repository?.spec?.workflows ?? [], + }); + deleteRepository({ name }); }, [deleteRepository, replaceRepository, name, selectedAction, repository]); diff --git a/public/app/features/provisioning/Repository/RepositoryActions.tsx b/public/app/features/provisioning/Repository/RepositoryActions.tsx index 70fa314bee1..edadb2b538e 100644 --- a/public/app/features/provisioning/Repository/RepositoryActions.tsx +++ b/public/app/features/provisioning/Repository/RepositoryActions.tsx @@ -1,4 +1,5 @@ import { t, Trans } from '@grafana/i18n'; +import { reportInteraction } from '@grafana/runtime'; import { Badge, Button, LinkButton, Stack } from '@grafana/ui'; import { Repository } from 'app/api/clients/provisioning/v0alpha1'; @@ -33,7 +34,17 @@ export function RepositoryActions({ repository }: RepositoryActionsProps) { )} - + { + reportInteraction('grafana_provisioning_repository_settings_opened', { + repositoryName: name, + repositoryType: repository.spec?.type ?? 'unknown', + }); + }} + > Settings diff --git a/public/app/features/provisioning/Repository/RepositoryCard.tsx b/public/app/features/provisioning/Repository/RepositoryCard.tsx index 545777b9f4f..8a8205e9a2f 100644 --- a/public/app/features/provisioning/Repository/RepositoryCard.tsx +++ b/public/app/features/provisioning/Repository/RepositoryCard.tsx @@ -1,6 +1,7 @@ import { ReactNode } from 'react'; import { t, Trans } from '@grafana/i18n'; +import { reportInteraction } from '@grafana/runtime'; import { Stack, Text, TextLink, Icon, Card, LinkButton, Badge } from '@grafana/ui'; import { Repository, ResourceCount } from 'app/api/clients/provisioning/v0alpha1'; @@ -103,7 +104,18 @@ export function RepositoryCard({ repository }: Props) { View - + { + reportInteraction('grafana_provisioning_repository_settings_opened', { + repositoryName: name, + repositoryType: spec?.type ?? 'unknown', + }); + }} + > Settings diff --git a/public/app/features/provisioning/Repository/SyncRepository.tsx b/public/app/features/provisioning/Repository/SyncRepository.tsx index bbf4ff01fb4..36a62d44d1f 100644 --- a/public/app/features/provisioning/Repository/SyncRepository.tsx +++ b/public/app/features/provisioning/Repository/SyncRepository.tsx @@ -2,6 +2,7 @@ import { useState } from 'react'; import { useNavigate } from 'react-router-dom-v5-compat'; import { Trans, t } from '@grafana/i18n'; +import { reportInteraction } from '@grafana/runtime'; import { Button, ConfirmModal } from '@grafana/ui'; import { Repository, useCreateRepositoryJobsMutation } from 'app/api/clients/provisioning/v0alpha1'; @@ -23,6 +24,11 @@ export function SyncRepository({ repository }: Props) { if (!name) { return; } + reportInteraction('grafana_provisioning_repository_pull_triggered', { + repositoryName: name, + repositoryType: repository.spec?.type ?? 'unknown', + target: repository.spec?.sync.target ?? 'unknown', + }); createJob({ name, jobSpec: { diff --git a/public/app/features/provisioning/Shared/ConnectRepositoryButton.tsx b/public/app/features/provisioning/Shared/ConnectRepositoryButton.tsx index e61b9f31e21..cdf80faec33 100644 --- a/public/app/features/provisioning/Shared/ConnectRepositoryButton.tsx +++ b/public/app/features/provisioning/Shared/ConnectRepositoryButton.tsx @@ -33,7 +33,9 @@ export function ConnectRepositoryButton({ items }: Props) { key={config.type} icon={config.icon} label={config.label} - onClick={() => navigate(`${CONNECT_URL}/${config.type}`)} + onClick={() => { + navigate(`${CONNECT_URL}/${config.type}`); + }} /> ); })} diff --git a/public/app/features/provisioning/Wizard/ProvisioningWizard.tsx b/public/app/features/provisioning/Wizard/ProvisioningWizard.tsx index 5eb4fd174ee..01b8c5436c3 100644 --- a/public/app/features/provisioning/Wizard/ProvisioningWizard.tsx +++ b/public/app/features/provisioning/Wizard/ProvisioningWizard.tsx @@ -5,7 +5,7 @@ import { useNavigate } from 'react-router-dom-v5-compat'; import { AppEvents, GrafanaTheme2 } from '@grafana/data'; import { t } from '@grafana/i18n'; -import { getAppEvents, isFetchError } from '@grafana/runtime'; +import { getAppEvents, isFetchError, reportInteraction } from '@grafana/runtime'; import { Box, Button, ConfirmModal, Stack, Text, useStyles2 } from '@grafana/ui'; import { RepositoryViewList, useDeleteRepositoryMutation } from 'app/api/clients/provisioning/v0alpha1'; import { FormPrompt } from 'app/core/components/FormPrompt/FormPrompt'; @@ -14,7 +14,7 @@ import { getDefaultValues } from '../Config/defaults'; import { ProvisioningAlert } from '../Shared/ProvisioningAlert'; import { PROVISIONING_URL } from '../constants'; import { useCreateOrUpdateRepository } from '../hooks/useCreateOrUpdateRepository'; -import { dataToSpec } from '../utils/data'; +import { dataToSpec, getWorkflows } from '../utils/data'; import { getFormErrors } from '../utils/getFormErrors'; import { BootstrapStep } from './BootstrapStep'; @@ -176,6 +176,11 @@ export const ProvisioningWizard = memo(function ProvisioningWizard({ if (previousStepIndex >= 0) { const previousStep = steps[previousStepIndex]; + reportInteraction('grafana_provisioning_wizard_previous_clicked', { + fromStep: activeStep, + toStep: previousStep.id, + repositoryType: repoType, + }); setActiveStep(previousStep.id); // Remove current step from completed steps when going back setCompletedSteps((prev) => prev.filter((step) => step !== activeStep)); @@ -209,6 +214,10 @@ export const ProvisioningWizard = memo(function ProvisioningWizard({ const handleConfirmCancel = () => { setShowCancelConfirmation(false); + reportInteraction('grafana_provisioning_wizard_cancelled', { + cancelledAtStep: activeStep, + repositoryType: repoType, + }); handleRepositoryDeletion(repoName); }; @@ -254,6 +263,12 @@ export const ProvisioningWizard = memo(function ProvisioningWizard({ // Only navigate to provisioning URL if we're on the actual last step if (isLastStep) { + const formData = getValues(); + reportInteraction('grafana_provisioning_repository_created', { + repositoryType: repoType, + target: syncTarget, + workflowsEnabled: getWorkflows(formData.repository), + }); navigate(PROVISIONING_URL); } else { let nextStepIndex = currentStepIndex + 1; @@ -274,6 +289,12 @@ export const ProvisioningWizard = memo(function ProvisioningWizard({ return; } + reportInteraction('grafana_provisioning_wizard_step_completed', { + step: activeStep, + repositoryType: repoType, + target: syncTarget, + }); + setActiveStep(steps[nextStepIndex].id); setCompletedSteps((prev) => [...new Set([...prev, activeStep])]); setStepStatusInfo({ status: 'idle' }); diff --git a/public/app/features/provisioning/components/BulkActions/BulkDeleteProvisionedResource.tsx b/public/app/features/provisioning/components/BulkActions/BulkDeleteProvisionedResource.tsx index 5c0c6f5de32..ff3743fc45b 100644 --- a/public/app/features/provisioning/components/BulkActions/BulkDeleteProvisionedResource.tsx +++ b/public/app/features/provisioning/components/BulkActions/BulkDeleteProvisionedResource.tsx @@ -3,7 +3,7 @@ import { FormProvider, useForm } from 'react-hook-form'; import { AppEvents } from '@grafana/data'; import { Trans, t } from '@grafana/i18n'; -import { getAppEvents } from '@grafana/runtime'; +import { getAppEvents, reportInteraction } from '@grafana/runtime'; import { Box, Button, Stack } from '@grafana/ui'; import { Job, RepositoryView } from 'app/api/clients/provisioning/v0alpha1'; import { DescendantCount } from 'app/features/browse-dashboards/components/BrowseActions/DescendantCount'; @@ -47,6 +47,17 @@ function FormContent({ initialValues, selectedItems, repository, workflowOptions const resources = collectSelectedItems(selectedItems); + const folderCount = Object.keys(selectedItems.folder || {}).length; + const dashboardCount = Object.keys(selectedItems.dashboard || {}).length; + reportInteraction('grafana_provisioning_bulk_delete_submitted', { + workflow: data.workflow, + repositoryName: repository.name ?? 'unknown', + repositoryType: repository.type ?? 'unknown', + resourceCount: resources.length, + folderCount, + dashboardCount, + }); + // Create the delete job spec const jobSpec: DeleteJobSpec = { action: 'delete', diff --git a/public/app/features/provisioning/components/BulkActions/BulkMoveProvisionedResource.tsx b/public/app/features/provisioning/components/BulkActions/BulkMoveProvisionedResource.tsx index 6e553ed760c..7f07a6184e7 100644 --- a/public/app/features/provisioning/components/BulkActions/BulkMoveProvisionedResource.tsx +++ b/public/app/features/provisioning/components/BulkActions/BulkMoveProvisionedResource.tsx @@ -4,7 +4,7 @@ import { FormProvider, useForm } from 'react-hook-form'; import { AppEvents } from '@grafana/data'; import { Trans, t } from '@grafana/i18n'; -import { getAppEvents } from '@grafana/runtime'; +import { getAppEvents, reportInteraction } from '@grafana/runtime'; import { Box, Button, Field, Stack } from '@grafana/ui'; import { useGetFolderQuery } from 'app/api/clients/folder/v1beta1'; import { RepositoryView, Job } from 'app/api/clients/provisioning/v0alpha1'; @@ -87,6 +87,13 @@ function FormContent({ initialValues, selectedItems, repository, workflowOptions return; } + reportInteraction('grafana_provisioning_bulk_move_submitted', { + workflow: data.workflow, + repositoryName: repository.name ?? 'unknown', + repositoryType: repository.type ?? 'unknown', + resourceCount: resources.length, + }); + // Create the move job spec const jobSpec: MoveJobSpec = { action: 'move', diff --git a/public/app/features/provisioning/components/Dashboards/DeleteProvisionedDashboardForm.tsx b/public/app/features/provisioning/components/Dashboards/DeleteProvisionedDashboardForm.tsx index 0d41a378d61..743975947a0 100644 --- a/public/app/features/provisioning/components/Dashboards/DeleteProvisionedDashboardForm.tsx +++ b/public/app/features/provisioning/components/Dashboards/DeleteProvisionedDashboardForm.tsx @@ -4,7 +4,7 @@ import { useNavigate } from 'react-router-dom-v5-compat'; import { AppEvents } from '@grafana/data'; import { Trans, t } from '@grafana/i18n'; -import { getAppEvents } from '@grafana/runtime'; +import { getAppEvents, reportInteraction } from '@grafana/runtime'; import { Button, Drawer, Stack } from '@grafana/ui'; import { Job, RepositoryView, useDeleteRepositoryFilesWithPathMutation } from 'app/api/clients/provisioning/v0alpha1'; import { DashboardScene } from 'app/features/dashboard-scene/scene/DashboardScene'; @@ -80,6 +80,12 @@ export function DeleteProvisionedDashboardForm({ return; } + reportInteraction('grafana_provisioning_dashboard_delete_submitted', { + workflow, + repositoryName: repo, + repositoryType: repository?.type ?? 'unknown', + }); + // Branch workflow: use /files API for direct file operations if (workflow === 'branch') { const branchRef = ref; diff --git a/public/app/features/provisioning/components/Dashboards/MoveProvisionedDashboardForm.tsx b/public/app/features/provisioning/components/Dashboards/MoveProvisionedDashboardForm.tsx index 3968484ceb2..637d0861882 100644 --- a/public/app/features/provisioning/components/Dashboards/MoveProvisionedDashboardForm.tsx +++ b/public/app/features/provisioning/components/Dashboards/MoveProvisionedDashboardForm.tsx @@ -5,7 +5,7 @@ import { useNavigate } from 'react-router-dom-v5-compat'; import { AppEvents } from '@grafana/data'; import { Trans, t } from '@grafana/i18n'; -import { getAppEvents } from '@grafana/runtime'; +import { getAppEvents, reportInteraction } from '@grafana/runtime'; import { Alert, Button, Drawer, Field, Input, Spinner, Stack } from '@grafana/ui'; import { useGetFolderQuery } from 'app/api/clients/folder/v1beta1'; import { @@ -123,6 +123,12 @@ export function MoveProvisionedDashboardForm({ return; } + reportInteraction('grafana_provisioning_dashboard_move_submitted', { + workflow, + repositoryName: repo, + repositoryType: repository?.type ?? 'unknown', + }); + // Branch workflow: use /files API for direct file operations if (workflow === 'branch') { if (!currentFileData?.resource?.file) { diff --git a/public/app/features/provisioning/components/Dashboards/SaveProvisionedDashboardForm.tsx b/public/app/features/provisioning/components/Dashboards/SaveProvisionedDashboardForm.tsx index 368076cafae..f278c2e436c 100644 --- a/public/app/features/provisioning/components/Dashboards/SaveProvisionedDashboardForm.tsx +++ b/public/app/features/provisioning/components/Dashboards/SaveProvisionedDashboardForm.tsx @@ -4,7 +4,7 @@ import { useNavigate } from 'react-router-dom-v5-compat'; import { AppEvents, locationUtil } from '@grafana/data'; import { Trans, t } from '@grafana/i18n'; -import { getAppEvents, locationService } from '@grafana/runtime'; +import { getAppEvents, locationService, reportInteraction } from '@grafana/runtime'; import { Dashboard } from '@grafana/schema'; import { Button, Field, Input, Stack, TextArea } from '@grafana/ui'; import { RepositoryView, Unstructured } from 'app/api/clients/provisioning/v0alpha1'; @@ -133,7 +133,15 @@ export function SaveProvisionedDashboardForm({ }); // Submit handler for saving the form data - const handleFormSubmit = async ({ title, description, repo, path, comment, ref }: ProvisionedDashboardFormData) => { + const handleFormSubmit = async ({ + title, + description, + repo, + path, + comment, + ref, + folder, + }: ProvisionedDashboardFormData) => { // Validate required fields if (!repo || !path) { console.error('Missing required fields for saving:', { repo, path }); @@ -155,6 +163,12 @@ export function SaveProvisionedDashboardForm({ copyTags: true, }); + reportInteraction('grafana_provisioning_dashboard_save_submitted', { + workflow, + repositoryName: repo, + repositoryType: repository?.type ?? 'unknown', + }); + createOrUpdateFile({ // Skip adding ref to the default branch request ref: ref === repository?.branch ? undefined : ref, diff --git a/public/app/features/provisioning/components/Folders/DeleteProvisionedFolderForm.tsx b/public/app/features/provisioning/components/Folders/DeleteProvisionedFolderForm.tsx index 2c76f5c877f..102cacbf7d4 100644 --- a/public/app/features/provisioning/components/Folders/DeleteProvisionedFolderForm.tsx +++ b/public/app/features/provisioning/components/Folders/DeleteProvisionedFolderForm.tsx @@ -4,7 +4,7 @@ import { useNavigate } from 'react-router-dom-v5-compat'; import { AppEvents } from '@grafana/data'; import { Trans, t } from '@grafana/i18n'; -import { getAppEvents } from '@grafana/runtime'; +import { getAppEvents, reportInteraction } from '@grafana/runtime'; import { Box, Button, Stack } from '@grafana/ui'; import { Folder } from 'app/api/clients/folder/v1beta1'; import { Job, RepositoryView, useDeleteRepositoryFilesWithPathMutation } from 'app/api/clients/provisioning/v0alpha1'; @@ -61,6 +61,12 @@ function FormContent({ initialValues, parentFolder, repository, workflowOptions, return; } + reportInteraction('grafana_provisioning_folder_delete_submitted', { + workflow, + repositoryName: repo, + repositoryType: repository?.type ?? 'unknown', + }); + // Branch workflow: use /files API for direct file operations if (workflow === 'branch') { const branchRef = ref; diff --git a/public/app/features/provisioning/components/Folders/NewProvisionedFolderForm.tsx b/public/app/features/provisioning/components/Folders/NewProvisionedFolderForm.tsx index 10bcfcdcf66..3b9d62916a4 100644 --- a/public/app/features/provisioning/components/Folders/NewProvisionedFolderForm.tsx +++ b/public/app/features/provisioning/components/Folders/NewProvisionedFolderForm.tsx @@ -3,7 +3,7 @@ import { useNavigate } from 'react-router-dom-v5-compat'; import { AppEvents } from '@grafana/data'; import { Trans, t } from '@grafana/i18n'; -import { getAppEvents } from '@grafana/runtime'; +import { getAppEvents, reportInteraction } from '@grafana/runtime'; import { Alert, Button, Field, Input, Stack } from '@grafana/ui'; import { Folder } from 'app/api/clients/folder/v1beta1'; import { RepositoryView, useCreateRepositoryFilesWithPathMutation } from 'app/api/clients/provisioning/v0alpha1'; @@ -115,6 +115,12 @@ function FormContent({ initialValues, repository, workflowOptions, folder, onDis ref = undefined; } + reportInteraction('grafana_provisioning_folder_create_submitted', { + workflow, + repositoryName: repoName, + repositoryType: repository?.type ?? 'unknown', + }); + create({ ref, name: repoName, diff --git a/public/app/features/provisioning/utils/data.ts b/public/app/features/provisioning/utils/data.ts index 452a86d7e46..930e943d83b 100644 --- a/public/app/features/provisioning/utils/data.ts +++ b/public/app/features/provisioning/utils/data.ts @@ -2,7 +2,7 @@ import { RepositorySpec } from 'app/api/clients/provisioning/v0alpha1'; import { RepositoryFormData } from '../types'; -const getWorkflows = (data: RepositoryFormData): RepositorySpec['workflows'] => { +export const getWorkflows = (data: RepositoryFormData): RepositorySpec['workflows'] => { if (data.readOnly) { return []; } From acbbfde256304c516f0fcd55f36cdf710f0c22a7 Mon Sep 17 00:00:00 2001 From: Ieva Date: Wed, 8 Oct 2025 13:37:12 +0100 Subject: [PATCH 011/112] AuthZ service: Expand the logic to also evaluate action sets (#112124) * expand AuthZ service logic to also evaluate action sets * handle folder creation * fix test * simplify mapper code Co-authored-by: gamab * more accurate variable name Co-authored-by: gamab * break alerting import cycle * Apply suggestion from @gamab --------- Co-authored-by: gamab Co-authored-by: Gabriel MABILLE --- .../ossaccesscontrol/receivers.go | 29 +++--- pkg/services/authz/rbac/mapper.go | 69 +++++++++++++- pkg/services/authz/rbac/models.go | 2 + pkg/services/authz/rbac/service.go | 28 +++--- pkg/services/authz/rbac/service_test.go | 91 ++++++++++++++++++- .../snapshot_mgmt_alerts_test.go | 3 +- pkg/services/ngalert/accesscontrol.go | 58 ++++++------ .../ngalert/accesscontrol/receivers.go | 66 +++----------- .../ngalert/accesscontrol/receivers_test.go | 84 ++++++++--------- .../ngalert/accesscontrol/rules_test.go | 2 +- pkg/services/ngalert/models/accesscontrol.go | 44 +++++++++ .../ngalert/notifier/receiver_svc_test.go | 74 +++++++-------- .../notifications/receivers/receiver_test.go | 23 +++-- 13 files changed, 359 insertions(+), 214 deletions(-) create mode 100644 pkg/services/ngalert/models/accesscontrol.go diff --git a/pkg/services/accesscontrol/ossaccesscontrol/receivers.go b/pkg/services/accesscontrol/ossaccesscontrol/receivers.go index 3ef4638b3dc..49ca4a0b3e8 100644 --- a/pkg/services/accesscontrol/ossaccesscontrol/receivers.go +++ b/pkg/services/accesscontrol/ossaccesscontrol/receivers.go @@ -13,8 +13,7 @@ import ( "github.com/grafana/grafana/pkg/services/accesscontrol/resourcepermissions" "github.com/grafana/grafana/pkg/services/featuremgmt" "github.com/grafana/grafana/pkg/services/licensing" - "github.com/grafana/grafana/pkg/services/ngalert" - alertingac "github.com/grafana/grafana/pkg/services/ngalert/accesscontrol" + "github.com/grafana/grafana/pkg/services/ngalert/models" "github.com/grafana/grafana/pkg/services/org" "github.com/grafana/grafana/pkg/services/serviceaccounts" "github.com/grafana/grafana/pkg/services/team" @@ -29,8 +28,8 @@ var ReceiversAdminActions = append(ReceiversEditActions, []string{accesscontrol. // defaultPermissions returns the default permissions for a newly created receiver. func defaultPermissions() []accesscontrol.SetResourcePermissionCommand { return []accesscontrol.SetResourcePermissionCommand{ - {BuiltinRole: string(org.RoleEditor), Permission: string(alertingac.ReceiverPermissionEdit)}, - {BuiltinRole: string(org.RoleViewer), Permission: string(alertingac.ReceiverPermissionView)}, + {BuiltinRole: string(org.RoleEditor), Permission: string(models.PermissionEdit)}, + {BuiltinRole: string(org.RoleViewer), Permission: string(models.PermissionView)}, } } @@ -43,7 +42,7 @@ func ProvideReceiverPermissionsService( Resource: "receivers", ResourceAttribute: "uid", ResourceTranslator: func(ctx context.Context, orgID int64, resourceID string) (string, error) { - return alertingac.ScopeReceiversProvider.GetResourceIDFromUID(resourceID), nil + return models.ScopeReceiversProvider.GetResourceIDFromUID(resourceID), nil }, Assignments: resourcepermissions.Assignments{ Users: true, @@ -52,13 +51,13 @@ func ProvideReceiverPermissionsService( ServiceAccounts: true, }, PermissionsToActions: map[string][]string{ - string(alertingac.ReceiverPermissionView): append([]string{}, ReceiversViewActions...), - string(alertingac.ReceiverPermissionEdit): append([]string{}, ReceiversEditActions...), - string(alertingac.ReceiverPermissionAdmin): append([]string{}, ReceiversAdminActions...), + string(models.PermissionView): append([]string{}, ReceiversViewActions...), + string(models.PermissionEdit): append([]string{}, ReceiversEditActions...), + string(models.PermissionAdmin): append([]string{}, ReceiversAdminActions...), }, ReaderRoleName: "Alerting receiver permission reader", WriterRoleName: "Alerting receiver permission writer", - RoleGroup: ngalert.AlertRolesGroup, + RoleGroup: models.AlertRolesGroup, } srv, err := resourcepermissions.New(cfg, options, features, router, license, ac, service, sql, teamService, userService, actionSetService) @@ -79,7 +78,7 @@ type ReceiverPermissionsService struct { // SetDefaultPermissions sets the default permissions for a newly created receiver. func (r ReceiverPermissionsService) SetDefaultPermissions(ctx context.Context, orgID int64, user identity.Requester, uid string) { r.log.Debug("Setting default permissions for receiver", "receiver_uid", uid) - resourceId := alertingac.ScopeReceiversProvider.GetResourceIDFromUID(uid) + resourceId := models.ScopeReceiversProvider.GetResourceIDFromUID(uid) permissions := defaultPermissions() clearCache := false if user != nil && user.IsIdentityType(claims.TypeUser, claims.TypeServiceAccount) { @@ -88,7 +87,7 @@ func (r ReceiverPermissionsService) SetDefaultPermissions(ctx context.Context, o r.log.Error("Could not make user admin", "receiver_uid", uid, "resource_id", resourceId, "id", user.GetID(), "error", err) } else { permissions = append(permissions, accesscontrol.SetResourcePermissionCommand{ - UserID: userID, Permission: string(alertingac.ReceiverPermissionAdmin), + UserID: userID, Permission: string(models.PermissionAdmin), }) clearCache = true } @@ -109,7 +108,7 @@ func (r ReceiverPermissionsService) SetDefaultPermissions(ctx context.Context, o // permissions to read and write permissions for the receiver, as well as read permissions for users, service accounts, and teams. func copyPermissionUser(orgID int64) identity.Requester { return accesscontrol.BackgroundUser("receiver_access_service", orgID, org.RoleAdmin, accesscontrol.ConcatPermissions( - accesscontrol.PermissionsForActions(ReceiversAdminActions, alertingac.ScopeReceiversAll), + accesscontrol.PermissionsForActions(ReceiversAdminActions, models.ScopeReceiversAll), []accesscontrol.Permission{ // Permissions needed for GetPermissions to return user, service account, and team permissions. {Action: accesscontrol.ActionOrgUsersRead, Scope: accesscontrol.ScopeUsersAll}, {Action: serviceaccounts.ActionRead, Scope: serviceaccounts.ScopeAll}, @@ -124,8 +123,8 @@ func copyPermissionUser(orgID int64) identity.Requester { // name. func (r ReceiverPermissionsService) CopyPermissions(ctx context.Context, orgID int64, user identity.Requester, oldUID, newUID string) (int, error) { r.log.Debug("Copying permissions from receiver", "old_uid", oldUID, "new_uid", newUID) - oldResourceId := alertingac.ScopeReceiversProvider.GetResourceIDFromUID(oldUID) - newResourceId := alertingac.ScopeReceiversProvider.GetResourceIDFromUID(newUID) + oldResourceId := models.ScopeReceiversProvider.GetResourceIDFromUID(oldUID) + newResourceId := models.ScopeReceiversProvider.GetResourceIDFromUID(newUID) currentPermissions, err := r.GetPermissions(ctx, copyPermissionUser(orgID), oldResourceId) if err != nil { return 0, err @@ -153,7 +152,7 @@ func (r ReceiverPermissionsService) CopyPermissions(ctx context.Context, orgID i } func (r ReceiverPermissionsService) DeleteResourcePermissions(ctx context.Context, orgID int64, uid string) error { - return r.Service.DeleteResourcePermissions(ctx, orgID, alertingac.ScopeReceiversProvider.GetResourceIDFromUID(uid)) + return r.Service.DeleteResourcePermissions(ctx, orgID, models.ScopeReceiversProvider.GetResourceIDFromUID(uid)) } // toSetResourcePermissionCommands converts a list of resource permissions to a list of set resource permission commands. diff --git a/pkg/services/authz/rbac/mapper.go b/pkg/services/authz/rbac/mapper.go index e688ef16e08..cf51380b22d 100644 --- a/pkg/services/authz/rbac/mapper.go +++ b/pkg/services/authz/rbac/mapper.go @@ -2,8 +2,10 @@ package rbac import ( "fmt" + "slices" "github.com/grafana/grafana/pkg/apimachinery/utils" + "github.com/grafana/grafana/pkg/services/accesscontrol/ossaccesscontrol" ) // Mapping maps a verb to a RBAC action and a resource name to a RBAC scope. @@ -11,6 +13,9 @@ type Mapping interface { // action returns the action for the given verb. // If no action is found, it returns false. Action(verb string) (string, bool) + // ActionSets returns the action sets for the given verb. + // If no action sets are found, it returns an empty slice. This is expected for resources that do not have action sets (anything apart from dashboards and folders). + ActionSets(verb string) []string // scope returns the scope for the given resource name. Scope(name string) string // prefix returns the scope prefix for the translation. @@ -27,6 +32,7 @@ type translation struct { resource string attribute string verbMapping map[string]string + actionSetMapping map[string][]string folderSupport bool skipScopeOnCreate bool } @@ -36,6 +42,11 @@ func (t translation) Action(verb string) (string, bool) { return action, ok } +func (t translation) ActionSets(verb string) []string { + actionSets := t.actionSetMapping[verb] + return actionSets +} + func (t translation) Scope(name string) string { return t.resource + ":" + t.attribute + ":" + name } @@ -101,13 +112,67 @@ func newResourceTranslation(resource string, attribute string, folderSupport, sk } } +// newDashboardTranslation creates a translation for dashboards and also maps the actions to action sets +func newDashboardTranslation() translation { + dashTranslation := newResourceTranslation("dashboards", "uid", true, false) + + actionSetMapping := make(map[string][]string) + for verb, rbacAction := range dashTranslation.verbMapping { + var dashActionSets []string + if slices.Contains(ossaccesscontrol.DashboardViewActions, rbacAction) { + dashActionSets = append(dashActionSets, "dashboards:view") + dashActionSets = append(dashActionSets, "folders:view") + } + if slices.Contains(ossaccesscontrol.DashboardEditActions, rbacAction) { + dashActionSets = append(dashActionSets, "dashboards:edit") + dashActionSets = append(dashActionSets, "folders:edit") + } + if slices.Contains(ossaccesscontrol.DashboardAdminActions, rbacAction) { + dashActionSets = append(dashActionSets, "dashboards:admin") + dashActionSets = append(dashActionSets, "folders:admin") + } + actionSetMapping[verb] = dashActionSets + } + + dashTranslation.actionSetMapping = actionSetMapping + return dashTranslation +} + +// newFolderTranslation creates a translation for folders and also maps the actions to action sets +func newFolderTranslation() translation { + folderTranslation := newResourceTranslation("folders", "uid", true, false) + + actionSetMapping := make(map[string][]string) + for verb, rbacAction := range folderTranslation.verbMapping { + var actionSets []string + // Folder creation has not been added to the FolderEditActions and FolderAdminActions slices (https://github.com/grafana/identity-access-team/issues/794) + // so we handle it as a special case for now + if rbacAction == "folders:create" { + actionSets = append(actionSets, "folders:edit") + actionSets = append(actionSets, "folders:admin") + } + if slices.Contains(ossaccesscontrol.FolderViewActions, rbacAction) { + actionSets = append(actionSets, "folders:view") + } + if slices.Contains(ossaccesscontrol.FolderEditActions, rbacAction) { + actionSets = append(actionSets, "folders:edit") + } + if slices.Contains(ossaccesscontrol.FolderAdminActions, rbacAction) { + actionSets = append(actionSets, "folders:admin") + } + actionSetMapping[verb] = actionSets + } + folderTranslation.actionSetMapping = actionSetMapping + return folderTranslation +} + func NewMapperRegistry() MapperRegistry { mapper := mapper(map[string]map[string]translation{ "dashboard.grafana.app": { - "dashboards": newResourceTranslation("dashboards", "uid", true, false), + "dashboards": newDashboardTranslation(), }, "folder.grafana.app": { - "folders": newResourceTranslation("folders", "uid", true, false), + "folders": newFolderTranslation(), }, "iam.grafana.app": { // Users is a special case. We translate user permissions from id to uid based. diff --git a/pkg/services/authz/rbac/models.go b/pkg/services/authz/rbac/models.go index 62495a5139e..38e2beab59e 100644 --- a/pkg/services/authz/rbac/models.go +++ b/pkg/services/authz/rbac/models.go @@ -7,6 +7,7 @@ type checkRequest struct { IdentityType claims.IdentityType UserUID string Action string // Verb has been mapped into an action + ActionSets []string Group string Resource string Verb string @@ -22,6 +23,7 @@ type listRequest struct { Resource string Verb string Action string + ActionSets []string Options *ListRequestOptions } diff --git a/pkg/services/authz/rbac/service.go b/pkg/services/authz/rbac/service.go index 9af70a26277..45d2c97c11d 100644 --- a/pkg/services/authz/rbac/service.go +++ b/pkg/services/authz/rbac/service.go @@ -160,7 +160,7 @@ func (s *Service) Check(ctx context.Context, req *authzv1.CheckRequest) (*authzv } s.metrics.permissionCacheUsage.WithLabelValues("false", checkReq.Action).Inc() - permissions, err := s.getIdentityPermissions(ctx, checkReq.Namespace, checkReq.IdentityType, checkReq.UserUID, checkReq.Action) + permissions, err := s.getIdentityPermissions(ctx, checkReq.Namespace, checkReq.IdentityType, checkReq.UserUID, checkReq.Action, checkReq.ActionSets) if err != nil { ctxLogger.Error("could not get user permissions", "subject", req.GetSubject(), "error", err) s.metrics.requestCount.WithLabelValues("true", "true", req.GetVerb(), req.GetGroup(), req.GetResource()).Inc() @@ -223,7 +223,7 @@ func (s *Service) List(ctx context.Context, req *authzv1.ListRequest) (*authzv1. if err != nil || listReq.Options.SkipCache { s.metrics.permissionCacheUsage.WithLabelValues("false", listReq.Action).Inc() - permissions, err = s.getIdentityPermissions(ctx, listReq.Namespace, listReq.IdentityType, listReq.UserUID, listReq.Action) + permissions, err = s.getIdentityPermissions(ctx, listReq.Namespace, listReq.IdentityType, listReq.UserUID, listReq.Action, listReq.ActionSets) if err != nil { ctxLogger.Error("could not get user permissions", "subject", req.GetSubject(), "error", err) s.metrics.requestCount.WithLabelValues("true", "true", req.GetVerb(), req.GetGroup(), req.GetResource()).Inc() @@ -260,7 +260,7 @@ func (s *Service) validateCheckRequest(ctx context.Context, req *authzv1.CheckRe return nil, err } - action, err := s.validateAction(ctx, req.GetGroup(), req.GetResource(), req.GetVerb()) + action, actionSets, err := s.validateAction(ctx, req.GetGroup(), req.GetResource(), req.GetVerb()) if err != nil { return nil, err } @@ -270,6 +270,7 @@ func (s *Service) validateCheckRequest(ctx context.Context, req *authzv1.CheckRe UserUID: userUID, IdentityType: idType, Action: action, + ActionSets: actionSets, Group: req.GetGroup(), Resource: req.GetResource(), Verb: req.GetVerb(), @@ -293,7 +294,7 @@ func (s *Service) validateListRequest(ctx context.Context, req *authzv1.ListRequ return nil, err } - action, err := s.validateAction(ctx, req.GetGroup(), req.GetResource(), req.GetVerb()) + action, actionSets, err := s.validateAction(ctx, req.GetGroup(), req.GetResource(), req.GetVerb()) if err != nil { return nil, err } @@ -311,6 +312,7 @@ func (s *Service) validateListRequest(ctx context.Context, req *authzv1.ListRequ UserUID: userUID, IdentityType: idType, Action: action, + ActionSets: actionSets, Group: req.GetGroup(), Resource: req.GetResource(), Verb: req.GetVerb(), @@ -359,34 +361,30 @@ func (s *Service) validateSubject(ctx context.Context, subject string) (string, } // Find the action for a selected verb -func (s *Service) validateAction(ctx context.Context, group, resource, verb string) (string, error) { +func (s *Service) validateAction(ctx context.Context, group, resource, verb string) (string, []string, error) { ctxLogger := s.logger.FromContext(ctx) t, ok := s.mapper.Get(group, resource) if !ok { ctxLogger.Error("unsupported resource", "group", group, "resource", resource) - return "", status.Error(codes.NotFound, "unsupported resource") + return "", nil, status.Error(codes.NotFound, "unsupported resource") } action, ok := t.Action(verb) if !ok { ctxLogger.Error("unsupported verb", "group", group, "resource", resource, "verb", verb) - return "", status.Error(codes.NotFound, "unsupported verb") + return "", nil, status.Error(codes.NotFound, "unsupported verb") } - return action, nil + actionSets := t.ActionSets(verb) + + return action, actionSets, nil } -func (s *Service) getIdentityPermissions(ctx context.Context, ns types.NamespaceInfo, idType types.IdentityType, userID, action string) (map[string]bool, error) { +func (s *Service) getIdentityPermissions(ctx context.Context, ns types.NamespaceInfo, idType types.IdentityType, userID, action string, actionSets []string) (map[string]bool, error) { ctx, span := s.tracer.Start(ctx, "authz_direct_db.service.getIdentityPermissions") defer span.End() - // When checking folder creation permissions, also check edit and admin action sets for folder, as the scoped folder create actions aren't stored in the DB separately - var actionSets []string - if action == "folders:create" { - actionSets = append(actionSets, "folders:edit", "folders:admin") - } - switch idType { case types.TypeAnonymous: return s.getAnonymousPermissions(ctx, ns, action, actionSets) diff --git a/pkg/services/authz/rbac/service_test.go b/pkg/services/authz/rbac/service_test.go index 884ade0613a..c3a9f29a3c1 100644 --- a/pkg/services/authz/rbac/service_test.go +++ b/pkg/services/authz/rbac/service_test.go @@ -3,6 +3,7 @@ package rbac import ( "context" "fmt" + "slices" "testing" "time" @@ -363,6 +364,7 @@ func TestService_mapping(t *testing.T) { }, output: &checkRequest{ Action: "folders:create", + ActionSets: []string{"folders:edit", "folders:admin"}, Group: "folder.grafana.app", Resource: "folders", Name: "aaa", @@ -618,6 +620,7 @@ func TestService_getUserPermissions(t *testing.T) { type testCase struct { name string permissions []accesscontrol.Permission + action string cacheHit bool expectedPerms map[string]bool } @@ -628,12 +631,14 @@ func TestService_getUserPermissions(t *testing.T) { permissions: []accesscontrol.Permission{ {Action: "dashboards:read", Scope: "dashboards:uid:some_dashboard"}, }, + action: "dashboards:read", cacheHit: false, expectedPerms: map[string]bool{"dashboards:uid:some_dashboard": true}, }, { name: "should return error if store fails", permissions: nil, + action: "dashboards:read", cacheHit: false, expectedPerms: map[string]bool{}, }, @@ -642,6 +647,7 @@ func TestService_getUserPermissions(t *testing.T) { permissions: []accesscontrol.Permission{ {Action: "teams:read", Scope: "teams:id:1"}, }, + action: "teams:read", cacheHit: false, expectedPerms: map[string]bool{"teams:uid:t1": true}, }, @@ -654,10 +660,9 @@ func TestService_getUserPermissions(t *testing.T) { ns := types.NamespaceInfo{Value: "stacks-12", OrgID: 1, StackID: 12} userID := &store.UserIdentifiers{UID: "test-uid", ID: 112} - action := "dashboards:read" if tc.cacheHit { - s.permCache.Set(ctx, userPermCacheKey(ns.Value, userID.UID, action), tc.expectedPerms) + s.permCache.Set(ctx, userPermCacheKey(ns.Value, userID.UID, tc.action), tc.expectedPerms) } store := &fakeStore{ @@ -677,7 +682,7 @@ func TestService_getUserPermissions(t *testing.T) { disableNsCheck: true, } - perms, err := s.getIdentityPermissions(ctx, ns, types.TypeUser, userID.UID, action) + perms, err := s.getIdentityPermissions(ctx, ns, types.TypeUser, userID.UID, tc.action, nil) require.NoError(t, err) require.Len(t, perms, len(tc.expectedPerms)) for scope := range perms { @@ -1086,6 +1091,53 @@ func TestService_Check(t *testing.T) { }, expected: true, }, + { + name: "should take into account action sets", + req: &authzv1.CheckRequest{ + Namespace: "org-12", + Subject: "user:test-uid", + Group: "dashboard.grafana.app", + Resource: "dashboards", + Verb: "get", + Name: "dash1", + }, + permissions: []accesscontrol.Permission{ + {Action: "dashboards:admin", Scope: "dashboards:uid:dash1"}, + }, + expected: true, + }, + { + name: "should take into account folder action sets for dashboard access", + req: &authzv1.CheckRequest{ + Namespace: "org-12", + Subject: "user:test-uid", + Group: "dashboard.grafana.app", + Resource: "dashboards", + Verb: "get", + Name: "dash1", + Folder: "some_folder", + }, + permissions: []accesscontrol.Permission{ + {Action: "folders:edit", Scope: "folders:uid:some_folder"}, + }, + expected: true, + }, + { + name: "lower level action set or action set on a different resource should not grant higher level access", + req: &authzv1.CheckRequest{ + Namespace: "org-12", + Subject: "user:test-uid", + Group: "folder.grafana.app", + Resource: "folders", + Verb: "delete", + Name: "folder1", + }, + permissions: []accesscontrol.Permission{ + {Action: "folders:view", Scope: "folders:uid:folder1"}, + {Action: "folders:edit", Scope: "folders:uid:other_folder"}, + }, + expected: false, + }, { // We've had cases where permissions were saved to the database // without splitting the scope into 'kind', 'attribute', and 'identifier'. @@ -1135,6 +1187,10 @@ func TestService_Check(t *testing.T) { if tc.req.Resource == "teams" { expAction = "teams:read" } + if tc.req.Resource == "folders" { + expAction = "folders:delete" + } + perms, ok := s.permCache.Get(ctx, userPermCacheKey("org-12", "test-uid", expAction)) require.True(t, ok) require.Len(t, perms, 1) @@ -1492,6 +1548,27 @@ func TestService_List(t *testing.T) { Folders: []string{"fold1"}, }, }, + { + name: "should list permissions for user with permission or action set permissions", + req: &authzv1.ListRequest{ + Namespace: "org-12", + Subject: "user:test-uid", + Group: "dashboard.grafana.app", + Resource: "dashboards", + Verb: "get", + }, + permissions: []accesscontrol.Permission{ + {Action: "dashboards:read", Scope: "dashboards:uid:dash1"}, + {Action: "dashboards:read", Scope: "dashboards:uid:dash2"}, + {Action: "dashboards:read", Scope: "folders:uid:fold1"}, + {Action: "dashboards:edit", Scope: "dashboards:uid:dash3"}, + {Action: "folders:view", Scope: "folders:uid:fold2"}, + }, + expected: &authzv1.ListResponse{ + Items: []string{"dash1", "dash2", "dash3"}, + Folders: []string{"fold1", "fold2"}, + }, + }, { name: "should return empty list for user without permission", req: &authzv1.ListRequest{ @@ -1824,7 +1901,13 @@ func (f *fakeStore) GetUserPermissions(ctx context.Context, namespace types.Name if f.err { return nil, fmt.Errorf("store error") } - return f.userPermissions, nil + var permissions []accesscontrol.Permission + for _, p := range f.userPermissions { + if p.Action == query.Action || slices.Contains(query.ActionSets, p.Action) { + permissions = append(permissions, p) + } + } + return permissions, nil } func (f *fakeStore) ListFolders(ctx context.Context, namespace types.NamespaceInfo) ([]store.Folder, error) { diff --git a/pkg/services/cloudmigration/cloudmigrationimpl/snapshot_mgmt_alerts_test.go b/pkg/services/cloudmigration/cloudmigrationimpl/snapshot_mgmt_alerts_test.go index 151d55562f9..ec50629c24b 100644 --- a/pkg/services/cloudmigration/cloudmigrationimpl/snapshot_mgmt_alerts_test.go +++ b/pkg/services/cloudmigration/cloudmigrationimpl/snapshot_mgmt_alerts_test.go @@ -20,7 +20,6 @@ import ( "github.com/grafana/grafana/pkg/services/datasources" "github.com/grafana/grafana/pkg/services/featuremgmt" "github.com/grafana/grafana/pkg/services/folder" - ac "github.com/grafana/grafana/pkg/services/ngalert/accesscontrol" "github.com/grafana/grafana/pkg/services/ngalert/api/tooling/definitions" "github.com/grafana/grafana/pkg/services/ngalert/models" "github.com/grafana/grafana/pkg/services/user" @@ -99,7 +98,7 @@ func TestGetContactPoints(t *testing.T) { Permissions: map[int64]map[string][]string{ 1: { accesscontrol.ActionAlertingNotificationsRead: nil, - accesscontrol.ActionAlertingReceiversReadSecrets: {ac.ScopeReceiversAll}, + accesscontrol.ActionAlertingReceiversReadSecrets: {models.ScopeReceiversAll}, }, }, } diff --git a/pkg/services/ngalert/accesscontrol.go b/pkg/services/ngalert/accesscontrol.go index 259db2bd5a9..e36c123c621 100644 --- a/pkg/services/ngalert/accesscontrol.go +++ b/pkg/services/ngalert/accesscontrol.go @@ -5,19 +5,17 @@ import ( "github.com/grafana/grafana/pkg/services/dashboards" "github.com/grafana/grafana/pkg/services/datasources" "github.com/grafana/grafana/pkg/services/featuremgmt" - ac "github.com/grafana/grafana/pkg/services/ngalert/accesscontrol" + "github.com/grafana/grafana/pkg/services/ngalert/models" "github.com/grafana/grafana/pkg/services/org" ) -const AlertRolesGroup = "Alerting" - var ( rulesReaderRole = accesscontrol.RoleRegistration{ Role: accesscontrol.RoleDTO{ Name: accesscontrol.FixedRolePrefix + "alerting.rules:reader", DisplayName: "Rules Reader", Description: "Read alert rules in all Grafana folders and external providers", - Group: AlertRolesGroup, + Group: models.AlertRolesGroup, Permissions: []accesscontrol.Permission{ { Action: accesscontrol.ActionAlertingRuleRead, @@ -47,7 +45,7 @@ var ( Name: accesscontrol.FixedRolePrefix + "alerting.rules:writer", DisplayName: "Rules Writer", Description: "Add, update, and delete rules in any Grafana folder and external providers", - Group: AlertRolesGroup, + Group: models.AlertRolesGroup, Permissions: accesscontrol.ConcatPermissions(rulesReaderRole.Role.Permissions, []accesscontrol.Permission{ { Action: accesscontrol.ActionAlertingRuleCreate, @@ -82,7 +80,7 @@ var ( Name: accesscontrol.FixedRolePrefix + "alerting.instances:reader", DisplayName: "Instances and Silences Reader", Description: "Read instances and silences of Grafana and external providers", - Group: AlertRolesGroup, + Group: models.AlertRolesGroup, Permissions: []accesscontrol.Permission{ { Action: accesscontrol.ActionAlertingInstanceRead, @@ -100,7 +98,7 @@ var ( Name: accesscontrol.FixedRolePrefix + "alerting.instances:writer", DisplayName: "Silences Writer", Description: "Add and update silences in Grafana and external providers", - Group: AlertRolesGroup, + Group: models.AlertRolesGroup, Permissions: accesscontrol.ConcatPermissions(instancesReaderRole.Role.Permissions, []accesscontrol.Permission{ { Action: accesscontrol.ActionAlertingInstanceCreate, @@ -121,9 +119,9 @@ var ( Name: accesscontrol.FixedRolePrefix + "alerting.receivers:reader", DisplayName: "Contact Point Reader", Description: "Read all contact points in Grafana", - Group: AlertRolesGroup, + Group: models.AlertRolesGroup, Permissions: []accesscontrol.Permission{ - {Action: accesscontrol.ActionAlertingReceiversRead, Scope: ac.ScopeReceiversAll}, + {Action: accesscontrol.ActionAlertingReceiversRead, Scope: models.ScopeReceiversAll}, }, }, } @@ -133,7 +131,7 @@ var ( Name: accesscontrol.FixedRolePrefix + "alerting.receivers:creator", DisplayName: "Contact Point Creator", Description: "Create new contact points in Grafana", - Group: AlertRolesGroup, + Group: models.AlertRolesGroup, Permissions: []accesscontrol.Permission{ {Action: accesscontrol.ActionAlertingReceiversCreate}, {Action: accesscontrol.ActionAlertingReceiversTest}, @@ -146,10 +144,10 @@ var ( Name: accesscontrol.FixedRolePrefix + "alerting.receivers:writer", DisplayName: "Contact Point Writer", Description: "Create, update, and delete all contact points in Grafana", - Group: AlertRolesGroup, + Group: models.AlertRolesGroup, Permissions: accesscontrol.ConcatPermissions(receiversReaderRole.Role.Permissions, receiversCreatorRole.Role.Permissions, []accesscontrol.Permission{ - {Action: accesscontrol.ActionAlertingReceiversUpdate, Scope: ac.ScopeReceiversAll}, - {Action: accesscontrol.ActionAlertingReceiversDelete, Scope: ac.ScopeReceiversAll}, + {Action: accesscontrol.ActionAlertingReceiversUpdate, Scope: models.ScopeReceiversAll}, + {Action: accesscontrol.ActionAlertingReceiversDelete, Scope: models.ScopeReceiversAll}, }), }, } @@ -159,7 +157,7 @@ var ( Name: accesscontrol.FixedRolePrefix + "alerting.templates:reader", DisplayName: "Templates Reader", Description: "Read all templates in Grafana alerting", - Group: AlertRolesGroup, + Group: models.AlertRolesGroup, Permissions: []accesscontrol.Permission{ {Action: accesscontrol.ActionAlertingNotificationsTemplatesRead}, }, @@ -171,7 +169,7 @@ var ( Name: accesscontrol.FixedRolePrefix + "alerting.templates:writer", DisplayName: "Templates Writer", Description: "Create, update, and delete all templates in Grafana alerting", - Group: AlertRolesGroup, + Group: models.AlertRolesGroup, Permissions: accesscontrol.ConcatPermissions(templatesReaderRole.Role.Permissions, []accesscontrol.Permission{ {Action: accesscontrol.ActionAlertingNotificationsTemplatesWrite}, {Action: accesscontrol.ActionAlertingNotificationsTemplatesDelete}, @@ -184,7 +182,7 @@ var ( Name: accesscontrol.FixedRolePrefix + "alerting.time-intervals:reader", DisplayName: "Time Intervals Reader", Description: "Read all time intervals in Grafana alerting", - Group: AlertRolesGroup, + Group: models.AlertRolesGroup, Permissions: []accesscontrol.Permission{ {Action: accesscontrol.ActionAlertingNotificationsTimeIntervalsRead}, }, @@ -196,7 +194,7 @@ var ( Name: accesscontrol.FixedRolePrefix + "alerting.time-intervals:writer", DisplayName: "Time Intervals Writer", Description: "Create, update, and delete all time intervals in Grafana alerting", - Group: AlertRolesGroup, + Group: models.AlertRolesGroup, Permissions: accesscontrol.ConcatPermissions(timeIntervalsReaderRole.Role.Permissions, []accesscontrol.Permission{ {Action: accesscontrol.ActionAlertingNotificationsTimeIntervalsWrite}, {Action: accesscontrol.ActionAlertingNotificationsTimeIntervalsDelete}, @@ -209,7 +207,7 @@ var ( Name: accesscontrol.FixedRolePrefix + "alerting.routes:reader", DisplayName: "Notification Policies Reader", Description: "Read all notification policies in Grafana alerting", - Group: AlertRolesGroup, + Group: models.AlertRolesGroup, Permissions: accesscontrol.ConcatPermissions([]accesscontrol.Permission{ {Action: accesscontrol.ActionAlertingRoutesRead}, }), @@ -221,7 +219,7 @@ var ( Name: accesscontrol.FixedRolePrefix + "alerting.routes:writer", DisplayName: "Notification Policies Writer", Description: "Update and reset notification policies in Grafana alerting", - Group: AlertRolesGroup, + Group: models.AlertRolesGroup, Permissions: accesscontrol.ConcatPermissions(routesReaderRole.Role.Permissions, []accesscontrol.Permission{ {Action: accesscontrol.ActionAlertingRoutesWrite}, }), @@ -233,7 +231,7 @@ var ( Name: accesscontrol.FixedRolePrefix + "alerting.notifications:reader", DisplayName: "Notifications Reader", Description: "Read notification policies and contact points in Grafana and external providers", - Group: AlertRolesGroup, + Group: models.AlertRolesGroup, Permissions: accesscontrol.ConcatPermissions(receiversReaderRole.Role.Permissions, templatesReaderRole.Role.Permissions, timeIntervalsReaderRole.Role.Permissions, routesReaderRole.Role.Permissions, []accesscontrol.Permission{ { Action: accesscontrol.ActionAlertingNotificationsRead, // TODO remove when we decide tò limit access to raw config API @@ -251,7 +249,7 @@ var ( Name: accesscontrol.FixedRolePrefix + "alerting.notifications:writer", DisplayName: "Notifications Writer", Description: "Add, update, and delete contact points and notification policies in Grafana and external providers", - Group: AlertRolesGroup, + Group: models.AlertRolesGroup, Permissions: accesscontrol.ConcatPermissions(notificationsReaderRole.Role.Permissions, receiversWriterRole.Role.Permissions, templatesWriterRole.Role.Permissions, timeIntervalsWriterRole.Role.Permissions, routesWriterRole.Role.Permissions, []accesscontrol.Permission{ { Action: accesscontrol.ActionAlertingNotificationsWrite, // TODO remove when we decide tò limit access to raw config API @@ -269,7 +267,7 @@ var ( Name: accesscontrol.FixedRolePrefix + "alerting:reader", DisplayName: "Full read-only access", Description: "Read alert rules, instances, silences, contact points, and notification policies in Grafana and all external providers", - Group: AlertRolesGroup, + Group: models.AlertRolesGroup, Permissions: accesscontrol.ConcatPermissions(rulesReaderRole.Role.Permissions, instancesReaderRole.Role.Permissions, notificationsReaderRole.Role.Permissions), }, Grants: []string{string(org.RoleViewer)}, @@ -280,7 +278,7 @@ var ( Name: accesscontrol.FixedRolePrefix + "alerting:writer", DisplayName: "Full write access", Description: "Add, update and delete alert rules, instances, silences, contact points, and notification policies in Grafana and all external providers", - Group: AlertRolesGroup, + Group: models.AlertRolesGroup, Permissions: accesscontrol.ConcatPermissions(rulesWriterRole.Role.Permissions, instancesWriterRole.Role.Permissions, notificationsWriterRole.Role.Permissions), }, Grants: []string{string(org.RoleEditor)}, @@ -291,11 +289,11 @@ var ( Name: accesscontrol.FixedRolePrefix + "alerting:admin", DisplayName: "Full admin access", Description: "Full write access in Grafana and all external providers, including their permissions and secrets", - Group: AlertRolesGroup, + Group: models.AlertRolesGroup, Permissions: accesscontrol.ConcatPermissions(alertingWriterRole.Role.Permissions, []accesscontrol.Permission{ - {Action: accesscontrol.ActionAlertingReceiversPermissionsRead, Scope: ac.ScopeReceiversAll}, - {Action: accesscontrol.ActionAlertingReceiversPermissionsWrite, Scope: ac.ScopeReceiversAll}, - {Action: accesscontrol.ActionAlertingReceiversReadSecrets, Scope: ac.ScopeReceiversAll}, + {Action: accesscontrol.ActionAlertingReceiversPermissionsRead, Scope: models.ScopeReceiversAll}, + {Action: accesscontrol.ActionAlertingReceiversPermissionsWrite, Scope: models.ScopeReceiversAll}, + {Action: accesscontrol.ActionAlertingReceiversReadSecrets, Scope: models.ScopeReceiversAll}, }), }, Grants: []string{string(org.RoleAdmin)}, @@ -306,7 +304,7 @@ var ( Name: accesscontrol.FixedRolePrefix + "alerting.provisioning:writer", DisplayName: "Access to alert rules provisioning API", Description: "Manage all alert rules, contact points, notification policies, silences, etc. in the organization via provisioning API.", - Group: AlertRolesGroup, + Group: models.AlertRolesGroup, Permissions: []accesscontrol.Permission{ { Action: accesscontrol.ActionAlertingProvisioningRead, // organization scope @@ -340,7 +338,7 @@ var ( Name: accesscontrol.FixedRolePrefix + "alerting.provisioning.secrets:reader", DisplayName: "Read via Provisioning API + Export Secrets", Description: "Read all alert rules, contact points, notification policies, silences, etc. in the organization via provisioning API and use export with decrypted secrets", - Group: AlertRolesGroup, + Group: models.AlertRolesGroup, Permissions: []accesscontrol.Permission{ { Action: accesscontrol.ActionAlertingProvisioningReadSecrets, // organization scope @@ -364,7 +362,7 @@ var ( Name: accesscontrol.FixedRolePrefix + "alerting.provisioning.provenance:writer", DisplayName: "Set provisioning status", Description: "Set provisioning status for alerting resources. Should be used together with other regular roles (Notifications Writer and/or Rules Writer)", - Group: AlertRolesGroup, + Group: models.AlertRolesGroup, Permissions: []accesscontrol.Permission{ { Action: accesscontrol.ActionAlertingProvisioningSetStatus, // organization scope diff --git a/pkg/services/ngalert/accesscontrol/receivers.go b/pkg/services/ngalert/accesscontrol/receivers.go index 1016f246a41..eb2904854bf 100644 --- a/pkg/services/ngalert/accesscontrol/receivers.go +++ b/pkg/services/ngalert/accesscontrol/receivers.go @@ -2,52 +2,10 @@ package accesscontrol import ( "context" - // #nosec G505 Used only for shortening the uid, not for security purposes. - "crypto/sha1" - "encoding/hex" "github.com/grafana/grafana/pkg/apimachinery/identity" ac "github.com/grafana/grafana/pkg/services/accesscontrol" "github.com/grafana/grafana/pkg/services/ngalert/models" - "github.com/grafana/grafana/pkg/util" -) - -const ( - ScopeReceiversRoot = "receivers" -) - -var ( - ScopeReceiversProvider = ReceiverScopeProvider{ac.NewScopeProvider(ScopeReceiversRoot)} - ScopeReceiversAll = ScopeReceiversProvider.GetResourceAllScope() -) - -type ReceiverScopeProvider struct { - ac.ScopeProvider -} - -func (p ReceiverScopeProvider) GetResourceScopeUID(uid string) string { - return ScopeReceiversProvider.ScopeProvider.GetResourceScopeUID(p.GetResourceIDFromUID(uid)) -} - -// GetResourceIDFromUID converts a receiver uid to a resource id. This is necessary as resource ids are limited to 40 characters. -// If the uid is already less than or equal to 40 characters, it is returned as is. -func (p ReceiverScopeProvider) GetResourceIDFromUID(uid string) string { - if len(uid) <= util.MaxUIDLength { - return uid - } - // #nosec G505 Used only for shortening the uid, not for security purposes. - h := sha1.New() - h.Write([]byte(uid)) - return hex.EncodeToString(h.Sum(nil)) -} - -// ReceiverPermission is a type for representing a receiver permission. -type ReceiverPermission string - -const ( - ReceiverPermissionView ReceiverPermission = "View" - ReceiverPermissionEdit ReceiverPermission = "Edit" - ReceiverPermissionAdmin ReceiverPermission = "Admin" ) var ( @@ -65,19 +23,19 @@ var ( // Asserts read-only access to all redacted receivers. readRedactedAllReceiversEval = ac.EvalAny( ac.EvalPermission(ac.ActionAlertingNotificationsRead), - ac.EvalPermission(ac.ActionAlertingReceiversRead, ScopeReceiversAll), + ac.EvalPermission(ac.ActionAlertingReceiversRead, models.ScopeReceiversAll), readDecryptedAllReceiversEval, ) // Asserts read-only access to all decrypted receivers. readDecryptedAllReceiversEval = ac.EvalAny( - ac.EvalPermission(ac.ActionAlertingReceiversReadSecrets, ScopeReceiversAll), + ac.EvalPermission(ac.ActionAlertingReceiversReadSecrets, models.ScopeReceiversAll), ) // Asserts read-only access to a specific redacted receiver. readRedactedReceiverEval = func(uid string) ac.Evaluator { return ac.EvalAny( ac.EvalPermission(ac.ActionAlertingNotificationsRead), - ac.EvalPermission(ac.ActionAlertingReceiversRead, ScopeReceiversProvider.GetResourceScopeUID(uid)), + ac.EvalPermission(ac.ActionAlertingReceiversRead, models.ScopeReceiversProvider.GetResourceScopeUID(uid)), readDecryptedReceiverEval(uid), ) } @@ -85,7 +43,7 @@ var ( // Asserts read-only access to a specific decrypted receiver. readDecryptedReceiverEval = func(uid string) ac.Evaluator { return ac.EvalAny( - ac.EvalPermission(ac.ActionAlertingReceiversReadSecrets, ScopeReceiversProvider.GetResourceScopeUID(uid)), + ac.EvalPermission(ac.ActionAlertingReceiversReadSecrets, models.ScopeReceiversProvider.GetResourceScopeUID(uid)), ) } @@ -126,14 +84,14 @@ var ( // Asserts update access to all receivers. updateAllReceiversEval = ac.EvalAny( ac.EvalPermission(ac.ActionAlertingNotificationsWrite), - ac.EvalPermission(ac.ActionAlertingReceiversUpdate, ScopeReceiversAll), + ac.EvalPermission(ac.ActionAlertingReceiversUpdate, models.ScopeReceiversAll), ) // Asserts update access to a specific receiver. updateReceiverEval = func(uid string) ac.Evaluator { return ac.EvalAny( ac.EvalPermission(ac.ActionAlertingNotificationsWrite), - ac.EvalPermission(ac.ActionAlertingReceiversUpdate, ScopeReceiversProvider.GetResourceScopeUID(uid)), + ac.EvalPermission(ac.ActionAlertingReceiversUpdate, models.ScopeReceiversProvider.GetResourceScopeUID(uid)), ) } @@ -148,14 +106,14 @@ var ( // Asserts delete access to all receivers. deleteAllReceiversEval = ac.EvalAny( ac.EvalPermission(ac.ActionAlertingNotificationsWrite), - ac.EvalPermission(ac.ActionAlertingReceiversDelete, ScopeReceiversAll), + ac.EvalPermission(ac.ActionAlertingReceiversDelete, models.ScopeReceiversAll), ) // Asserts delete access to a specific receiver. deleteReceiverEval = func(uid string) ac.Evaluator { return ac.EvalAny( ac.EvalPermission(ac.ActionAlertingNotificationsWrite), - ac.EvalPermission(ac.ActionAlertingReceiversDelete, ScopeReceiversProvider.GetResourceScopeUID(uid)), + ac.EvalPermission(ac.ActionAlertingReceiversDelete, models.ScopeReceiversProvider.GetResourceScopeUID(uid)), ) } @@ -169,15 +127,15 @@ var ( // Asserts resource permissions access to all receivers. permissionsAllReceiversEval = ac.EvalAll( - ac.EvalPermission(ac.ActionAlertingReceiversPermissionsRead, ScopeReceiversAll), - ac.EvalPermission(ac.ActionAlertingReceiversPermissionsWrite, ScopeReceiversAll), + ac.EvalPermission(ac.ActionAlertingReceiversPermissionsRead, models.ScopeReceiversAll), + ac.EvalPermission(ac.ActionAlertingReceiversPermissionsWrite, models.ScopeReceiversAll), ) // Asserts resource permissions access to a specific receiver. permissionsReceiverEval = func(uid string) ac.Evaluator { return ac.EvalAll( - ac.EvalPermission(ac.ActionAlertingReceiversPermissionsRead, ScopeReceiversProvider.GetResourceScopeUID(uid)), - ac.EvalPermission(ac.ActionAlertingReceiversPermissionsWrite, ScopeReceiversProvider.GetResourceScopeUID(uid)), + ac.EvalPermission(ac.ActionAlertingReceiversPermissionsRead, models.ScopeReceiversProvider.GetResourceScopeUID(uid)), + ac.EvalPermission(ac.ActionAlertingReceiversPermissionsWrite, models.ScopeReceiversProvider.GetResourceScopeUID(uid)), ) } ) diff --git a/pkg/services/ngalert/accesscontrol/receivers_test.go b/pkg/services/ngalert/accesscontrol/receivers_test.go index 6ad2e1db463..7be1e9498e2 100644 --- a/pkg/services/ngalert/accesscontrol/receivers_test.go +++ b/pkg/services/ngalert/accesscontrol/receivers_test.go @@ -85,7 +85,7 @@ func TestReceiverAccess(t *testing.T) { // Receiver read. { name: "global receiver reader should have no elevated permissions", - user: newEmptyUser(ac.Permission{Action: ac.ActionAlertingReceiversRead, Scope: ScopeReceiversAll}), + user: newEmptyUser(ac.Permission{Action: ac.ActionAlertingReceiversRead, Scope: models.ScopeReceiversAll}), expected: map[string]models.ReceiverPermissionSet{ recv1.UID: permissions(), recv2.UID: permissions(), @@ -94,7 +94,7 @@ func TestReceiverAccess(t *testing.T) { }, { name: "global receiver secret reader should have secret permissions", - user: newEmptyUser(ac.Permission{Action: ac.ActionAlertingReceiversReadSecrets, Scope: ScopeReceiversAll}), + user: newEmptyUser(ac.Permission{Action: ac.ActionAlertingReceiversReadSecrets, Scope: models.ScopeReceiversAll}), expected: map[string]models.ReceiverPermissionSet{ recv1.UID: permissions(models.ReceiverPermissionReadSecret), recv2.UID: permissions(models.ReceiverPermissionReadSecret), @@ -104,8 +104,8 @@ func TestReceiverAccess(t *testing.T) { { name: "per-receiver secret reader should have per-receiver", user: newEmptyUser( - ac.Permission{Action: ac.ActionAlertingReceiversReadSecrets, Scope: ScopeReceiversProvider.GetResourceScopeUID(recv1.UID)}, - ac.Permission{Action: ac.ActionAlertingReceiversReadSecrets, Scope: ScopeReceiversProvider.GetResourceScopeUID(recv3.UID)}, + ac.Permission{Action: ac.ActionAlertingReceiversReadSecrets, Scope: models.ScopeReceiversProvider.GetResourceScopeUID(recv1.UID)}, + ac.Permission{Action: ac.ActionAlertingReceiversReadSecrets, Scope: models.ScopeReceiversProvider.GetResourceScopeUID(recv3.UID)}, ), expected: map[string]models.ReceiverPermissionSet{ recv1.UID: permissions(models.ReceiverPermissionReadSecret), @@ -173,7 +173,7 @@ func TestReceiverAccess(t *testing.T) { // Receiver update. { name: "global receiver update should have write but no delete", - user: newViewUser(ac.Permission{Action: ac.ActionAlertingReceiversUpdate, Scope: ScopeReceiversAll}), + user: newViewUser(ac.Permission{Action: ac.ActionAlertingReceiversUpdate, Scope: models.ScopeReceiversAll}), expected: map[string]models.ReceiverPermissionSet{ recv1.UID: permissions(models.ReceiverPermissionWrite), recv2.UID: permissions(models.ReceiverPermissionWrite), @@ -183,8 +183,8 @@ func TestReceiverAccess(t *testing.T) { { name: "per-receiver update should have per-receiver write but no delete", user: newViewUser( - ac.Permission{Action: ac.ActionAlertingReceiversUpdate, Scope: ScopeReceiversProvider.GetResourceScopeUID(recv1.UID)}, - ac.Permission{Action: ac.ActionAlertingReceiversUpdate, Scope: ScopeReceiversProvider.GetResourceScopeUID(recv3.UID)}, + ac.Permission{Action: ac.ActionAlertingReceiversUpdate, Scope: models.ScopeReceiversProvider.GetResourceScopeUID(recv1.UID)}, + ac.Permission{Action: ac.ActionAlertingReceiversUpdate, Scope: models.ScopeReceiversProvider.GetResourceScopeUID(recv3.UID)}, ), expected: map[string]models.ReceiverPermissionSet{ recv1.UID: permissions(models.ReceiverPermissionWrite), @@ -195,8 +195,8 @@ func TestReceiverAccess(t *testing.T) { { name: "per-receiver update should require read", user: newEmptyUser( - ac.Permission{Action: ac.ActionAlertingReceiversUpdate, Scope: ScopeReceiversProvider.GetResourceScopeUID(recv1.UID)}, - ac.Permission{Action: ac.ActionAlertingReceiversUpdate, Scope: ScopeReceiversProvider.GetResourceScopeUID(recv3.UID)}, + ac.Permission{Action: ac.ActionAlertingReceiversUpdate, Scope: models.ScopeReceiversProvider.GetResourceScopeUID(recv1.UID)}, + ac.Permission{Action: ac.ActionAlertingReceiversUpdate, Scope: models.ScopeReceiversProvider.GetResourceScopeUID(recv3.UID)}, ), expected: map[string]models.ReceiverPermissionSet{ recv1.UID: permissions(), @@ -207,7 +207,7 @@ func TestReceiverAccess(t *testing.T) { // Receiver delete. { name: "global receiver delete should have delete but no write", - user: newViewUser(ac.Permission{Action: ac.ActionAlertingReceiversDelete, Scope: ScopeReceiversAll}), + user: newViewUser(ac.Permission{Action: ac.ActionAlertingReceiversDelete, Scope: models.ScopeReceiversAll}), expected: map[string]models.ReceiverPermissionSet{ recv1.UID: permissions(models.ReceiverPermissionDelete), recv2.UID: permissions(models.ReceiverPermissionDelete), @@ -217,8 +217,8 @@ func TestReceiverAccess(t *testing.T) { { name: "per-receiver delete should have per-receiver delete but no write", user: newViewUser( - ac.Permission{Action: ac.ActionAlertingReceiversDelete, Scope: ScopeReceiversProvider.GetResourceScopeUID(recv1.UID)}, - ac.Permission{Action: ac.ActionAlertingReceiversDelete, Scope: ScopeReceiversProvider.GetResourceScopeUID(recv3.UID)}, + ac.Permission{Action: ac.ActionAlertingReceiversDelete, Scope: models.ScopeReceiversProvider.GetResourceScopeUID(recv1.UID)}, + ac.Permission{Action: ac.ActionAlertingReceiversDelete, Scope: models.ScopeReceiversProvider.GetResourceScopeUID(recv3.UID)}, ), expected: map[string]models.ReceiverPermissionSet{ recv1.UID: permissions(models.ReceiverPermissionDelete), @@ -229,8 +229,8 @@ func TestReceiverAccess(t *testing.T) { { name: "per-receiver delete should require read", user: newEmptyUser( - ac.Permission{Action: ac.ActionAlertingReceiversDelete, Scope: ScopeReceiversProvider.GetResourceScopeUID(recv1.UID)}, - ac.Permission{Action: ac.ActionAlertingReceiversDelete, Scope: ScopeReceiversProvider.GetResourceScopeUID(recv3.UID)}, + ac.Permission{Action: ac.ActionAlertingReceiversDelete, Scope: models.ScopeReceiversProvider.GetResourceScopeUID(recv1.UID)}, + ac.Permission{Action: ac.ActionAlertingReceiversDelete, Scope: models.ScopeReceiversProvider.GetResourceScopeUID(recv3.UID)}, ), expected: map[string]models.ReceiverPermissionSet{ recv1.UID: permissions(), @@ -241,7 +241,7 @@ func TestReceiverAccess(t *testing.T) { // Receiver admin. { name: "receiver read permissions alone can't admin", - user: newViewUser(ac.Permission{Action: ac.ActionAlertingReceiversPermissionsRead, Scope: ScopeReceiversAll}), + user: newViewUser(ac.Permission{Action: ac.ActionAlertingReceiversPermissionsRead, Scope: models.ScopeReceiversAll}), expected: map[string]models.ReceiverPermissionSet{ recv1.UID: permissions(), recv2.UID: permissions(), @@ -250,7 +250,7 @@ func TestReceiverAccess(t *testing.T) { }, { name: "receiver write permissions alone can't admin", - user: newViewUser(ac.Permission{Action: ac.ActionAlertingReceiversPermissionsWrite, Scope: ScopeReceiversAll}), + user: newViewUser(ac.Permission{Action: ac.ActionAlertingReceiversPermissionsWrite, Scope: models.ScopeReceiversAll}), expected: map[string]models.ReceiverPermissionSet{ recv1.UID: permissions(), recv2.UID: permissions(), @@ -260,8 +260,8 @@ func TestReceiverAccess(t *testing.T) { { name: "global receiver read + write permissions can admin", user: newViewUser( - ac.Permission{Action: ac.ActionAlertingReceiversPermissionsRead, Scope: ScopeReceiversAll}, - ac.Permission{Action: ac.ActionAlertingReceiversPermissionsWrite, Scope: ScopeReceiversAll}, + ac.Permission{Action: ac.ActionAlertingReceiversPermissionsRead, Scope: models.ScopeReceiversAll}, + ac.Permission{Action: ac.ActionAlertingReceiversPermissionsWrite, Scope: models.ScopeReceiversAll}, ), expected: map[string]models.ReceiverPermissionSet{ recv1.UID: permissions(models.ReceiverPermissionAdmin), @@ -272,10 +272,10 @@ func TestReceiverAccess(t *testing.T) { { name: "per-receiver read + write permissions should have per-receiver admin", user: newViewUser( - ac.Permission{Action: ac.ActionAlertingReceiversPermissionsRead, Scope: ScopeReceiversProvider.GetResourceScopeUID(recv1.UID)}, - ac.Permission{Action: ac.ActionAlertingReceiversPermissionsWrite, Scope: ScopeReceiversProvider.GetResourceScopeUID(recv1.UID)}, - ac.Permission{Action: ac.ActionAlertingReceiversPermissionsRead, Scope: ScopeReceiversProvider.GetResourceScopeUID(recv3.UID)}, - ac.Permission{Action: ac.ActionAlertingReceiversPermissionsWrite, Scope: ScopeReceiversProvider.GetResourceScopeUID(recv3.UID)}, + ac.Permission{Action: ac.ActionAlertingReceiversPermissionsRead, Scope: models.ScopeReceiversProvider.GetResourceScopeUID(recv1.UID)}, + ac.Permission{Action: ac.ActionAlertingReceiversPermissionsWrite, Scope: models.ScopeReceiversProvider.GetResourceScopeUID(recv1.UID)}, + ac.Permission{Action: ac.ActionAlertingReceiversPermissionsRead, Scope: models.ScopeReceiversProvider.GetResourceScopeUID(recv3.UID)}, + ac.Permission{Action: ac.ActionAlertingReceiversPermissionsWrite, Scope: models.ScopeReceiversProvider.GetResourceScopeUID(recv3.UID)}, ), expected: map[string]models.ReceiverPermissionSet{ recv1.UID: permissions(models.ReceiverPermissionAdmin), @@ -286,10 +286,10 @@ func TestReceiverAccess(t *testing.T) { { name: "per-receiver admin should require read", user: newEmptyUser( - ac.Permission{Action: ac.ActionAlertingReceiversPermissionsRead, Scope: ScopeReceiversProvider.GetResourceScopeUID(recv1.UID)}, - ac.Permission{Action: ac.ActionAlertingReceiversPermissionsWrite, Scope: ScopeReceiversProvider.GetResourceScopeUID(recv1.UID)}, - ac.Permission{Action: ac.ActionAlertingReceiversPermissionsRead, Scope: ScopeReceiversProvider.GetResourceScopeUID(recv3.UID)}, - ac.Permission{Action: ac.ActionAlertingReceiversPermissionsWrite, Scope: ScopeReceiversProvider.GetResourceScopeUID(recv3.UID)}, + ac.Permission{Action: ac.ActionAlertingReceiversPermissionsRead, Scope: models.ScopeReceiversProvider.GetResourceScopeUID(recv1.UID)}, + ac.Permission{Action: ac.ActionAlertingReceiversPermissionsWrite, Scope: models.ScopeReceiversProvider.GetResourceScopeUID(recv1.UID)}, + ac.Permission{Action: ac.ActionAlertingReceiversPermissionsRead, Scope: models.ScopeReceiversProvider.GetResourceScopeUID(recv3.UID)}, + ac.Permission{Action: ac.ActionAlertingReceiversPermissionsWrite, Scope: models.ScopeReceiversProvider.GetResourceScopeUID(recv3.UID)}, ), expected: map[string]models.ReceiverPermissionSet{ recv1.UID: permissions(), @@ -302,7 +302,7 @@ func TestReceiverAccess(t *testing.T) { name: "legacy provisioning secret read, receiver write", user: newViewUser( ac.Permission{Action: ac.ActionAlertingProvisioningReadSecrets}, - ac.Permission{Action: ac.ActionAlertingReceiversUpdate, Scope: ScopeReceiversProvider.GetResourceScopeUID(recv2.UID)}, + ac.Permission{Action: ac.ActionAlertingReceiversUpdate, Scope: models.ScopeReceiversProvider.GetResourceScopeUID(recv2.UID)}, ), expected: map[string]models.ReceiverPermissionSet{ recv1.UID: permissions(), @@ -319,7 +319,7 @@ func TestReceiverAccess(t *testing.T) { name: "legacy provisioning secret read, receiver delete", user: newViewUser( ac.Permission{Action: ac.ActionAlertingProvisioningReadSecrets}, - ac.Permission{Action: ac.ActionAlertingReceiversDelete, Scope: ScopeReceiversProvider.GetResourceScopeUID(recv2.UID)}, + ac.Permission{Action: ac.ActionAlertingReceiversDelete, Scope: models.ScopeReceiversProvider.GetResourceScopeUID(recv2.UID)}, ), expected: map[string]models.ReceiverPermissionSet{ recv1.UID: permissions(), @@ -336,7 +336,7 @@ func TestReceiverAccess(t *testing.T) { name: "legacy write, receiver secret", user: newViewUser( ac.Permission{Action: ac.ActionAlertingNotificationsWrite}, - ac.Permission{Action: ac.ActionAlertingReceiversReadSecrets, Scope: ScopeReceiversProvider.GetResourceScopeUID(recv2.UID)}, + ac.Permission{Action: ac.ActionAlertingReceiversReadSecrets, Scope: models.ScopeReceiversProvider.GetResourceScopeUID(recv2.UID)}, ), expected: map[string]models.ReceiverPermissionSet{ recv1.UID: permissions(models.ReceiverPermissionWrite, models.ReceiverPermissionDelete), @@ -347,12 +347,12 @@ func TestReceiverAccess(t *testing.T) { { name: "mixed secret / delete / write", user: newViewUser( - ac.Permission{Action: ac.ActionAlertingReceiversReadSecrets, Scope: ScopeReceiversProvider.GetResourceScopeUID(recv1.UID)}, - ac.Permission{Action: ac.ActionAlertingReceiversReadSecrets, Scope: ScopeReceiversProvider.GetResourceScopeUID(recv3.UID)}, - ac.Permission{Action: ac.ActionAlertingReceiversUpdate, Scope: ScopeReceiversProvider.GetResourceScopeUID(recv1.UID)}, - ac.Permission{Action: ac.ActionAlertingReceiversUpdate, Scope: ScopeReceiversProvider.GetResourceScopeUID(recv2.UID)}, - ac.Permission{Action: ac.ActionAlertingReceiversDelete, Scope: ScopeReceiversProvider.GetResourceScopeUID(recv2.UID)}, - ac.Permission{Action: ac.ActionAlertingReceiversDelete, Scope: ScopeReceiversProvider.GetResourceScopeUID(recv3.UID)}, + ac.Permission{Action: ac.ActionAlertingReceiversReadSecrets, Scope: models.ScopeReceiversProvider.GetResourceScopeUID(recv1.UID)}, + ac.Permission{Action: ac.ActionAlertingReceiversReadSecrets, Scope: models.ScopeReceiversProvider.GetResourceScopeUID(recv3.UID)}, + ac.Permission{Action: ac.ActionAlertingReceiversUpdate, Scope: models.ScopeReceiversProvider.GetResourceScopeUID(recv1.UID)}, + ac.Permission{Action: ac.ActionAlertingReceiversUpdate, Scope: models.ScopeReceiversProvider.GetResourceScopeUID(recv2.UID)}, + ac.Permission{Action: ac.ActionAlertingReceiversDelete, Scope: models.ScopeReceiversProvider.GetResourceScopeUID(recv2.UID)}, + ac.Permission{Action: ac.ActionAlertingReceiversDelete, Scope: models.ScopeReceiversProvider.GetResourceScopeUID(recv3.UID)}, ), expected: map[string]models.ReceiverPermissionSet{ recv1.UID: permissions(models.ReceiverPermissionReadSecret, models.ReceiverPermissionWrite), @@ -363,12 +363,12 @@ func TestReceiverAccess(t *testing.T) { { name: "mixed requires read", user: newEmptyUser( - ac.Permission{Action: ac.ActionAlertingReceiversReadSecrets, Scope: ScopeReceiversProvider.GetResourceScopeUID(recv1.UID)}, - ac.Permission{Action: ac.ActionAlertingReceiversReadSecrets, Scope: ScopeReceiversProvider.GetResourceScopeUID(recv3.UID)}, - ac.Permission{Action: ac.ActionAlertingReceiversUpdate, Scope: ScopeReceiversProvider.GetResourceScopeUID(recv1.UID)}, - ac.Permission{Action: ac.ActionAlertingReceiversUpdate, Scope: ScopeReceiversProvider.GetResourceScopeUID(recv2.UID)}, - ac.Permission{Action: ac.ActionAlertingReceiversDelete, Scope: ScopeReceiversProvider.GetResourceScopeUID(recv2.UID)}, - ac.Permission{Action: ac.ActionAlertingReceiversDelete, Scope: ScopeReceiversProvider.GetResourceScopeUID(recv3.UID)}, + ac.Permission{Action: ac.ActionAlertingReceiversReadSecrets, Scope: models.ScopeReceiversProvider.GetResourceScopeUID(recv1.UID)}, + ac.Permission{Action: ac.ActionAlertingReceiversReadSecrets, Scope: models.ScopeReceiversProvider.GetResourceScopeUID(recv3.UID)}, + ac.Permission{Action: ac.ActionAlertingReceiversUpdate, Scope: models.ScopeReceiversProvider.GetResourceScopeUID(recv1.UID)}, + ac.Permission{Action: ac.ActionAlertingReceiversUpdate, Scope: models.ScopeReceiversProvider.GetResourceScopeUID(recv2.UID)}, + ac.Permission{Action: ac.ActionAlertingReceiversDelete, Scope: models.ScopeReceiversProvider.GetResourceScopeUID(recv2.UID)}, + ac.Permission{Action: ac.ActionAlertingReceiversDelete, Scope: models.ScopeReceiversProvider.GetResourceScopeUID(recv3.UID)}, ), expected: map[string]models.ReceiverPermissionSet{ recv1.UID: permissions(models.ReceiverPermissionReadSecret, models.ReceiverPermissionWrite), @@ -404,7 +404,7 @@ func newEmptyUser(permissions ...ac.Permission) identity.Requester { func newViewUser(permissions ...ac.Permission) identity.Requester { return ac.BackgroundUser("test", orgID, org.RoleNone, append([]ac.Permission{ - {Action: ac.ActionAlertingReceiversRead, Scope: ScopeReceiversAll}, + {Action: ac.ActionAlertingReceiversRead, Scope: models.ScopeReceiversAll}, {Action: ac.ActionAlertingNotificationsRead}, }, permissions...)) } diff --git a/pkg/services/ngalert/accesscontrol/rules_test.go b/pkg/services/ngalert/accesscontrol/rules_test.go index a9006f5ef0f..0cf9a4454ab 100644 --- a/pkg/services/ngalert/accesscontrol/rules_test.go +++ b/pkg/services/ngalert/accesscontrol/rules_test.go @@ -85,7 +85,7 @@ func getReceiverScopesForRules(rules models.RulesGroup) []string { var result []string for _, rule := range rules { for _, ns := range rule.NotificationSettings { - scope := ScopeReceiversProvider.GetResourceScopeUID(legacy_storage.NameToUid(ns.Receiver)) + scope := models.ScopeReceiversProvider.GetResourceScopeUID(legacy_storage.NameToUid(ns.Receiver)) if _, ok := scopesMap[scope]; ok { continue } diff --git a/pkg/services/ngalert/models/accesscontrol.go b/pkg/services/ngalert/models/accesscontrol.go new file mode 100644 index 00000000000..1a8da627e84 --- /dev/null +++ b/pkg/services/ngalert/models/accesscontrol.go @@ -0,0 +1,44 @@ +package models + +import ( + // #nosec G505 Used only for shortening the uid, not for security purposes. + "crypto/sha1" + "encoding/hex" + + "github.com/grafana/grafana/pkg/services/accesscontrol" + "github.com/grafana/grafana/pkg/util" +) + +const ( + ScopeReceiversRoot = "receivers" + AlertRolesGroup = "Alerting" + + PermissionView ReceiverPermission = "View" + PermissionEdit ReceiverPermission = "Edit" + PermissionAdmin ReceiverPermission = "Admin" +) + +var ( + ScopeReceiversProvider = ReceiverScopeProvider{accesscontrol.NewScopeProvider(ScopeReceiversRoot)} + ScopeReceiversAll = ScopeReceiversProvider.GetResourceAllScope() +) + +type ReceiverScopeProvider struct { + accesscontrol.ScopeProvider +} + +func (p ReceiverScopeProvider) GetResourceScopeUID(uid string) string { + return ScopeReceiversProvider.ScopeProvider.GetResourceScopeUID(p.GetResourceIDFromUID(uid)) +} + +// GetResourceIDFromUID converts a receiver uid to a resource id. This is necessary as resource ids are limited to 40 characters. +// If the uid is already less than or equal to 40 characters, it is returned as is. +func (p ReceiverScopeProvider) GetResourceIDFromUID(uid string) string { + if len(uid) <= util.MaxUIDLength { + return uid + } + // #nosec G505 Used only for shortening the uid, not for security purposes. + h := sha1.New() + h.Write([]byte(uid)) + return hex.EncodeToString(h.Sum(nil)) +} diff --git a/pkg/services/ngalert/notifier/receiver_svc_test.go b/pkg/services/ngalert/notifier/receiver_svc_test.go index ad968282ed1..85e7733f4bd 100644 --- a/pkg/services/ngalert/notifier/receiver_svc_test.go +++ b/pkg/services/ngalert/notifier/receiver_svc_test.go @@ -117,7 +117,7 @@ func TestIntegrationReceiverService_DecryptRedact(t *testing.T) { Permissions: map[int64]map[string][]string{ 1: { accesscontrol.ActionAlertingNotificationsRead: nil, - accesscontrol.ActionAlertingReceiversReadSecrets: {ac.ScopeReceiversAll}, + accesscontrol.ActionAlertingReceiversReadSecrets: {models.ScopeReceiversAll}, }, }, } @@ -343,7 +343,7 @@ func TestReceiverService_Create(t *testing.T) { }} decryptUser := &user.SignedInUser{OrgID: 1, Permissions: map[int64]map[string][]string{ 1: { - accesscontrol.ActionAlertingReceiversReadSecrets: {ac.ScopeReceiversAll}, + accesscontrol.ActionAlertingReceiversReadSecrets: {models.ScopeReceiversAll}, }, }} @@ -556,7 +556,7 @@ func TestReceiverService_Update(t *testing.T) { }} decryptUser := &user.SignedInUser{OrgID: 1, Permissions: map[int64]map[string][]string{ 1: { - accesscontrol.ActionAlertingReceiversReadSecrets: {ac.ScopeReceiversAll}, + accesscontrol.ActionAlertingReceiversReadSecrets: {models.ScopeReceiversAll}, }, }} @@ -949,30 +949,30 @@ func TestReceiverServiceAC_Read(t *testing.T) { }, { name: "global receivers permissions - read all", - permissions: map[string][]string{accesscontrol.ActionAlertingReceiversRead: {ac.ScopeReceiversAll}}, + permissions: map[string][]string{accesscontrol.ActionAlertingReceiversRead: {models.ScopeReceiversAll}}, existing: allReceivers(), visible: allReceivers(), }, { name: "single receivers permissions - read some", permissions: map[string][]string{accesscontrol.ActionAlertingReceiversRead: { - ac.ScopeReceiversProvider.GetResourceScopeUID(recv1.UID), - ac.ScopeReceiversProvider.GetResourceScopeUID(recv3.UID), + models.ScopeReceiversProvider.GetResourceScopeUID(recv1.UID), + models.ScopeReceiversProvider.GetResourceScopeUID(recv3.UID), }}, existing: allReceivers(), visible: []models.Receiver{recv1, recv3}, }, { name: "global receivers secret permissions - read all", - permissions: map[string][]string{accesscontrol.ActionAlertingReceiversReadSecrets: {ac.ScopeReceiversAll}}, + permissions: map[string][]string{accesscontrol.ActionAlertingReceiversReadSecrets: {models.ScopeReceiversAll}}, existing: allReceivers(), visible: allReceivers(), }, { name: "single receivers secret permissions - read some", permissions: map[string][]string{accesscontrol.ActionAlertingReceiversReadSecrets: { - ac.ScopeReceiversProvider.GetResourceScopeUID(recv1.UID), - ac.ScopeReceiversProvider.GetResourceScopeUID(recv3.UID), + models.ScopeReceiversProvider.GetResourceScopeUID(recv1.UID), + models.ScopeReceiversProvider.GetResourceScopeUID(recv3.UID), }}, existing: allReceivers(), visible: []models.Receiver{recv1, recv3}, @@ -1175,15 +1175,15 @@ func TestReceiverServiceAC_Update(t *testing.T) { }, { name: "global receivers permissions - not authorized without read", - permissions: map[string][]string{accesscontrol.ActionAlertingReceiversUpdate: {ac.ScopeReceiversAll}}, + permissions: map[string][]string{accesscontrol.ActionAlertingReceiversUpdate: {models.ScopeReceiversAll}}, existing: allReceivers(), hasAccess: nil, }, { name: "single receivers permissions - not authorized without read", permissions: map[string][]string{accesscontrol.ActionAlertingReceiversUpdate: { - ac.ScopeReceiversProvider.GetResourceScopeUID(recv1.UID), - ac.ScopeReceiversProvider.GetResourceScopeUID(recv3.UID), + models.ScopeReceiversProvider.GetResourceScopeUID(recv1.UID), + models.ScopeReceiversProvider.GetResourceScopeUID(recv3.UID), }}, existing: allReceivers(), hasAccess: nil, @@ -1196,7 +1196,7 @@ func TestReceiverServiceAC_Update(t *testing.T) { }, { name: "global receivers permissions - update all", - permissions: map[string][]string{accesscontrol.ActionAlertingReceiversUpdate: {ac.ScopeReceiversAll}, accesscontrol.ActionAlertingReceiversRead: {ac.ScopeReceiversAll}}, + permissions: map[string][]string{accesscontrol.ActionAlertingReceiversUpdate: {models.ScopeReceiversAll}, accesscontrol.ActionAlertingReceiversRead: {models.ScopeReceiversAll}}, existing: allReceivers(), hasAccess: allReceivers(), }, @@ -1204,12 +1204,12 @@ func TestReceiverServiceAC_Update(t *testing.T) { name: "single receivers permissions - update some", permissions: map[string][]string{ accesscontrol.ActionAlertingReceiversUpdate: { - ac.ScopeReceiversProvider.GetResourceScopeUID(recv1.UID), - ac.ScopeReceiversProvider.GetResourceScopeUID(recv3.UID), + models.ScopeReceiversProvider.GetResourceScopeUID(recv1.UID), + models.ScopeReceiversProvider.GetResourceScopeUID(recv3.UID), }, accesscontrol.ActionAlertingReceiversRead: { - ac.ScopeReceiversProvider.GetResourceScopeUID(recv1.UID), - ac.ScopeReceiversProvider.GetResourceScopeUID(recv3.UID), + models.ScopeReceiversProvider.GetResourceScopeUID(recv1.UID), + models.ScopeReceiversProvider.GetResourceScopeUID(recv3.UID), }, }, existing: allReceivers(), @@ -1219,12 +1219,12 @@ func TestReceiverServiceAC_Update(t *testing.T) { name: "single receivers mixed read permissions - update some", permissions: map[string][]string{ accesscontrol.ActionAlertingReceiversUpdate: { - ac.ScopeReceiversProvider.GetResourceScopeUID(recv1.UID), - ac.ScopeReceiversProvider.GetResourceScopeUID(recv3.UID), + models.ScopeReceiversProvider.GetResourceScopeUID(recv1.UID), + models.ScopeReceiversProvider.GetResourceScopeUID(recv3.UID), }, accesscontrol.ActionAlertingReceiversRead: { - ac.ScopeReceiversProvider.GetResourceScopeUID(recv2.UID), - ac.ScopeReceiversProvider.GetResourceScopeUID(recv3.UID), + models.ScopeReceiversProvider.GetResourceScopeUID(recv2.UID), + models.ScopeReceiversProvider.GetResourceScopeUID(recv3.UID), }, }, existing: allReceivers(), @@ -1234,8 +1234,8 @@ func TestReceiverServiceAC_Update(t *testing.T) { name: "single receivers mixed global read permissions - update some", permissions: map[string][]string{ accesscontrol.ActionAlertingReceiversUpdate: { - ac.ScopeReceiversProvider.GetResourceScopeUID(recv1.UID), - ac.ScopeReceiversProvider.GetResourceScopeUID(recv3.UID), + models.ScopeReceiversProvider.GetResourceScopeUID(recv1.UID), + models.ScopeReceiversProvider.GetResourceScopeUID(recv3.UID), }, accesscontrol.ActionAlertingNotificationsRead: nil, }, @@ -1327,15 +1327,15 @@ func TestReceiverServiceAC_Delete(t *testing.T) { }, { name: "global receivers permissions - not authorized without read", - permissions: map[string][]string{accesscontrol.ActionAlertingReceiversDelete: {ac.ScopeReceiversAll}}, + permissions: map[string][]string{accesscontrol.ActionAlertingReceiversDelete: {models.ScopeReceiversAll}}, existing: allReceivers(), hasAccess: nil, }, { name: "single receivers permissions - not authorized without read", permissions: map[string][]string{accesscontrol.ActionAlertingReceiversDelete: { - ac.ScopeReceiversProvider.GetResourceScopeUID(recv1.UID), - ac.ScopeReceiversProvider.GetResourceScopeUID(recv3.UID), + models.ScopeReceiversProvider.GetResourceScopeUID(recv1.UID), + models.ScopeReceiversProvider.GetResourceScopeUID(recv3.UID), }}, existing: allReceivers(), hasAccess: nil, @@ -1348,7 +1348,7 @@ func TestReceiverServiceAC_Delete(t *testing.T) { }, { name: "global receivers permissions - delete all", - permissions: map[string][]string{accesscontrol.ActionAlertingReceiversDelete: {ac.ScopeReceiversAll}, accesscontrol.ActionAlertingReceiversRead: {ac.ScopeReceiversAll}}, + permissions: map[string][]string{accesscontrol.ActionAlertingReceiversDelete: {models.ScopeReceiversAll}, accesscontrol.ActionAlertingReceiversRead: {models.ScopeReceiversAll}}, existing: allReceivers(), hasAccess: allReceivers(), }, @@ -1356,12 +1356,12 @@ func TestReceiverServiceAC_Delete(t *testing.T) { name: "single receivers permissions - delete some", permissions: map[string][]string{ accesscontrol.ActionAlertingReceiversDelete: { - ac.ScopeReceiversProvider.GetResourceScopeUID(recv1.UID), - ac.ScopeReceiversProvider.GetResourceScopeUID(recv3.UID), + models.ScopeReceiversProvider.GetResourceScopeUID(recv1.UID), + models.ScopeReceiversProvider.GetResourceScopeUID(recv3.UID), }, accesscontrol.ActionAlertingReceiversRead: { - ac.ScopeReceiversProvider.GetResourceScopeUID(recv1.UID), - ac.ScopeReceiversProvider.GetResourceScopeUID(recv3.UID), + models.ScopeReceiversProvider.GetResourceScopeUID(recv1.UID), + models.ScopeReceiversProvider.GetResourceScopeUID(recv3.UID), }, }, existing: allReceivers(), @@ -1371,12 +1371,12 @@ func TestReceiverServiceAC_Delete(t *testing.T) { name: "single receivers mixed read permissions - delete some", permissions: map[string][]string{ accesscontrol.ActionAlertingReceiversDelete: { - ac.ScopeReceiversProvider.GetResourceScopeUID(recv1.UID), - ac.ScopeReceiversProvider.GetResourceScopeUID(recv3.UID), + models.ScopeReceiversProvider.GetResourceScopeUID(recv1.UID), + models.ScopeReceiversProvider.GetResourceScopeUID(recv3.UID), }, accesscontrol.ActionAlertingReceiversRead: { - ac.ScopeReceiversProvider.GetResourceScopeUID(recv2.UID), - ac.ScopeReceiversProvider.GetResourceScopeUID(recv3.UID), + models.ScopeReceiversProvider.GetResourceScopeUID(recv2.UID), + models.ScopeReceiversProvider.GetResourceScopeUID(recv3.UID), }, }, existing: allReceivers(), @@ -1386,8 +1386,8 @@ func TestReceiverServiceAC_Delete(t *testing.T) { name: "single receivers mixed global read permissions - delete some", permissions: map[string][]string{ accesscontrol.ActionAlertingReceiversDelete: { - ac.ScopeReceiversProvider.GetResourceScopeUID(recv1.UID), - ac.ScopeReceiversProvider.GetResourceScopeUID(recv3.UID), + models.ScopeReceiversProvider.GetResourceScopeUID(recv1.UID), + models.ScopeReceiversProvider.GetResourceScopeUID(recv3.UID), }, accesscontrol.ActionAlertingNotificationsRead: nil, }, diff --git a/pkg/tests/apis/alerting/notifications/receivers/receiver_test.go b/pkg/tests/apis/alerting/notifications/receivers/receiver_test.go index ced6afdb670..0f7b476ecad 100644 --- a/pkg/tests/apis/alerting/notifications/receivers/receiver_test.go +++ b/pkg/tests/apis/alerting/notifications/receivers/receiver_test.go @@ -35,7 +35,6 @@ import ( "github.com/grafana/grafana/pkg/services/accesscontrol/resourcepermissions" "github.com/grafana/grafana/pkg/services/dashboards" "github.com/grafana/grafana/pkg/services/folder/foldertest" - alertingac "github.com/grafana/grafana/pkg/services/ngalert/accesscontrol" "github.com/grafana/grafana/pkg/services/ngalert/api" "github.com/grafana/grafana/pkg/services/ngalert/api/tooling/definitions" ngmodels "github.com/grafana/grafana/pkg/services/ngalert/models" @@ -204,7 +203,7 @@ func TestIntegrationResourcePermissions(t *testing.T) { name: "Admin creates, assigns read, noneUser has no metadata but has access", creatingUser: admin, testUser: noneUser, - assignments: []accesscontrol.SetResourcePermissionCommand{{UserID: mustID(noneUser), Permission: string(alertingac.ReceiverPermissionView)}}, + assignments: []accesscontrol.SetResourcePermissionCommand{{UserID: mustID(noneUser), Permission: string(ngmodels.PermissionView)}}, expACMetadata: nil, expRead: true, }, @@ -212,7 +211,7 @@ func TestIntegrationResourcePermissions(t *testing.T) { name: "Admin creates, assigns write, noneUser has write metadata and access", creatingUser: admin, testUser: noneUser, - assignments: []accesscontrol.SetResourcePermissionCommand{{UserID: mustID(noneUser), Permission: string(alertingac.ReceiverPermissionEdit)}}, + assignments: []accesscontrol.SetResourcePermissionCommand{{UserID: mustID(noneUser), Permission: string(ngmodels.PermissionEdit)}}, expACMetadata: writeACMetadata, expRead: true, }, @@ -220,7 +219,7 @@ func TestIntegrationResourcePermissions(t *testing.T) { name: "Admin creates, assigns admin, noneUser has all metadata and access", creatingUser: admin, testUser: noneUser, - assignments: []accesscontrol.SetResourcePermissionCommand{{UserID: mustID(noneUser), Permission: string(alertingac.ReceiverPermissionAdmin)}}, + assignments: []accesscontrol.SetResourcePermissionCommand{{UserID: mustID(noneUser), Permission: string(ngmodels.PermissionAdmin)}}, expACMetadata: allACMetadata, expRead: true, }, @@ -229,7 +228,7 @@ func TestIntegrationResourcePermissions(t *testing.T) { name: "Admin creates, assigns read to noneUser, creator has no metadata and no access", creatingUser: admin, testUser: creator, - assignments: []accesscontrol.SetResourcePermissionCommand{{UserID: mustID(noneUser), Permission: string(alertingac.ReceiverPermissionView)}}, + assignments: []accesscontrol.SetResourcePermissionCommand{{UserID: mustID(noneUser), Permission: string(ngmodels.PermissionView)}}, expACMetadata: nil, expRead: false, }, @@ -237,7 +236,7 @@ func TestIntegrationResourcePermissions(t *testing.T) { name: "Admin creates, assigns write to noneUser, creator has no metadata and no access", creatingUser: admin, testUser: creator, - assignments: []accesscontrol.SetResourcePermissionCommand{{UserID: mustID(noneUser), Permission: string(alertingac.ReceiverPermissionEdit)}}, + assignments: []accesscontrol.SetResourcePermissionCommand{{UserID: mustID(noneUser), Permission: string(ngmodels.PermissionEdit)}}, expACMetadata: nil, expRead: false, }, @@ -245,7 +244,7 @@ func TestIntegrationResourcePermissions(t *testing.T) { name: "Admin creates, assigns admin to noneUser, creator has no metadata and no access", creatingUser: admin, testUser: creator, - assignments: []accesscontrol.SetResourcePermissionCommand{{UserID: mustID(noneUser), Permission: string(alertingac.ReceiverPermissionAdmin)}}, + assignments: []accesscontrol.SetResourcePermissionCommand{{UserID: mustID(noneUser), Permission: string(ngmodels.PermissionAdmin)}}, expACMetadata: nil, expRead: false, }, @@ -254,7 +253,7 @@ func TestIntegrationResourcePermissions(t *testing.T) { name: "Admin creates, assigns editor, viewer has write metadata and access", creatingUser: admin, testUser: viewer, - assignments: []accesscontrol.SetResourcePermissionCommand{{UserID: mustID(viewer), Permission: string(alertingac.ReceiverPermissionEdit)}}, + assignments: []accesscontrol.SetResourcePermissionCommand{{UserID: mustID(viewer), Permission: string(ngmodels.PermissionEdit)}}, expACMetadata: writeACMetadata, expRead: true, }, @@ -262,7 +261,7 @@ func TestIntegrationResourcePermissions(t *testing.T) { name: "Admin creates, assigns admin, viewer has all metadata and access", creatingUser: admin, testUser: viewer, - assignments: []accesscontrol.SetResourcePermissionCommand{{UserID: mustID(viewer), Permission: string(alertingac.ReceiverPermissionAdmin)}}, + assignments: []accesscontrol.SetResourcePermissionCommand{{UserID: mustID(viewer), Permission: string(ngmodels.PermissionAdmin)}}, expACMetadata: allACMetadata, expRead: true, }, @@ -270,7 +269,7 @@ func TestIntegrationResourcePermissions(t *testing.T) { name: "Admin creates, assigns admin, editor has all metadata and access", creatingUser: admin, testUser: editor, - assignments: []accesscontrol.SetResourcePermissionCommand{{UserID: mustID(editor), Permission: string(alertingac.ReceiverPermissionAdmin)}}, + assignments: []accesscontrol.SetResourcePermissionCommand{{UserID: mustID(editor), Permission: string(ngmodels.PermissionAdmin)}}, expACMetadata: allACMetadata, expRead: true, }, @@ -279,7 +278,7 @@ func TestIntegrationResourcePermissions(t *testing.T) { name: "Admin creates, assigns admin to staff, viewer has no metadata and access", creatingUser: admin, testUser: viewer, - assignments: []accesscontrol.SetResourcePermissionCommand{{TeamID: org1.Staff.ID, Permission: string(alertingac.ReceiverPermissionAdmin)}}, + assignments: []accesscontrol.SetResourcePermissionCommand{{TeamID: org1.Staff.ID, Permission: string(ngmodels.PermissionAdmin)}}, expACMetadata: nil, expRead: true, }, @@ -287,7 +286,7 @@ func TestIntegrationResourcePermissions(t *testing.T) { name: "Admin creates, assigns admin to staff, editor has all metadata and access", creatingUser: admin, testUser: editor, - assignments: []accesscontrol.SetResourcePermissionCommand{{TeamID: org1.Staff.ID, Permission: string(alertingac.ReceiverPermissionAdmin)}}, + assignments: []accesscontrol.SetResourcePermissionCommand{{TeamID: org1.Staff.ID, Permission: string(ngmodels.PermissionAdmin)}}, expACMetadata: allACMetadata, expRead: true, }, From fcdfc865618b785ea37558f90abc52ca5834c0fe Mon Sep 17 00:00:00 2001 From: Tobias Skarhed <1438972+tskarhed@users.noreply.github.com> Date: Wed, 8 Oct 2025 15:01:02 +0200 Subject: [PATCH 012/112] ErrorBoundary: Move boundary type to context (#112167) Move boundary type to context --- .../grafana-ui/src/components/ErrorBoundary/ErrorBoundary.tsx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/grafana-ui/src/components/ErrorBoundary/ErrorBoundary.tsx b/packages/grafana-ui/src/components/ErrorBoundary/ErrorBoundary.tsx index a1bdc9a945f..63044d76d2f 100644 --- a/packages/grafana-ui/src/components/ErrorBoundary/ErrorBoundary.tsx +++ b/packages/grafana-ui/src/components/ErrorBoundary/ErrorBoundary.tsx @@ -44,8 +44,8 @@ export class ErrorBoundary extends PureComponent { this.props.errorLogger(error); } else { faro?.api?.pushError(error, { - type: 'boundary', context: { + type: 'boundary', source: this.props.boundaryName ?? 'unknown', }, }); From ff448240c1af4553df88511fb84a07d3dfcbb40f Mon Sep 17 00:00:00 2001 From: Matheus Macabu Date: Wed, 8 Oct 2025 15:04:36 +0200 Subject: [PATCH 013/112] Makefile: Change build-go to only build the binaries, and deprecate build-go-fast (#111986) --- .bra.toml | 2 +- Makefile | 8 ++++---- scripts/grafana-server/start-server | 2 +- 3 files changed, 6 insertions(+), 6 deletions(-) diff --git a/.bra.toml b/.bra.toml index 6572f9a6afb..5af8e05ebcc 100644 --- a/.bra.toml +++ b/.bra.toml @@ -17,7 +17,7 @@ watch_exts = [".go", ".ini", ".toml", ".template.html"] ignore_files = [".*_gen.go"] build_delay = 1500 cmds = [ - ["make", "GO_BUILD_DEV=1", "build-go-fast"], + ["make", "GO_BUILD_DEV=1", "build-go"], ["make", "gen-jsonnet"], ["./bin/grafana", "server", "-profile", "-profile-addr=127.0.0.1", "-profile-port=6000", "-profile-block-rate=1", "-profile-mutex-rate=5", "-packaging=dev", "cfg:app_mode=development"] ] diff --git a/Makefile b/Makefile index 5a8b35f73b5..da5a7dfb749 100644 --- a/Makefile +++ b/Makefile @@ -233,13 +233,13 @@ update-workspace: gen-go bash scripts/go-workspace/update-workspace.sh .PHONY: build-go -build-go: gen-go update-workspace ## Build all Go binaries. +build-go: ## Build all Go binaries. @echo "build go files with updated workspace" $(GO) run build.go $(GO_BUILD_FLAGS) build -build-go-fast: gen-go ## Build all Go binaries. - @echo "build go files" - $(GO) run build.go $(GO_BUILD_FLAGS) build +.PHONY: build-go-fast +build-go-fast: ## Build all Go binaries without updating workspace. + @echo "!!! [DEPRECATED] use build-go, they do the same thing now. This command will be removed soon" .PHONY: build-backend build-backend: ## Build Grafana backend. diff --git a/scripts/grafana-server/start-server b/scripts/grafana-server/start-server index d9581910b83..7b790a9bb9c 100755 --- a/scripts/grafana-server/start-server +++ b/scripts/grafana-server/start-server @@ -26,7 +26,7 @@ echo starting server # see https://github.com/air-verse/air/issues/525 # if this gets resolved, we could remove the go build and rely on the binary being present as before if [[ ! -f ./bin/"$ARCH"grafana ]]; then - make GO_BUILD_DEV=1 build-go-fast + make GO_BUILD_DEV=1 build-go fi cp -r ./bin $RUNDIR From 121663aa1682df85f86bb0137dfd63e18741417a Mon Sep 17 00:00:00 2001 From: Matt Vella Date: Wed, 8 Oct 2025 08:28:39 -0500 Subject: [PATCH 014/112] Add SolarWinds plugin (#112111) * added enterprise datasource solarwinds * i18n --- .../introduction/grafana-enterprise.md | 1 + .../datasources/state/buildCategories.test.ts | 2 +- .../datasources/state/buildCategories.ts | 10 ++ public/img/plugins/solarWinds.svg | 132 ++++++++++++++++++ public/locales/en-US/grafana.json | 1 + 5 files changed, 145 insertions(+), 1 deletion(-) create mode 100644 public/img/plugins/solarWinds.svg diff --git a/docs/sources/introduction/grafana-enterprise.md b/docs/sources/introduction/grafana-enterprise.md index ef8884f3c55..023aa5d6583 100644 --- a/docs/sources/introduction/grafana-enterprise.md +++ b/docs/sources/introduction/grafana-enterprise.md @@ -107,6 +107,7 @@ With a Grafana Enterprise license, you also get access to premium data sources, - [SAP HANA®](/grafana/plugins/grafana-saphana-datasource) - [ServiceNow](/grafana/plugins/grafana-servicenow-datasource) - [Snowflake](/grafana/plugins/grafana-snowflake-datasource) +- [SolarWinds](/grafana/plugins/grafana-solarwinds-datasource) - [Splunk](/grafana/plugins/grafana-splunk-datasource) - [Splunk Infrastructure monitoring (SignalFx)](/grafana/plugins/grafana-splunk-monitoring-datasource) - [Sqlyze Datasource](/grafana/plugins/grafana-odbc-datasource) diff --git a/public/app/features/datasources/state/buildCategories.test.ts b/public/app/features/datasources/state/buildCategories.test.ts index 81d402ba7d5..3231a029ff7 100644 --- a/public/app/features/datasources/state/buildCategories.test.ts +++ b/public/app/features/datasources/state/buildCategories.test.ts @@ -53,7 +53,7 @@ describe('buildCategories', () => { it('should add enterprise phantom plugins', () => { const enterprisePluginsCategory = categories[3]; expect(enterprisePluginsCategory.title).toBe('Enterprise plugins'); - expect(enterprisePluginsCategory.plugins.length).toBe(30); + expect(enterprisePluginsCategory.plugins.length).toBe(31); expect(enterprisePluginsCategory.plugins[0].name).toBe('Adobe Analytics'); expect(enterprisePluginsCategory.plugins[enterprisePluginsCategory.plugins.length - 1].name).toBe('Zendesk'); }); diff --git a/public/app/features/datasources/state/buildCategories.ts b/public/app/features/datasources/state/buildCategories.ts index ec813a6e1e6..7aeeed6abb4 100644 --- a/public/app/features/datasources/state/buildCategories.ts +++ b/public/app/features/datasources/state/buildCategories.ts @@ -29,6 +29,7 @@ import sapHanaPng from 'img/plugins/sap_hana.png'; import servicenowSvg from 'img/plugins/servicenow.svg'; import signalfxLogoSvg from 'img/plugins/signalfx-logo.svg'; import snowflakeSvg from 'img/plugins/snowflake.svg'; +import solarWindsSvg from 'img/plugins/solarWinds.svg'; import splunkLogo128Png from 'img/plugins/splunk_logo_128.png'; import sumoSvg from 'img/plugins/sumo.svg'; import wavefrontSvg from 'img/plugins/wavefront.svg'; @@ -408,6 +409,15 @@ function getEnterprisePhantomPlugins(): DataSourcePluginMeta[] { name: 'LogicMonitor Devices', imgUrl: logicMonitorSvg, }), + getPhantomPlugin({ + id: 'grafana-solarwinds-datasource', + description: t( + 'datasources.get-enterprise-phantom-plugins.description.solarwinds-datasource', + 'SolarWinds datasource' + ), + name: 'SolarWinds', + imgUrl: solarWindsSvg, + }), ]; } diff --git a/public/img/plugins/solarWinds.svg b/public/img/plugins/solarWinds.svg new file mode 100644 index 00000000000..053918df3bc --- /dev/null +++ b/public/img/plugins/solarWinds.svg @@ -0,0 +1,132 @@ + + + + + + \ No newline at end of file diff --git a/public/locales/en-US/grafana.json b/public/locales/en-US/grafana.json index 0cdda8b0a2a..112a1337dd0 100644 --- a/public/locales/en-US/grafana.json +++ b/public/locales/en-US/grafana.json @@ -6624,6 +6624,7 @@ "service-now-integration-and-data-source": "ServiceNow integration and data source", "signal-fx-integration-and-datasource": "SignalFx integration and datasource", "snowflake-integration-and-data-source": "Snowflake integration and data source", + "solarwinds-datasource": "SolarWinds datasource", "sumo-logic-integration-and-datasource": "SumoLogic integration and datasource", "visualize-and-explore-dynatrace-data": "Visualize and explore Dynatrace data", "visualize-and-explore-oracle-sql": "Visualize and explore Oracle SQL", From def3b9f5a601848baaaaab7836fe43e7a93999f5 Mon Sep 17 00:00:00 2001 From: Kevin Yu Date: Wed, 8 Oct 2025 06:44:20 -0700 Subject: [PATCH 015/112] CloudWatch: Add syntax highlighting and autocomplete for logs diff command (#111207) --- .../completion/CompletionItemProvider.test.ts | 19 +++++++++++++++++- .../logs/completion/CompletionItemProvider.ts | 10 +++++++++- .../logs/completion/statementPosition.test.ts | 20 +++++++++++++++++++ .../logs/completion/statementPosition.ts | 11 ++++++++++ .../logs/completion/suggestionKinds.ts | 3 +++ .../cloudwatch/language/logs/language.ts | 6 ++++-- .../cloudwatch/language/monarch/types.ts | 5 +++++ .../diffModifierQuery.ts | 4 ++++ .../cloudwatch-logs-test-data/diffQuery.ts | 4 ++++ 9 files changed, 78 insertions(+), 4 deletions(-) create mode 100644 public/app/plugins/datasource/cloudwatch/mocks/cloudwatch-logs-test-data/diffModifierQuery.ts create mode 100644 public/app/plugins/datasource/cloudwatch/mocks/cloudwatch-logs-test-data/diffQuery.ts diff --git a/public/app/plugins/datasource/cloudwatch/language/logs/completion/CompletionItemProvider.test.ts b/public/app/plugins/datasource/cloudwatch/language/logs/completion/CompletionItemProvider.test.ts index 052583eb6f2..e586b46ed64 100644 --- a/public/app/plugins/datasource/cloudwatch/language/logs/completion/CompletionItemProvider.test.ts +++ b/public/app/plugins/datasource/cloudwatch/language/logs/completion/CompletionItemProvider.test.ts @@ -4,6 +4,8 @@ import { CustomVariableModel } from '@grafana/data'; import { monacoTypes } from '@grafana/ui'; import { setupMockedTemplateService, logGroupNamesVariable } from '../../../mocks/CloudWatchDataSource'; +import { logsTestDataDiffModifierQuery } from '../../../mocks/cloudwatch-logs-test-data/diffModifierQuery'; +import { logsTestDataDiffQuery } from '../../../mocks/cloudwatch-logs-test-data/diffQuery'; import { logsTestDataEmptyQuery } from '../../../mocks/cloudwatch-logs-test-data/empty'; import { logsTestDataFilterQuery } from '../../../mocks/cloudwatch-logs-test-data/filterQuery'; import { logsTestDataNewCommandQuery } from '../../../mocks/cloudwatch-logs-test-data/newCommandQuery'; @@ -12,7 +14,7 @@ import { ResourcesAPI } from '../../../resources/ResourcesAPI'; import { ResourceResponse } from '../../../resources/types'; import { LogGroup, LogGroupField } from '../../../types'; import cloudWatchLogsLanguageDefinition, { CLOUDWATCH_LOGS_LANGUAGE_DEFINITION_ID } from '../definition'; -import { LOGS_COMMANDS, LOGS_FUNCTION_OPERATORS, SORT_DIRECTION_KEYWORDS, language } from '../language'; +import { DIFF_MODIFIERS, LOGS_COMMANDS, LOGS_FUNCTION_OPERATORS, SORT_DIRECTION_KEYWORDS, language } from '../language'; import { LogsCompletionItemProvider } from './CompletionItemProvider'; @@ -81,6 +83,21 @@ describe('LogsCompletionItemProvider', () => { expect(suggestionLabels).toEqual(expect.arrayContaining(LOGS_FUNCTION_OPERATORS)); }); + it('returns diff modifiers after the diff keyword', async () => { + const suggestions = await getSuggestions(logsTestDataDiffQuery.query, logsTestDataDiffQuery.position); + const suggestionLabels = suggestions.map((s) => s.label); + expect(suggestionLabels).toEqual(expect.arrayContaining(DIFF_MODIFIERS)); + }); + + it('returns diff modifiers when partially typed', async () => { + const suggestions = await getSuggestions( + logsTestDataDiffModifierQuery.query, + logsTestDataDiffModifierQuery.position + ); + const suggestionLabels = suggestions.map((s) => s.label); + expect(suggestionLabels).toEqual(expect.arrayContaining(DIFF_MODIFIERS)); + }); + it('returns `in []` snippet for the `in` keyword', async () => { const suggestions = await getSuggestions(logsTestDataFilterQuery.query, logsTestDataFilterQuery.position); const suggestionLabels = suggestions.map((s) => s.label); diff --git a/public/app/plugins/datasource/cloudwatch/language/logs/completion/CompletionItemProvider.ts b/public/app/plugins/datasource/cloudwatch/language/logs/completion/CompletionItemProvider.ts index d37615e2713..2d8a62920cb 100644 --- a/public/app/plugins/datasource/cloudwatch/language/logs/completion/CompletionItemProvider.ts +++ b/public/app/plugins/datasource/cloudwatch/language/logs/completion/CompletionItemProvider.ts @@ -8,7 +8,7 @@ import { LinkedToken } from '../../monarch/LinkedToken'; import { TRIGGER_SUGGEST } from '../../monarch/commands'; import { CompletionItem, CompletionItemPriority, StatementPosition, SuggestionKind } from '../../monarch/types'; import { fetchLogGroupFields } from '../../utils'; -import { LOGS_COMMANDS, LOGS_FUNCTION_OPERATORS, SORT_DIRECTION_KEYWORDS } from '../language'; +import { LOGS_COMMANDS, LOGS_FUNCTION_OPERATORS, SORT_DIRECTION_KEYWORDS, DIFF_MODIFIERS } from '../language'; import { getStatementPosition } from './statementPosition'; import { getSuggestionKinds } from './suggestionKinds'; @@ -115,6 +115,14 @@ export class LogsCompletionItemProvider extends CompletionItemProvider { }); }); break; + case SuggestionKind.DiffModifier: + DIFF_MODIFIERS.forEach((modifier) => { + addSuggestion(modifier, { + sortText: CompletionItemPriority.High, + kind: monaco.languages.CompletionItemKind.Keyword, + }); + }); + break; case SuggestionKind.InKeyword: addSuggestion('in []', { insertText: 'in ["$0"]', diff --git a/public/app/plugins/datasource/cloudwatch/language/logs/completion/statementPosition.test.ts b/public/app/plugins/datasource/cloudwatch/language/logs/completion/statementPosition.test.ts index 3996e81832e..a9291592e37 100644 --- a/public/app/plugins/datasource/cloudwatch/language/logs/completion/statementPosition.test.ts +++ b/public/app/plugins/datasource/cloudwatch/language/logs/completion/statementPosition.test.ts @@ -3,6 +3,8 @@ import * as monaco from 'monaco-editor/esm/vs/editor/editor.api'; import { monacoTypes } from '@grafana/ui'; import { logsTestDataCommentOnlyQuery } from '../../../mocks/cloudwatch-logs-test-data/commentOnlyQuery'; +import { logsTestDataDiffModifierQuery } from '../../../mocks/cloudwatch-logs-test-data/diffModifierQuery'; +import { logsTestDataDiffQuery } from '../../../mocks/cloudwatch-logs-test-data/diffQuery'; import { logsTestDataEmptyQuery } from '../../../mocks/cloudwatch-logs-test-data/empty'; import { logsTestDataMultiLineFullQuery } from '../../../mocks/cloudwatch-logs-test-data/multiLineFullQuery'; import { logsTestDataSingleLineFullQuery } from '../../../mocks/cloudwatch-logs-test-data/singleLineFullQuery'; @@ -164,4 +166,22 @@ describe('getStatementPosition', () => { getStatementPosition(generateToken(logsTestDataMultiLineFullQuery.query, { lineNumber: 5, column: 3 })) ).toEqual(StatementPosition.Comment); }); + + it('should return StatementPosition.DiffKeyword inside the `diff` keyword', () => { + expect(getStatementPosition(generateToken(logsTestDataDiffQuery.query, { lineNumber: 1, column: 22 }))).toEqual( + StatementPosition.DiffKeyword + ); + }); + + it('should return StatementPosition.AfterDiffKeyword after the `diff` keyword', () => { + expect(getStatementPosition(generateToken(logsTestDataDiffQuery.query, { lineNumber: 1, column: 25 }))).toEqual( + StatementPosition.AfterDiffKeyword + ); + }); + + it('should return StatementPosition.DiffModifierArg when typing a diff modifier', () => { + expect( + getStatementPosition(generateToken(logsTestDataDiffModifierQuery.query, { lineNumber: 1, column: 28 })) + ).toEqual(StatementPosition.DiffModifierArg); + }); }); diff --git a/public/app/plugins/datasource/cloudwatch/language/logs/completion/statementPosition.ts b/public/app/plugins/datasource/cloudwatch/language/logs/completion/statementPosition.ts index 01724bdf72b..3d3deea543c 100644 --- a/public/app/plugins/datasource/cloudwatch/language/logs/completion/statementPosition.ts +++ b/public/app/plugins/datasource/cloudwatch/language/logs/completion/statementPosition.ts @@ -9,6 +9,7 @@ import { LIMIT, PARSE, DEDUP, + DIFF, LOGS_COMMANDS, LOGS_FUNCTION_OPERATORS, LOGS_LOGIC_OPERATORS, @@ -66,6 +67,8 @@ export const getStatementPosition = (currentToken: LinkedToken | null): Statemen switch (normalizedCurrentToken) { case DEDUP: return StatementPosition.DedupKeyword; + case DIFF: + return StatementPosition.DiffKeyword; case DISPLAY: return StatementPosition.DisplayKeyword; case FIELDS: @@ -95,6 +98,8 @@ export const getStatementPosition = (currentToken: LinkedToken | null): Statemen switch (normalizedPreviousNonWhiteSpace) { case DEDUP: return StatementPosition.AfterDedupKeyword; + case DIFF: + return StatementPosition.AfterDiffKeyword; case DISPLAY: return StatementPosition.AfterDisplayKeyword; case FIELDS: @@ -171,6 +176,9 @@ export const getStatementPosition = (currentToken: LinkedToken | null): Statemen if (nearestKeyword.value === FILTER) { return StatementPosition.FilterArg; } + if (nearestKeyword.value === DIFF) { + return StatementPosition.DiffModifierArg; + } return StatementPosition.CommandArg; } @@ -189,6 +197,9 @@ export const getStatementPosition = (currentToken: LinkedToken | null): Statemen if (nearestKeyword.value === FILTER) { return StatementPosition.FilterArg; } + if (nearestKeyword.value === DIFF) { + return StatementPosition.DiffModifierArg; + } return StatementPosition.CommandArg; } diff --git a/public/app/plugins/datasource/cloudwatch/language/logs/completion/suggestionKinds.ts b/public/app/plugins/datasource/cloudwatch/language/logs/completion/suggestionKinds.ts index f311a5769eb..a98e5a70903 100644 --- a/public/app/plugins/datasource/cloudwatch/language/logs/completion/suggestionKinds.ts +++ b/public/app/plugins/datasource/cloudwatch/language/logs/completion/suggestionKinds.ts @@ -7,6 +7,9 @@ export function getSuggestionKinds(statementPosition: StatementPosition): Sugges case StatementPosition.AfterSortKeyword: case StatementPosition.SortArg: return [SuggestionKind.SortOrderDirectionKeyword, SuggestionKind.Function]; + case StatementPosition.AfterDiffKeyword: + case StatementPosition.DiffModifierArg: + return [SuggestionKind.DiffModifier]; case StatementPosition.AfterDisplayKeyword: case StatementPosition.AfterFieldsKeyword: case StatementPosition.AfterFilterKeyword: diff --git a/public/app/plugins/datasource/cloudwatch/language/logs/language.ts b/public/app/plugins/datasource/cloudwatch/language/logs/language.ts index c994d328e42..f70d844aaf7 100644 --- a/public/app/plugins/datasource/cloudwatch/language/logs/language.ts +++ b/public/app/plugins/datasource/cloudwatch/language/logs/language.ts @@ -7,6 +7,7 @@ interface CloudWatchLogsLanguage extends monacoType.languages.IMonarchLanguage { builtinFunctions: string[]; } +export const DIFF = 'diff'; export const DISPLAY = 'display'; export const FIELDS = 'fields'; export const FILTER = 'filter'; @@ -16,7 +17,7 @@ export const SORT = 'sort'; export const LIMIT = 'limit'; export const PARSE = 'parse'; export const DEDUP = 'dedup'; -export const LOGS_COMMANDS = [DISPLAY, FIELDS, FILTER, PATTERN, STATS, SORT, LIMIT, PARSE, DEDUP]; +export const LOGS_COMMANDS = [DISPLAY, FIELDS, FILTER, PATTERN, STATS, SORT, LIMIT, PARSE, DEDUP, DIFF]; export const LOGS_LOGIC_OPERATORS = ['and', 'or', 'not']; @@ -77,7 +78,8 @@ export const LOGS_FUNCTION_OPERATORS = [ ]; export const SORT_DIRECTION_KEYWORDS = ['asc', 'desc']; -export const LOGS_KEYWORDS = ['like', 'by', 'in', 'as', ...SORT_DIRECTION_KEYWORDS]; +export const DIFF_MODIFIERS = ['previousDay', 'previousWeek', 'previousMonth']; +export const LOGS_KEYWORDS = ['like', 'by', 'in', 'as', ...SORT_DIRECTION_KEYWORDS, ...DIFF_MODIFIERS]; export const language: CloudWatchLogsLanguage = { defaultToken: 'invalid', diff --git a/public/app/plugins/datasource/cloudwatch/language/monarch/types.ts b/public/app/plugins/datasource/cloudwatch/language/monarch/types.ts index 475153d4a5f..9fa32cd3d6c 100644 --- a/public/app/plugins/datasource/cloudwatch/language/monarch/types.ts +++ b/public/app/plugins/datasource/cloudwatch/language/monarch/types.ts @@ -103,6 +103,10 @@ export enum StatementPosition { LikeKeyword, AfterLikeKeyword, + DiffKeyword, + AfterDiffKeyword, + DiffModifierArg, + Function, FunctionArg, CommandArg, @@ -171,6 +175,7 @@ export enum SuggestionKind { Command, Function, InKeyword, + DiffModifier, // PPL BooleanFunction, diff --git a/public/app/plugins/datasource/cloudwatch/mocks/cloudwatch-logs-test-data/diffModifierQuery.ts b/public/app/plugins/datasource/cloudwatch/mocks/cloudwatch-logs-test-data/diffModifierQuery.ts new file mode 100644 index 00000000000..6e1b21e0f84 --- /dev/null +++ b/public/app/plugins/datasource/cloudwatch/mocks/cloudwatch-logs-test-data/diffModifierQuery.ts @@ -0,0 +1,4 @@ +export const logsTestDataDiffModifierQuery = { + query: `pattern @message | diff previous`, + position: { lineNumber: 1, column: 32 }, +}; diff --git a/public/app/plugins/datasource/cloudwatch/mocks/cloudwatch-logs-test-data/diffQuery.ts b/public/app/plugins/datasource/cloudwatch/mocks/cloudwatch-logs-test-data/diffQuery.ts new file mode 100644 index 00000000000..ccf23d98f4e --- /dev/null +++ b/public/app/plugins/datasource/cloudwatch/mocks/cloudwatch-logs-test-data/diffQuery.ts @@ -0,0 +1,4 @@ +export const logsTestDataDiffQuery = { + query: `pattern @message | diff `, + position: { lineNumber: 1, column: 24 }, +}; From 891f6a58bf6504692a9890456e149f8c163c6363 Mon Sep 17 00:00:00 2001 From: Ashley Harrison Date: Wed, 8 Oct 2025 14:51:33 +0100 Subject: [PATCH 016/112] Palette: use `semi-dark-yellow` for more contrast (#112090) * use semi-dark-yellow for more contrast * fix unit tests --- packages/grafana-data/src/field/fieldColor.test.ts | 2 +- packages/grafana-data/src/field/fieldDisplay.test.ts | 4 ++-- packages/grafana-data/src/themes/createVisualizationColors.ts | 2 +- 3 files changed, 4 insertions(+), 4 deletions(-) diff --git a/packages/grafana-data/src/field/fieldColor.test.ts b/packages/grafana-data/src/field/fieldColor.test.ts index bcfa9648df4..3463549c3c4 100644 --- a/packages/grafana-data/src/field/fieldColor.test.ts +++ b/packages/grafana-data/src/field/fieldColor.test.ts @@ -47,7 +47,7 @@ describe('fieldColorModeRegistry', () => { it('Palette classic with series index 1', () => { const calcFn = getCalculator({ mode: FieldColorModeId.PaletteClassic, seriesIndex: 1, name: 'series2' }); - expect(calcFn(70, 0, undefined)).toEqual('#FADE2A'); + expect(calcFn(70, 0, undefined)).toEqual('#F2CC0C'); }); it('Palette uses name', () => { diff --git a/packages/grafana-data/src/field/fieldDisplay.test.ts b/packages/grafana-data/src/field/fieldDisplay.test.ts index d4419310bad..718c0e54430 100644 --- a/packages/grafana-data/src/field/fieldDisplay.test.ts +++ b/packages/grafana-data/src/field/fieldDisplay.test.ts @@ -388,7 +388,7 @@ describe('FieldDisplay', () => { const result = getFieldDisplayValues(options); expect(result[0].display.color).toEqual('#73BF69'); - expect(result[1].display.color).toEqual('#FADE2A'); + expect(result[1].display.color).toEqual('#F2CC0C'); }); it('When showing all values lookup color via an override', () => { @@ -435,7 +435,7 @@ describe('FieldDisplay', () => { const result = getFieldDisplayValues(options); expect(result[0].display.color).toEqual('#AAA'); - expect(result[1].display.color).toEqual('#FADE2A'); + expect(result[1].display.color).toEqual('#F2CC0C'); }); it('Multiple other string fields', () => { diff --git a/packages/grafana-data/src/themes/createVisualizationColors.ts b/packages/grafana-data/src/themes/createVisualizationColors.ts index 80238d2a655..fca963c9c07 100644 --- a/packages/grafana-data/src/themes/createVisualizationColors.ts +++ b/packages/grafana-data/src/themes/createVisualizationColors.ts @@ -261,7 +261,7 @@ function getClassicPalette() { return [ 'green', - 'yellow', + 'semi-dark-yellow', 'blue', 'orange', 'red', From 2f7d32ab556473a4f47777cc692e976aef8a9671 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Irene=20Rodr=C3=ADguez?= Date: Wed, 8 Oct 2025 17:04:08 +0200 Subject: [PATCH 017/112] Docs: Clarify RBAC availability in documentation (#112172) --- .../access-control/rbac-for-app-plugins/index.md | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/docs/sources/administration/roles-and-permissions/access-control/rbac-for-app-plugins/index.md b/docs/sources/administration/roles-and-permissions/access-control/rbac-for-app-plugins/index.md index 7ffb3c3d26a..de442b3d5d6 100644 --- a/docs/sources/administration/roles-and-permissions/access-control/rbac-for-app-plugins/index.md +++ b/docs/sources/administration/roles-and-permissions/access-control/rbac-for-app-plugins/index.md @@ -5,6 +5,7 @@ description: Learn about how to configure access to app plugins using RBAC labels: products: - cloud + - enterprise menuTitle: RBAC for app plugins title: RBAC for app plugins weight: 90 @@ -39,7 +40,7 @@ refs: # RBAC for app plugins {{< admonition type="note" >}} -Available in [Grafana Cloud](/docs/grafana-cloud). +Available in [Grafana Enterprise](https://grafana.com/docs/grafana//introduction/grafana-enterprise/) and [Grafana Cloud](/docs/grafana-cloud). {{< /admonition >}} RBAC can be used to manage access to [app plugins](https://grafana.com/docs/grafana/latest/administration/plugin-management/#app-plugins). From 3f40947cd6ff0e1a13e4bacdc0d036b6b07bd496 Mon Sep 17 00:00:00 2001 From: Mihai Doarna Date: Wed, 8 Oct 2025 18:04:24 +0300 Subject: [PATCH 018/112] IAM: Add a local cache for the SSO settings service (#112152) * add a local cache for the SSO settings service * add unit tests for GetForProviderFromCache() func * append sso settings on update if not found --- pkg/services/ssosettings/ssosettings.go | 2 + .../ssosettings/ssosettingsimpl/service.go | 53 ++++ .../ssosettingsimpl/service_test.go | 267 ++++++++++++++++++ .../ssosettingstests/service_fake.go | 8 + .../ssosettingstests/service_mock.go | 30 ++ 5 files changed, 360 insertions(+) diff --git a/pkg/services/ssosettings/ssosettings.go b/pkg/services/ssosettings/ssosettings.go index 96aec5daad5..a27c2cc3ec8 100644 --- a/pkg/services/ssosettings/ssosettings.go +++ b/pkg/services/ssosettings/ssosettings.go @@ -22,6 +22,8 @@ type Service interface { ListWithRedactedSecrets(ctx context.Context) ([]*models.SSOSettings, error) // GetForProvider returns the SSO settings for a given provider (DB or config file) GetForProvider(ctx context.Context, provider string) (*models.SSOSettings, error) + // GetForProviderFromCache returns the SSO settings for a given provider from cache. It falls back to GetForProvider if the settings are not in the cache. + GetForProviderFromCache(ctx context.Context, provider string) (*models.SSOSettings, error) // GetForProviderWithRedactedSecrets returns the SSO settings for a given provider (DB or config file) with secret values redacted GetForProviderWithRedactedSecrets(ctx context.Context, provider string) (*models.SSOSettings, error) // Upsert creates or updates the SSO settings for a given provider diff --git a/pkg/services/ssosettings/ssosettingsimpl/service.go b/pkg/services/ssosettings/ssosettingsimpl/service.go index fd09a1481ff..254a2a3059a 100644 --- a/pkg/services/ssosettings/ssosettingsimpl/service.go +++ b/pkg/services/ssosettings/ssosettingsimpl/service.go @@ -5,7 +5,9 @@ import ( "encoding/base64" "errors" "fmt" + "slices" "strings" + "sync" "time" "github.com/prometheus/client_golang/prometheus" @@ -43,6 +45,8 @@ type Service struct { providersList []string configurableProviders map[string]bool reloadables map[string]ssosettings.Reloadable + cachedSSOSettings []*models.SSOSettings + cacheMutex sync.RWMutex } func ProvideService(cfg *setting.Cfg, sqlStore db.DB, ac ac.AccessControl, @@ -86,6 +90,7 @@ func ProvideService(cfg *setting.Cfg, sqlStore db.DB, ac ac.AccessControl, configurableProviders: configurableProviders, reloadables: make(map[string]ssosettings.Reloadable), settingsProvider: settingsProvider, + cachedSSOSettings: make([]*models.SSOSettings, 0), } usageStats.RegisterMetricsFunc(svc.getUsageStats) @@ -120,6 +125,28 @@ func (s *Service) GetForProvider(ctx context.Context, provider string) (*models. return s.mergeSSOSettings(dbSettings, systemSettings), nil } +func (s *Service) GetForProviderFromCache(ctx context.Context, provider string) (*models.SSOSettings, error) { + s.cacheMutex.RLock() + defer s.cacheMutex.RUnlock() + + for _, setting := range s.cachedSSOSettings { + if setting.Provider == provider { + return &models.SSOSettings{ + Provider: setting.Provider, + Source: setting.Source, + Settings: deepCopyMap(setting.Settings), + }, nil + } + } + + // If settings are not in the cache, we return them from the database if the provider is valid + if slices.Contains(s.providersList, provider) { + return s.GetForProvider(ctx, provider) + } + + return nil, nil +} + func (s *Service) GetForProviderWithRedactedSecrets(ctx context.Context, provider string) (*models.SSOSettings, error) { if !s.isProviderConfigurable(provider) { return nil, ssosettings.ErrNotConfigurable @@ -160,6 +187,8 @@ func (s *Service) List(ctx context.Context) ([]*models.SSOSettings, error) { result = append(result, s.mergeSSOSettings(dbSettings, fallbackSettings)) } + s.setCachedSSOSettings(result) + return result, nil } @@ -272,6 +301,8 @@ func (s *Service) Delete(ctx context.Context, provider string) error { } func (s *Service) reload(reloadable ssosettings.Reloadable, provider string, currentSettings models.SSOSettings) { + s.updateCachedSSOSettings(provider, ¤tSettings) + err := reloadable.Reload(context.Background(), currentSettings) if err != nil { s.metrics.reloadFailures.WithLabelValues(provider).Inc() @@ -629,3 +660,25 @@ func deepCopySlice(s []any) []any { return newSlice } + +func (s *Service) setCachedSSOSettings(settings []*models.SSOSettings) { + s.cacheMutex.Lock() + defer s.cacheMutex.Unlock() + + s.cachedSSOSettings = settings +} + +func (s *Service) updateCachedSSOSettings(provider string, settings *models.SSOSettings) { + s.cacheMutex.Lock() + defer s.cacheMutex.Unlock() + + for i := range s.cachedSSOSettings { + if s.cachedSSOSettings[i].Provider == provider { + s.cachedSSOSettings[i] = settings + return + } + } + + // Provider not found, append new settings + s.cachedSSOSettings = append(s.cachedSSOSettings, settings) +} diff --git a/pkg/services/ssosettings/ssosettingsimpl/service_test.go b/pkg/services/ssosettings/ssosettingsimpl/service_test.go index 781ba148ea0..d803b14fd6d 100644 --- a/pkg/services/ssosettings/ssosettingsimpl/service_test.go +++ b/pkg/services/ssosettings/ssosettingsimpl/service_test.go @@ -367,6 +367,273 @@ func TestService_GetForProvider(t *testing.T) { } } +func TestService_GetForProviderFromCache(t *testing.T) { + t.Parallel() + + testCases := []struct { + name string + provider string + setup func(env testEnv) + want *models.SSOSettings + wantErr bool + }{ + { + name: "should return successfully from cache", + provider: "github", + setup: func(env testEnv) { + env.service.cachedSSOSettings = []*models.SSOSettings{ + { + Provider: "github", + Settings: map[string]any{ + "enabled": true, + "client_id": "client_id", + "client_secret": "secret", + }, + Source: models.DB, + }, + } + }, + want: &models.SSOSettings{ + Provider: "github", + Settings: map[string]any{ + "enabled": true, + "client_id": "client_id", + "client_secret": "secret", + }, + }, + wantErr: false, + }, + { + name: "should return successfully from database if not in cache", + provider: "github", + setup: func(env testEnv) { + env.service.cachedSSOSettings = []*models.SSOSettings{ + { + Provider: "google", + Settings: map[string]any{"enabled": true}, + Source: models.DB, + }, + } + env.store.ExpectedSSOSetting = &models.SSOSettings{ + Provider: "github", + Settings: map[string]any{ + "enabled": true, + "client_id": "client_id", + "client_secret": base64.RawStdEncoding.EncodeToString([]byte("client_secret")), + }, + Source: models.DB, + } + env.secrets.On("Decrypt", mock.Anything, []byte("client_secret"), mock.Anything).Return([]byte("decrypted-client-secret"), nil).Once() + }, + want: &models.SSOSettings{ + Provider: "github", + Settings: map[string]any{ + "enabled": true, + "client_id": "client_id", + "client_secret": "decrypted-client-secret", + }, + Source: models.DB, + }, + wantErr: false, + }, + { + name: "should return nil if provider is not valid", + provider: "invalid", + setup: func(env testEnv) { + env.service.cachedSSOSettings = []*models.SSOSettings{ + { + Provider: "github", + Settings: map[string]any{"enabled": true}, + Source: models.DB, + }, + } + }, + want: nil, + wantErr: false, + }, + { + name: "should return error if store returns an error", + provider: "github", + setup: func(env testEnv) { + env.store.ExpectedError = fmt.Errorf("error") + }, + want: nil, + wantErr: true, + }, + } + + for _, tc := range testCases { + // create a local copy of "tc" to allow concurrent access within tests to the different items of testCases, + // otherwise it would be like a moving pointer while tests run in parallel + tc := tc + + t.Run(tc.name, func(t *testing.T) { + t.Parallel() + + env := setupTestEnv(t, true, false, true) + if tc.setup != nil { + tc.setup(env) + } + + actual, err := env.service.GetForProviderFromCache(context.Background(), tc.provider) + + if tc.wantErr { + require.Error(t, err) + return + } + + require.NoError(t, err) + require.Equal(t, tc.want, actual) + + env.secrets.AssertExpectations(t) + }) + } + + t.Run("should return settings from cache after calling List", func(t *testing.T) { + t.Parallel() + + env := setupTestEnv(t, true, false, true) + env.store.ExpectedSSOSettings = []*models.SSOSettings{ + { + Provider: "github", + Settings: map[string]any{ + "enabled": true, + "client_id": "github_client_id", + "client_secret": base64.RawStdEncoding.EncodeToString([]byte("client_secret")), + }, + Source: models.DB, + }, + { + Provider: "okta", + Settings: map[string]any{ + "enabled": false, + "client_id": "okta_client_id", + "other_secret": base64.RawStdEncoding.EncodeToString([]byte("other_secret")), + }, + Source: models.DB, + }, + } + env.secrets.On("Decrypt", mock.Anything, []byte("client_secret"), mock.Anything).Return([]byte("decrypted-client-secret"), nil).Once() + env.secrets.On("Decrypt", mock.Anything, []byte("other_secret"), mock.Anything).Return([]byte("decrypted-other-secret"), nil).Once() + + _, err := env.service.List(context.Background()) + require.NoError(t, err) + + actual, err := env.service.GetForProviderFromCache(context.Background(), "github") + require.NoError(t, err) + require.Equal(t, "github", actual.Provider) + require.Equal(t, map[string]any{ + "enabled": true, + "client_id": "github_client_id", + "client_secret": "decrypted-client-secret", + }, actual.Settings) + require.Equal(t, env.store.ExpectedSSOSettings[0].Source, actual.Source) + + actual, err = env.service.GetForProviderFromCache(context.Background(), "okta") + require.NoError(t, err) + require.Equal(t, "okta", actual.Provider) + require.Equal(t, map[string]any{ + "enabled": false, + "client_id": "okta_client_id", + "other_secret": "decrypted-other-secret", + }, actual.Settings) + require.Equal(t, env.store.ExpectedSSOSettings[1].Source, actual.Source) + }) + + testCasesUpsert := []struct { + name string + provider string + settings []*models.SSOSettings + }{ + { + name: "should return settings from cache after upsert if provider is already in cache", + provider: "azuread", + settings: []*models.SSOSettings{ + { + Provider: "azuread", + Settings: map[string]any{"enabled": true}, + Source: models.DB, + }, + }, + }, + { + name: "should return settings from cache after upsert if provider is not in cache", + provider: "github", + settings: []*models.SSOSettings{}, + }, + } + for _, tc := range testCasesUpsert { + // create a local copy of "tc" to allow concurrent access within tests to the different items of testCases, + // otherwise it would be like a moving pointer while tests run in parallel + tc := tc + + t.Run(tc.name, func(t *testing.T) { + t.Parallel() + + env := setupTestEnv(t, false, false, false) + + env.service.cachedSSOSettings = tc.settings + + settings := models.SSOSettings{ + Provider: tc.provider, + Settings: map[string]any{ + "client_id": "client-id", + "client_secret": "client-secret", + "enabled": true, + }, + } + + var wg sync.WaitGroup + wg.Add(1) + + reloadable := ssosettingstests.NewMockReloadable(t) + reloadable.On("Validate", mock.Anything, settings, mock.Anything, mock.Anything).Return(nil) + reloadable.On("Reload", mock.Anything, mock.MatchedBy(func(settings models.SSOSettings) bool { + defer wg.Done() + return true + })).Return(nil).Maybe() + env.reloadables[tc.provider] = reloadable + + env.secrets.On("Encrypt", mock.Anything, []byte("client-secret"), mock.Anything).Return([]byte("encrypted-client-secret"), nil).Once() + env.secrets.On("Decrypt", mock.Anything, []byte("encrypted-current-client-secret"), mock.Anything).Return([]byte("current-client-secret"), nil).Once() + + env.store.UpsertFn = func(ctx context.Context, settings *models.SSOSettings) error { + currentTime := time.Now() + settings.ID = "someid" + settings.Created = currentTime + settings.Updated = currentTime + + env.store.ActualSSOSettings = *settings + return nil + } + + env.store.GetFn = func(ctx context.Context, provider string) (*models.SSOSettings, error) { + return &models.SSOSettings{ + ID: "someid", + Provider: provider, + Settings: map[string]any{ + "client_secret": base64.RawStdEncoding.EncodeToString([]byte("encrypted-current-client-secret")), + }, + }, nil + } + + err := env.service.Upsert(context.Background(), &settings, &user.SignedInUser{}) + require.NoError(t, err) + + wg.Wait() + + actual, err := env.service.GetForProviderFromCache(context.Background(), tc.provider) + require.NoError(t, err) + require.Equal(t, tc.provider, actual.Provider) + require.Equal(t, map[string]any{ + "enabled": true, + "client_id": "client-id", + "client_secret": "client-secret", + }, actual.Settings) + }) + } +} + func TestService_GetForProviderWithRedactedSecrets(t *testing.T) { t.Parallel() diff --git a/pkg/services/ssosettings/ssosettingstests/service_fake.go b/pkg/services/ssosettings/ssosettingstests/service_fake.go index f629d25a296..87debf227bf 100644 --- a/pkg/services/ssosettings/ssosettingstests/service_fake.go +++ b/pkg/services/ssosettings/ssosettingstests/service_fake.go @@ -60,6 +60,14 @@ func (f *FakeService) GetForProvider(ctx context.Context, provider string) (*mod return f.ExpectedSSOSetting, f.ExpectedError } +func (f *FakeService) GetForProviderFromCache(ctx context.Context, provider string) (*models.SSOSettings, error) { + if f.GetForProviderFn != nil { + return f.GetForProviderFn(ctx, provider) + } + f.ActualProvider = provider + return f.ExpectedSSOSetting, f.ExpectedError +} + func (f *FakeService) GetForProviderWithRedactedSecrets(ctx context.Context, provider string) (*models.SSOSettings, error) { if f.GetForProviderWithRedactedSecretsFn != nil { return f.GetForProviderWithRedactedSecretsFn(ctx, provider) diff --git a/pkg/services/ssosettings/ssosettingstests/service_mock.go b/pkg/services/ssosettings/ssosettingstests/service_mock.go index 528e3e5a059..4c7aa593870 100644 --- a/pkg/services/ssosettings/ssosettingstests/service_mock.go +++ b/pkg/services/ssosettings/ssosettingstests/service_mock.go @@ -66,6 +66,36 @@ func (_m *MockService) GetForProvider(ctx context.Context, provider string) (*mo return r0, r1 } +// GetForProviderFromCache provides a mock function with given fields: ctx, provider +func (_m *MockService) GetForProviderFromCache(ctx context.Context, provider string) (*models.SSOSettings, error) { + ret := _m.Called(ctx, provider) + + if len(ret) == 0 { + panic("no return value specified for GetForProviderFromCache") + } + + var r0 *models.SSOSettings + var r1 error + if rf, ok := ret.Get(0).(func(context.Context, string) (*models.SSOSettings, error)); ok { + return rf(ctx, provider) + } + if rf, ok := ret.Get(0).(func(context.Context, string) *models.SSOSettings); ok { + r0 = rf(ctx, provider) + } else { + if ret.Get(0) != nil { + r0 = ret.Get(0).(*models.SSOSettings) + } + } + + if rf, ok := ret.Get(1).(func(context.Context, string) error); ok { + r1 = rf(ctx, provider) + } else { + r1 = ret.Error(1) + } + + return r0, r1 +} + // GetForProviderWithRedactedSecrets provides a mock function with given fields: ctx, provider func (_m *MockService) GetForProviderWithRedactedSecrets(ctx context.Context, provider string) (*models.SSOSettings, error) { ret := _m.Called(ctx, provider) From 3c6768460c1bed4eeed5aace9ef3699f9d47679f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Peter=20=C5=A0tibran=C3=BD?= Date: Wed, 8 Oct 2025 17:04:38 +0200 Subject: [PATCH 019/112] Remove unused index_max_batch_size option to bleve backend. (#112171) * Remove unused index_max_batch_size option to bleve backend. * Remove batchSize argument. --- pkg/setting/setting.go | 1 - pkg/setting/setting_unified_storage.go | 1 - pkg/storage/unified/search/bleve.go | 4 --- .../unified/search/bleve_performance_test.go | 2 +- .../unified/search/bleve_search_test.go | 27 +++++++++---------- pkg/storage/unified/search/options.go | 1 - 6 files changed, 14 insertions(+), 22 deletions(-) diff --git a/pkg/setting/setting.go b/pkg/setting/setting.go index 89a42e9d56d..49d9dc9511b 100644 --- a/pkg/setting/setting.go +++ b/pkg/setting/setting.go @@ -577,7 +577,6 @@ type Cfg struct { IndexPath string IndexWorkers int IndexRebuildWorkers int - IndexMaxBatchSize int IndexFileThreshold int IndexMinCount int IndexRebuildInterval time.Duration diff --git a/pkg/setting/setting_unified_storage.go b/pkg/setting/setting_unified_storage.go index 914d5f789d7..759d076e51c 100644 --- a/pkg/setting/setting_unified_storage.go +++ b/pkg/setting/setting_unified_storage.go @@ -55,7 +55,6 @@ func (cfg *Cfg) setUnifiedStorageConfig() { cfg.IndexPath = section.Key("index_path").String() cfg.IndexWorkers = section.Key("index_workers").MustInt(10) cfg.IndexRebuildWorkers = section.Key("index_rebuild_workers").MustInt(5) - cfg.IndexMaxBatchSize = section.Key("index_max_batch_size").MustInt(100) cfg.EnableSharding = section.Key("enable_sharding").MustBool(false) cfg.QOSEnabled = section.Key("qos_enabled").MustBool(false) cfg.QOSNumberWorker = section.Key("qos_num_worker").MustInt(16) diff --git a/pkg/storage/unified/search/bleve.go b/pkg/storage/unified/search/bleve.go index 190322bab7c..0f3ab8b2614 100644 --- a/pkg/storage/unified/search/bleve.go +++ b/pkg/storage/unified/search/bleve.go @@ -65,10 +65,6 @@ type BleveOptions struct { // The resource count where values switch from memory to file based FileThreshold int64 - // How big should a batch get before flushing - // ?? not totally sure the units - BatchSize int - // Index cache TTL for bleve indices. 0 disables expiration for in-memory indexes. // Also used for file-based indexes, if they are not owned by this instance, and they are not fetched from the cache recently. IndexCacheTTL time.Duration diff --git a/pkg/storage/unified/search/bleve_performance_test.go b/pkg/storage/unified/search/bleve_performance_test.go index f8f869180cf..643e6a70f14 100644 --- a/pkg/storage/unified/search/bleve_performance_test.go +++ b/pkg/storage/unified/search/bleve_performance_test.go @@ -20,7 +20,7 @@ func setupIndex(b testing.TB, useFullNgram bool) resource.ResourceIndex { // batchSize := 10000 // faster 5s (for 200k documents) - 27s (for 1M documents) batchSize := 100000 // fasterer 3.5s (for 200k documents) - 27s (for 1M documents) writer := newTestWriter(size, batchSize) - return newTestDashboardsIndex(b, 1, int64(size), int64(batchSize), writer, useFullNgram) + return newTestDashboardsIndex(b, 1, int64(size), writer, useFullNgram) } const maxAllowedTime = 20 * time.Millisecond // Reasonable (can vary per env) performance threshold per query (e.g., 20ms) diff --git a/pkg/storage/unified/search/bleve_search_test.go b/pkg/storage/unified/search/bleve_search_test.go index 860df9f268a..476f35985a1 100644 --- a/pkg/storage/unified/search/bleve_search_test.go +++ b/pkg/storage/unified/search/bleve_search_test.go @@ -68,7 +68,7 @@ func TestCanSearchByTitle(t *testing.T) { t.Run(name, func(t *testing.T) { t.Run("and query is empty, sort documents by title instead of search score", func(t *testing.T) { - index := newTestDashboardsIndex(t, threshold, 2, 2, noop, useFullNgram) + index := newTestDashboardsIndex(t, threshold, 2, noop, useFullNgram) indexDocumentsWithTitles(t, index, key, map[string]string{ "name1": "bbb", "name2": "aaa", @@ -78,7 +78,7 @@ func TestCanSearchByTitle(t *testing.T) { }) t.Run("will boost phrase match query over match query results", func(t *testing.T) { - index := newTestDashboardsIndex(t, threshold, 2, 2, noop, useFullNgram) + index := newTestDashboardsIndex(t, threshold, 2, noop, useFullNgram) indexDocumentsWithTitles(t, index, key, map[string]string{ "name1": "I want to say a hello", "name2": "we want hello", @@ -88,7 +88,7 @@ func TestCanSearchByTitle(t *testing.T) { }) t.Run("will prioritize matches", func(t *testing.T) { - index := newTestDashboardsIndex(t, threshold, 2, 2, noop, useFullNgram) + index := newTestDashboardsIndex(t, threshold, 2, noop, useFullNgram) indexDocumentsWithTitles(t, index, key, map[string]string{ "name1": "Asserts Dashboards", "name2": "New dashboard 10", @@ -98,7 +98,7 @@ func TestCanSearchByTitle(t *testing.T) { }) t.Run("all terms must match", func(t *testing.T) { - index := newTestDashboardsIndex(t, threshold, 2, 2, noop, useFullNgram) + index := newTestDashboardsIndex(t, threshold, 2, noop, useFullNgram) indexDocumentsWithTitles(t, index, key, map[string]string{ "name1": "Dashboard", "name2": "New dashboard 10", @@ -108,7 +108,7 @@ func TestCanSearchByTitle(t *testing.T) { }) t.Run("will boost exact match query over match phrase query results", func(t *testing.T) { - index := newTestDashboardsIndex(t, threshold, 2, 2, noop, useFullNgram) + index := newTestDashboardsIndex(t, threshold, 2, noop, useFullNgram) indexDocumentsWithTitles(t, index, key, map[string]string{ "name1": "we want hello pls", "name2": "we want hello", @@ -118,7 +118,7 @@ func TestCanSearchByTitle(t *testing.T) { }) t.Run("title with numbers will match document", func(t *testing.T) { - index := newTestDashboardsIndex(t, threshold, 2, 2, noop, useFullNgram) + index := newTestDashboardsIndex(t, threshold, 2, noop, useFullNgram) indexDocumentsWithTitles(t, index, key, map[string]string{ "name1": "A123456", }) @@ -132,7 +132,7 @@ func TestCanSearchByTitle(t *testing.T) { }) t.Run("title will match escaped characters", func(t *testing.T) { - index := newTestDashboardsIndex(t, threshold, 2, 2, noop, useFullNgram) + index := newTestDashboardsIndex(t, threshold, 2, noop, useFullNgram) indexDocumentsWithTitles(t, index, key, map[string]string{ "name1": "what\"s up", "name2": "what\"s that", @@ -143,7 +143,7 @@ func TestCanSearchByTitle(t *testing.T) { }) t.Run("title search will match document", func(t *testing.T) { - index := newTestDashboardsIndex(t, threshold, 2, 2, noop, useFullNgram) + index := newTestDashboardsIndex(t, threshold, 2, noop, useFullNgram) indexDocumentsWithTitles(t, index, key, map[string]string{ "name1": "I want to say a wonderfully Hello to the WORLD! Hello-world", }) @@ -166,7 +166,7 @@ func TestCanSearchByTitle(t *testing.T) { }) t.Run("title search will NOT match documents", func(t *testing.T) { - index := newTestDashboardsIndex(t, threshold, 2, 2, noop, useFullNgram) + index := newTestDashboardsIndex(t, threshold, 2, noop, useFullNgram) indexDocumentsWithTitles(t, index, key, map[string]string{ "name1": "I want to say a wonderfully Hello to the WORLD! Hello-world", "name2": "A0456", @@ -182,7 +182,7 @@ func TestCanSearchByTitle(t *testing.T) { }) t.Run("title search with character will match one document", func(t *testing.T) { - index := newTestDashboardsIndex(t, threshold, 2, 2, noop, useFullNgram) + index := newTestDashboardsIndex(t, threshold, 2, noop, useFullNgram) indexDocumentsWithTitles(t, index, key, map[string]string{ "name1": "foo", }) @@ -202,7 +202,7 @@ func TestCanSearchByTitle(t *testing.T) { }) t.Run("title search will ignore terms < 3 characters", func(t *testing.T) { - index := newTestDashboardsIndex(t, threshold, 2, 2, noop, useFullNgram) + index := newTestDashboardsIndex(t, threshold, 2, noop, useFullNgram) indexDocumentsWithTitles(t, index, key, map[string]string{ "name1": "new dashboard", "name2": "new dash", @@ -224,7 +224,7 @@ func TestCanSearchByTitle(t *testing.T) { name = fmt.Sprintf(name, " NOT ") } t.Run(name, func(t *testing.T) { - index := newTestDashboardsIndex(t, threshold, 2, 2, noop, useFullNgram) + index := newTestDashboardsIndex(t, threshold, 2, noop, useFullNgram) indexDocumentsWithTitles(t, index, key, map[string]string{ "name1": "new dashboard", "name2": "new dash", @@ -271,7 +271,7 @@ func newQueryByTitle(query string) *resourcepb.ResourceSearchRequest { } } -func newTestDashboardsIndex(t testing.TB, threshold int64, size int64, batchSize int64, writer resource.BuildFn, useFullNgram bool) resource.ResourceIndex { +func newTestDashboardsIndex(t testing.TB, threshold int64, size int64, writer resource.BuildFn, useFullNgram bool) resource.ResourceIndex { key := &resourcepb.ResourceKey{ Namespace: "default", Group: "dashboard.grafana.app", @@ -280,7 +280,6 @@ func newTestDashboardsIndex(t testing.TB, threshold int64, size int64, batchSize backend, err := search.NewBleveBackend(search.BleveOptions{ Root: t.TempDir(), FileThreshold: threshold, // use in-memory for tests - BatchSize: int(batchSize), UseFullNgram: useFullNgram, }, tracing.NewNoopTracerService(), nil) require.NoError(t, err) diff --git a/pkg/storage/unified/search/options.go b/pkg/storage/unified/search/options.go index 0d856b586c3..47a24659347 100644 --- a/pkg/storage/unified/search/options.go +++ b/pkg/storage/unified/search/options.go @@ -43,7 +43,6 @@ func NewSearchOptions( bleve, err := NewBleveBackend(BleveOptions{ Root: root, FileThreshold: int64(cfg.IndexFileThreshold), // fewer than X items will use a memory index - BatchSize: cfg.IndexMaxBatchSize, // This is the batch size for how many objects to add to the index at once IndexCacheTTL: cfg.IndexCacheTTL, // How long to keep the index cache in memory BuildVersion: cfg.BuildVersion, UseFullNgram: features.IsEnabledGlobally(featuremgmt.FlagUnifiedStorageUseFullNgram), From a0fc683a67ef9b73f20934f910c3c2b450451f75 Mon Sep 17 00:00:00 2001 From: Matias Chomicki Date: Wed, 8 Oct 2025 17:57:39 +0200 Subject: [PATCH 020/112] Explore: Add missing height to logs in Explore (#112176) * Explore: Add missing height to logs in Explore * Add max height to controlled log rows --- public/app/features/explore/Logs/Logs.tsx | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/public/app/features/explore/Logs/Logs.tsx b/public/app/features/explore/Logs/Logs.tsx index 56076abfcce..6677b49ca91 100644 --- a/public/app/features/explore/Logs/Logs.tsx +++ b/public/app/features/explore/Logs/Logs.tsx @@ -1003,7 +1003,7 @@ const UnthemedLogs: React.FunctionComponent = (props: Props) => { {(!config.featureToggles.newLogsPanel || visualisationType === 'table') && config.featureToggles.logsPanelControls && hasData && ( -
+
Date: Wed, 8 Oct 2025 14:42:13 -0400 Subject: [PATCH 021/112] Timeseries: Numeric duration values could render as NaN (#73795) (#112076) --- packages/grafana-data/src/field/displayProcessor.test.ts | 5 +++++ packages/grafana-data/src/field/displayProcessor.ts | 8 ++++++-- 2 files changed, 11 insertions(+), 2 deletions(-) diff --git a/packages/grafana-data/src/field/displayProcessor.test.ts b/packages/grafana-data/src/field/displayProcessor.test.ts index 2cd13b48b58..b47ca53b17e 100644 --- a/packages/grafana-data/src/field/displayProcessor.test.ts +++ b/packages/grafana-data/src/field/displayProcessor.test.ts @@ -423,6 +423,11 @@ describe('Format value', () => { const processor = getDisplayProcessorFromConfig({ decimals: 5 }, FieldType.number); expect(processor(35, 2).text).toEqual('35.00000'); }); + + it('does not attempt to coerce non-numeric strings back to numbers', () => { + const processor = getDisplayProcessorFromConfig({ unit: 'dthms' }, FieldType.number); + expect(processor(35, 2).text).toEqual('00:00:35'); // we're checking that this isn't NaN. + }); }); }); diff --git a/packages/grafana-data/src/field/displayProcessor.ts b/packages/grafana-data/src/field/displayProcessor.ts index a9f0a7c2927..28c17bb604d 100644 --- a/packages/grafana-data/src/field/displayProcessor.ts +++ b/packages/grafana-data/src/field/displayProcessor.ts @@ -49,7 +49,7 @@ export function getDisplayProcessor(options?: DisplayProcessorOptions): DisplayP const { palette } = options.theme.visualization; let unit = config.unit; - let hasDateUnit = unit && (timeFormats[unit] || unit.startsWith('time:')); + let hasDateUnit = Boolean(unit && (timeFormats[unit] || unit.startsWith('time:'))); let showMs = false; if (field.type === FieldType.time && !hasDateUnit) { @@ -152,7 +152,11 @@ export function getDisplayProcessor(options?: DisplayProcessorOptions): DisplayP // this is needed because we may have determined the minimum determined `adjacentDecimals` for y tick increments based on // e.g. 'seconds' field unit (0.15s, 0.20s, 0.25s), but then formatFunc decided to return milli or nanos (150, 200, 250) // so we end up with excess precision: 150.00, 200.00, 250.00 - v.text = +v.text + ''; + // #73795 - some units, like duration formats, return text which cannot be coerced back into a number safely at this point. + const asNum = +v.text; + if (!Number.isNaN(asNum)) { + v.text = asNum + ''; + } } else { v = formatFunc(numeric, config.decimals, null, options.timeZone, showMs); } From d247f3ef4a20873a20a219c3d4fbb75756e2384e Mon Sep 17 00:00:00 2001 From: Drew Slobodnjak <60050885+drew08t@users.noreply.github.com> Date: Wed, 8 Oct 2025 12:03:39 -0700 Subject: [PATCH 022/112] Geomap: Ensure default marker path consistency (#111912) * Geomap: Ensure default marker path consistency * Remove redundant comments * Revert migration test --- .../plugins/panel/geomap/migrations.test.ts | 2 +- .../panel/geomap/style/markers.test.ts | 28 +++++++++++++++++++ .../app/plugins/panel/geomap/style/types.ts | 2 +- 3 files changed, 30 insertions(+), 2 deletions(-) diff --git a/public/app/plugins/panel/geomap/migrations.test.ts b/public/app/plugins/panel/geomap/migrations.test.ts index b37cbcf7ec5..6cf672211dd 100644 --- a/public/app/plugins/panel/geomap/migrations.test.ts +++ b/public/app/plugins/panel/geomap/migrations.test.ts @@ -76,7 +76,7 @@ describe('Worldmap Migrations', () => { "min": 2, }, "symbol": { - "fixed": "build/img/icons/marker/circle.svg", + "fixed": "img/icons/marker/circle.svg", "mode": "fixed", }, "symbolAlign": { diff --git a/public/app/plugins/panel/geomap/style/markers.test.ts b/public/app/plugins/panel/geomap/style/markers.test.ts index c7ce4cb845b..7b2e75219a4 100644 --- a/public/app/plugins/panel/geomap/style/markers.test.ts +++ b/public/app/plugins/panel/geomap/style/markers.test.ts @@ -8,7 +8,10 @@ import { opacityExpression, rotationExpression, offsetExpression, + getMarkerMaker, + circleMarker, } from './markers'; +import { defaultStyleConfig } from './types'; // Mock dependencies jest.mock('app/features/dimensions/resource', () => ({ @@ -101,3 +104,28 @@ describe('getWebGLStyle', () => { consoleErrorSpy.mockRestore(); }); }); + +describe('Icon Path Consistency', () => { + const circleIconPath = 'img/icons/marker/circle.svg'; + + beforeEach(() => { + jest.clearAllMocks(); + }); + + it('should use consistent paths between default and selected circle icons', () => { + expect(defaultStyleConfig.symbol.fixed).toBe(circleIconPath); + }); + + it('should handle default and explicitly selected circle icons the same way for WebGL style', async () => { + const defaultStyle = await getWebGLStyle(); // No symbol + const explicitCircleStyle = await getWebGLStyle(circleIconPath); + + expect(explicitCircleStyle).toEqual(defaultStyle); + }); + + it('should create valid marker maker for default icon', async () => { + const defaultMaker = await getMarkerMaker(); // No symbol + + expect(defaultMaker).toBe(circleMarker); + }); +}); diff --git a/public/app/plugins/panel/geomap/style/types.ts b/public/app/plugins/panel/geomap/style/types.ts index de8cf8008e0..72703a90476 100644 --- a/public/app/plugins/panel/geomap/style/types.ts +++ b/public/app/plugins/panel/geomap/style/types.ts @@ -74,7 +74,7 @@ export const defaultStyleConfig = Object.freeze({ opacity: 0.4, symbol: { mode: ResourceDimensionMode.Fixed, - fixed: 'build/img/icons/marker/circle.svg', + fixed: 'img/icons/marker/circle.svg', }, symbolAlign: { horizontal: HorizontalAlign.Center, From 1cbe7c8848c3bd0505b5b5d134817e6c7d45c2c3 Mon Sep 17 00:00:00 2001 From: Gabriel MABILLE Date: Wed, 8 Oct 2025 21:41:44 +0200 Subject: [PATCH 023/112] `AuthZ`: log incomplete folder tree (#112151) --- pkg/services/authz/rbac/service.go | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/pkg/services/authz/rbac/service.go b/pkg/services/authz/rbac/service.go index 45d2c97c11d..3aa77178b1e 100644 --- a/pkg/services/authz/rbac/service.go +++ b/pkg/services/authz/rbac/service.go @@ -678,6 +678,11 @@ func (s *Service) checkInheritedPermissions(ctx context.Context, scopeMap map[st ctxLogger.Error("could not build folder and dashboard tree", "error", err) return false, err } + if !s.isFolderInTree(tree, req.ParentFolder) { + // Not erroring here as the permission might exist but the folder wasn't synchronized yet + // Once in mode 5 we can deny access here + ctxLogger.Error("parent folder not found in folder tree", "folder", req.ParentFolder) + } } if scopeMap["folders:uid:"+req.ParentFolder] { @@ -724,6 +729,9 @@ func (s *Service) buildFolderTree(ctx context.Context, ns types.NamespaceInfo) ( span.SetAttributes(attribute.Int("num_folders", len(folders))) tree := newFolderTree(folders) + if len(tree.Nodes) != len(folders) { + s.logger.FromContext(ctx).Warn("mismatched folder count when building tree", "namespace", ns.Value, "expected", len(folders), "got", len(tree.Nodes)) + } s.folderCache.Set(ctx, folderCacheKey(ns.Value), tree) return tree, nil From 0e107c77de59abaf66d9e2ad7ec6d8f063473138 Mon Sep 17 00:00:00 2001 From: Oscar Kilhed Date: Thu, 9 Oct 2025 09:39:09 +0200 Subject: [PATCH 024/112] Update hidden element copy (#112089) --- .../hooks/ConditionalRenderingOverlay.tsx | 7 +------ .../dashboard-scene/scene/layout-tabs/TabItemRenderer.tsx | 7 +------ public/locales/en-US/grafana.json | 2 +- 3 files changed, 3 insertions(+), 13 deletions(-) diff --git a/public/app/features/dashboard-scene/conditional-rendering/hooks/ConditionalRenderingOverlay.tsx b/public/app/features/dashboard-scene/conditional-rendering/hooks/ConditionalRenderingOverlay.tsx index 12a737969b7..d41d74136d5 100644 --- a/public/app/features/dashboard-scene/conditional-rendering/hooks/ConditionalRenderingOverlay.tsx +++ b/public/app/features/dashboard-scene/conditional-rendering/hooks/ConditionalRenderingOverlay.tsx @@ -9,12 +9,7 @@ export const ConditionalRenderingOverlay = () => { return (
-
diff --git a/public/app/features/dashboard-scene/scene/layout-tabs/TabItemRenderer.tsx b/public/app/features/dashboard-scene/scene/layout-tabs/TabItemRenderer.tsx index 1c3e30d4aeb..06d526420a1 100644 --- a/public/app/features/dashboard-scene/scene/layout-tabs/TabItemRenderer.tsx +++ b/public/app/features/dashboard-scene/scene/layout-tabs/TabItemRenderer.tsx @@ -101,12 +101,7 @@ export function TabItemRenderer({ model }: SceneComponentProps) { function IsHiddenSuffix() { return ( - diff --git a/public/locales/en-US/grafana.json b/public/locales/en-US/grafana.json index 112a1337dd0..718e89cedb9 100644 --- a/public/locales/en-US/grafana.json +++ b/public/locales/en-US/grafana.json @@ -4572,7 +4572,7 @@ "unsupported-object-type": "Conditional rendering not supported for this item type" }, "overlay": { - "tooltip": "Element is hidden due to conditional rendering." + "tooltip": "Element is hidden by show/hide rules." }, "root": { "title": "Show / hide rules" From 857e73bdc28b999affe5b6fa3b853324b6347ab6 Mon Sep 17 00:00:00 2001 From: Victor Marin <36818606+mdvictor@users.noreply.github.com> Date: Thu, 9 Oct 2025 10:54:45 +0300 Subject: [PATCH 025/112] Dashboards: Set AdHocFilters variable multi operator property properly (#112155) Fixes case where adhoc filter would not get multi operators even though DS supported it --- public/app/features/dashboard-scene/utils/variables.ts | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/public/app/features/dashboard-scene/utils/variables.ts b/public/app/features/dashboard-scene/utils/variables.ts index 6b6eeae5e77..43214dce91c 100644 --- a/public/app/features/dashboard-scene/utils/variables.ts +++ b/public/app/features/dashboard-scene/utils/variables.ts @@ -65,7 +65,7 @@ export function createVariablesForSnapshot(oldModel: DashboardModel) { useQueriesAsFilterForOptions: true, layout: config.featureToggles.newFiltersUI ? 'combobox' : undefined, supportsMultiValueOperators: Boolean( - getDataSourceSrv().getInstanceSettings(v.datasource)?.meta.multiValueFilterOperators + getDataSourceSrv().getInstanceSettings({ type: v.datasource?.type })?.meta.multiValueFilterOperators ), }); } @@ -152,7 +152,7 @@ export function createSceneVariableFromVariableModel(variable: TypedVariableMode useQueriesAsFilterForOptions: true, layout: config.featureToggles.newFiltersUI ? 'combobox' : undefined, supportsMultiValueOperators: Boolean( - getDataSourceSrv().getInstanceSettings(variable.datasource)?.meta.multiValueFilterOperators + getDataSourceSrv().getInstanceSettings({ type: variable.datasource?.type })?.meta.multiValueFilterOperators ), }); } From f4cd46504b6e68865ccc6a83594ecb5a88abdc38 Mon Sep 17 00:00:00 2001 From: Gabriel MABILLE Date: Thu, 9 Oct 2025 10:49:50 +0200 Subject: [PATCH 026/112] `AuthZ`: Add if user is allowed to the span attribute (#112197) * `AuthZ`: Add if user is allowed to the span attribute * Suggestiong --- pkg/services/authz/rbac/service.go | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/pkg/services/authz/rbac/service.go b/pkg/services/authz/rbac/service.go index 3aa77178b1e..6ba8e0a8838 100644 --- a/pkg/services/authz/rbac/service.go +++ b/pkg/services/authz/rbac/service.go @@ -119,6 +119,7 @@ func (s *Service) Check(ctx context.Context, req *authzv1.CheckRequest) (*authzv ctxLogger.Debug("Check execution time", "duration", time.Since(start).Milliseconds()) }(time.Now()) + allow := &authzv1.CheckResponse{Allowed: true} deny := &authzv1.CheckResponse{Allowed: false} checkReq, err := s.validateCheckRequest(ctx, req) @@ -135,13 +136,14 @@ func (s *Service) Check(ctx context.Context, req *authzv1.CheckRequest) (*authzv attribute.String("action", checkReq.Action), attribute.String("name", checkReq.Name), attribute.String("folder", checkReq.ParentFolder), + attribute.Bool("allowed", false), ) permDenialKey := userPermDenialCacheKey(checkReq.Namespace.Value, checkReq.UserUID, checkReq.Action, checkReq.Name, checkReq.ParentFolder) if _, ok := s.permDenialCache.Get(ctx, permDenialKey); ok { s.metrics.permissionCacheUsage.WithLabelValues("true", checkReq.Action).Inc() s.metrics.requestCount.WithLabelValues("false", "true", req.GetVerb(), req.GetGroup(), req.GetResource()).Inc() - return &authzv1.CheckResponse{Allowed: false}, nil + return deny, nil } cachedPerms, err := s.getCachedIdentityPermissions(ctx, checkReq.Namespace, checkReq.IdentityType, checkReq.UserUID, checkReq.Action) @@ -155,7 +157,8 @@ func (s *Service) Check(ctx context.Context, req *authzv1.CheckRequest) (*authzv if allowed { s.metrics.permissionCacheUsage.WithLabelValues("true", checkReq.Action).Inc() s.metrics.requestCount.WithLabelValues("false", "true", req.GetVerb(), req.GetGroup(), req.GetResource()).Inc() - return &authzv1.CheckResponse{Allowed: allowed}, nil + span.SetAttributes(attribute.Bool("allowed", true)) + return allow, nil } } s.metrics.permissionCacheUsage.WithLabelValues("false", checkReq.Action).Inc() @@ -179,6 +182,7 @@ func (s *Service) Check(ctx context.Context, req *authzv1.CheckRequest) (*authzv } s.metrics.requestCount.WithLabelValues("false", "true", req.GetVerb(), req.GetGroup(), req.GetResource()).Inc() + span.SetAttributes(attribute.Bool("allowed", allowed)) return &authzv1.CheckResponse{Allowed: allowed}, nil } From 3ad54c0c1e7820fc14994957c4e4b0a1de46161b Mon Sep 17 00:00:00 2001 From: Matheus Macabu Date: Thu, 9 Oct 2025 10:56:12 +0200 Subject: [PATCH 027/112] Scopes: Remove all duplicated types for single source of truth (#112100) * apimachinery: Remove unused scope types * tsdb/loki: use scope types from apps/scope * promlib: use scope types from apps/scope --- .../apis/common/v0alpha1/types.go | 43 ----- .../common/v0alpha1/zz_generated.deepcopy.go | 80 --------- .../common/v0alpha1/zz_generated.openapi.go | 159 ------------------ pkg/promlib/go.mod | 5 +- pkg/promlib/go.sum | 5 + pkg/promlib/models/query.go | 41 +---- pkg/promlib/models/query.panel.schema.json | 10 -- pkg/promlib/models/query.request.schema.json | 10 -- pkg/promlib/models/query.types.json | 12 +- pkg/promlib/models/scope.go | 21 +-- pkg/promlib/models/scope_test.go | 107 ++++++------ pkg/promlib/resource/resource.go | 5 +- pkg/promlib/resource/resource_test.go | 10 +- pkg/tsdb/loki/loki.go | 8 +- pkg/tsdb/loki/scopes.go | 7 +- pkg/tsdb/loki/scopes_test.go | 34 ++-- pkg/tsdb/loki/types.go | 5 +- 17 files changed, 115 insertions(+), 447 deletions(-) diff --git a/pkg/apimachinery/apis/common/v0alpha1/types.go b/pkg/apimachinery/apis/common/v0alpha1/types.go index 94f5670a89a..9cb6f958a87 100644 --- a/pkg/apimachinery/apis/common/v0alpha1/types.go +++ b/pkg/apimachinery/apis/common/v0alpha1/types.go @@ -42,46 +42,3 @@ func (r ObjectReference) ToOwnerReference() metav1.OwnerReference { UID: r.UID, } } - -// +k8s:deepcopy-gen:interfaces=k8s.io/apimachinery/pkg/runtime.Object -type Scope struct { - metav1.TypeMeta `json:",inline"` - metav1.ObjectMeta `json:"metadata,omitempty"` - - Spec ScopeSpec `json:"spec,omitempty"` -} - -type ScopeSpec struct { - Title string `json:"title"` - // Provides a default path for the scope. This refers to a list of nodes in the selector. This is used to display the title next to the selected scope and expand the selector to the proper path. - // This will override whichever is selected from in the selector. - // The path is a list of node ids, starting at the direct parent of the selected node towards the root. - // +listType=atomic - DefaultPath []string `json:"defaultPath,omitempty"` - - // +listType=atomic - Filters []ScopeFilter `json:"filters,omitempty"` -} - -type ScopeFilter struct { - Key string `json:"key"` - Value string `json:"value"` - // Values is used for operators that require multiple values (e.g. one-of and not-one-of). - // +listType=atomic - Values []string `json:"values,omitempty"` - Operator FilterOperator `json:"operator"` -} - -// Type of the filter operator. -// +enum -type FilterOperator string - -// Defines values for FilterOperator. -const ( - FilterOperatorEquals FilterOperator = "equals" - FilterOperatorNotEquals FilterOperator = "not-equals" - FilterOperatorRegexMatch FilterOperator = "regex-match" - FilterOperatorRegexNotMatch FilterOperator = "regex-not-match" - FilterOperatorOneOf FilterOperator = "one-of" - FilterOperatorNotOneOf FilterOperator = "not-one-of" -) diff --git a/pkg/apimachinery/apis/common/v0alpha1/zz_generated.deepcopy.go b/pkg/apimachinery/apis/common/v0alpha1/zz_generated.deepcopy.go index 41c6e53441b..ce8cef02c51 100644 --- a/pkg/apimachinery/apis/common/v0alpha1/zz_generated.deepcopy.go +++ b/pkg/apimachinery/apis/common/v0alpha1/zz_generated.deepcopy.go @@ -7,10 +7,6 @@ package v0alpha1 -import ( - runtime "k8s.io/apimachinery/pkg/runtime" -) - // DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. func (in *InlineSecureValue) DeepCopyInto(out *InlineSecureValue) { *out = *in @@ -42,79 +38,3 @@ func (in *ObjectReference) DeepCopy() *ObjectReference { in.DeepCopyInto(out) return out } - -// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. -func (in *Scope) DeepCopyInto(out *Scope) { - *out = *in - out.TypeMeta = in.TypeMeta - in.ObjectMeta.DeepCopyInto(&out.ObjectMeta) - in.Spec.DeepCopyInto(&out.Spec) - return -} - -// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new Scope. -func (in *Scope) DeepCopy() *Scope { - if in == nil { - return nil - } - out := new(Scope) - in.DeepCopyInto(out) - return out -} - -// DeepCopyObject is an autogenerated deepcopy function, copying the receiver, creating a new runtime.Object. -func (in *Scope) DeepCopyObject() runtime.Object { - if c := in.DeepCopy(); c != nil { - return c - } - return nil -} - -// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. -func (in *ScopeFilter) DeepCopyInto(out *ScopeFilter) { - *out = *in - if in.Values != nil { - in, out := &in.Values, &out.Values - *out = make([]string, len(*in)) - copy(*out, *in) - } - return -} - -// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new ScopeFilter. -func (in *ScopeFilter) DeepCopy() *ScopeFilter { - if in == nil { - return nil - } - out := new(ScopeFilter) - in.DeepCopyInto(out) - return out -} - -// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. -func (in *ScopeSpec) DeepCopyInto(out *ScopeSpec) { - *out = *in - if in.DefaultPath != nil { - in, out := &in.DefaultPath, &out.DefaultPath - *out = make([]string, len(*in)) - copy(*out, *in) - } - if in.Filters != nil { - in, out := &in.Filters, &out.Filters - *out = make([]ScopeFilter, len(*in)) - for i := range *in { - (*in)[i].DeepCopyInto(&(*out)[i]) - } - } - return -} - -// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new ScopeSpec. -func (in *ScopeSpec) DeepCopy() *ScopeSpec { - if in == nil { - return nil - } - out := new(ScopeSpec) - in.DeepCopyInto(out) - return out -} diff --git a/pkg/apimachinery/apis/common/v0alpha1/zz_generated.openapi.go b/pkg/apimachinery/apis/common/v0alpha1/zz_generated.openapi.go index b3042736e0c..da02bcce8ac 100644 --- a/pkg/apimachinery/apis/common/v0alpha1/zz_generated.openapi.go +++ b/pkg/apimachinery/apis/common/v0alpha1/zz_generated.openapi.go @@ -17,9 +17,6 @@ func GetOpenAPIDefinitions(ref common.ReferenceCallback) map[string]common.OpenA return map[string]common.OpenAPIDefinition{ "github.com/grafana/grafana/pkg/apimachinery/apis/common/v0alpha1.InlineSecureValue": InlineSecureValue{}.OpenAPIDefinition(), "github.com/grafana/grafana/pkg/apimachinery/apis/common/v0alpha1.ObjectReference": schema_apimachinery_apis_common_v0alpha1_ObjectReference(ref), - "github.com/grafana/grafana/pkg/apimachinery/apis/common/v0alpha1.Scope": schema_apimachinery_apis_common_v0alpha1_Scope(ref), - "github.com/grafana/grafana/pkg/apimachinery/apis/common/v0alpha1.ScopeFilter": schema_apimachinery_apis_common_v0alpha1_ScopeFilter(ref), - "github.com/grafana/grafana/pkg/apimachinery/apis/common/v0alpha1.ScopeSpec": schema_apimachinery_apis_common_v0alpha1_ScopeSpec(ref), "github.com/grafana/grafana/pkg/apimachinery/apis/common/v0alpha1.Unstructured": Unstructured{}.OpenAPIDefinition(), "k8s.io/apimachinery/pkg/apis/meta/v1.APIGroup": schema_pkg_apis_meta_v1_APIGroup(ref), "k8s.io/apimachinery/pkg/apis/meta/v1.APIGroupList": schema_pkg_apis_meta_v1_APIGroupList(ref), @@ -139,162 +136,6 @@ func schema_apimachinery_apis_common_v0alpha1_ObjectReference(ref common.Referen } } -func schema_apimachinery_apis_common_v0alpha1_Scope(ref common.ReferenceCallback) common.OpenAPIDefinition { - return common.OpenAPIDefinition{ - Schema: spec.Schema{ - SchemaProps: spec.SchemaProps{ - Type: []string{"object"}, - Properties: map[string]spec.Schema{ - "kind": { - SchemaProps: spec.SchemaProps{ - Description: "Kind is a string value representing the REST resource this object represents. Servers may infer this from the endpoint the client submits requests to. Cannot be updated. In CamelCase. More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#types-kinds", - Type: []string{"string"}, - Format: "", - }, - }, - "apiVersion": { - SchemaProps: spec.SchemaProps{ - Description: "APIVersion defines the versioned schema of this representation of an object. Servers should convert recognized schemas to the latest internal value, and may reject unrecognized values. More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#resources", - Type: []string{"string"}, - Format: "", - }, - }, - "metadata": { - SchemaProps: spec.SchemaProps{ - Default: map[string]interface{}{}, - Ref: ref("k8s.io/apimachinery/pkg/apis/meta/v1.ObjectMeta"), - }, - }, - "spec": { - SchemaProps: spec.SchemaProps{ - Default: map[string]interface{}{}, - Ref: ref("github.com/grafana/grafana/pkg/apimachinery/apis/common/v0alpha1.ScopeSpec"), - }, - }, - }, - }, - }, - Dependencies: []string{ - "github.com/grafana/grafana/pkg/apimachinery/apis/common/v0alpha1.ScopeSpec", "k8s.io/apimachinery/pkg/apis/meta/v1.ObjectMeta"}, - } -} - -func schema_apimachinery_apis_common_v0alpha1_ScopeFilter(ref common.ReferenceCallback) common.OpenAPIDefinition { - return common.OpenAPIDefinition{ - Schema: spec.Schema{ - SchemaProps: spec.SchemaProps{ - Type: []string{"object"}, - Properties: map[string]spec.Schema{ - "key": { - SchemaProps: spec.SchemaProps{ - Default: "", - Type: []string{"string"}, - Format: "", - }, - }, - "value": { - SchemaProps: spec.SchemaProps{ - Default: "", - Type: []string{"string"}, - Format: "", - }, - }, - "values": { - VendorExtensible: spec.VendorExtensible{ - Extensions: spec.Extensions{ - "x-kubernetes-list-type": "atomic", - }, - }, - SchemaProps: spec.SchemaProps{ - Description: "Values is used for operators that require multiple values (e.g. one-of and not-one-of).", - Type: []string{"array"}, - Items: &spec.SchemaOrArray{ - Schema: &spec.Schema{ - SchemaProps: spec.SchemaProps{ - Default: "", - Type: []string{"string"}, - Format: "", - }, - }, - }, - }, - }, - "operator": { - SchemaProps: spec.SchemaProps{ - Description: "Possible enum values:\n - `\"equals\"`\n - `\"not-equals\"`\n - `\"not-one-of\"`\n - `\"one-of\"`\n - `\"regex-match\"`\n - `\"regex-not-match\"`", - Default: "", - Type: []string{"string"}, - Format: "", - Enum: []interface{}{"equals", "not-equals", "not-one-of", "one-of", "regex-match", "regex-not-match"}, - }, - }, - }, - Required: []string{"key", "value", "operator"}, - }, - }, - } -} - -func schema_apimachinery_apis_common_v0alpha1_ScopeSpec(ref common.ReferenceCallback) common.OpenAPIDefinition { - return common.OpenAPIDefinition{ - Schema: spec.Schema{ - SchemaProps: spec.SchemaProps{ - Type: []string{"object"}, - Properties: map[string]spec.Schema{ - "title": { - SchemaProps: spec.SchemaProps{ - Default: "", - Type: []string{"string"}, - Format: "", - }, - }, - "defaultPath": { - VendorExtensible: spec.VendorExtensible{ - Extensions: spec.Extensions{ - "x-kubernetes-list-type": "atomic", - }, - }, - SchemaProps: spec.SchemaProps{ - Description: "Provides a default path for the scope. This refers to a list of nodes in the selector. This is used to display the title next to the selected scope and expand the selector to the proper path. This will override whichever is selected from in the selector. The path is a list of node ids, starting at the direct parent of the selected node towards the root.", - Type: []string{"array"}, - Items: &spec.SchemaOrArray{ - Schema: &spec.Schema{ - SchemaProps: spec.SchemaProps{ - Default: "", - Type: []string{"string"}, - Format: "", - }, - }, - }, - }, - }, - "filters": { - VendorExtensible: spec.VendorExtensible{ - Extensions: spec.Extensions{ - "x-kubernetes-list-type": "atomic", - }, - }, - SchemaProps: spec.SchemaProps{ - Type: []string{"array"}, - Items: &spec.SchemaOrArray{ - Schema: &spec.Schema{ - SchemaProps: spec.SchemaProps{ - Default: map[string]interface{}{}, - Ref: ref("github.com/grafana/grafana/pkg/apimachinery/apis/common/v0alpha1.ScopeFilter"), - }, - }, - }, - }, - }, - }, - Required: []string{"title"}, - }, - }, - Dependencies: []string{ - "github.com/grafana/grafana/pkg/apimachinery/apis/common/v0alpha1.ScopeFilter"}, - } -} - func schema_pkg_apis_meta_v1_APIGroup(ref common.ReferenceCallback) common.OpenAPIDefinition { return common.OpenAPIDefinition{ Schema: spec.Schema{ diff --git a/pkg/promlib/go.mod b/pkg/promlib/go.mod index e28af53a841..cc2420b2223 100644 --- a/pkg/promlib/go.mod +++ b/pkg/promlib/go.mod @@ -5,6 +5,7 @@ go 1.25.2 require ( github.com/grafana/dskit v0.0.0-20250908063411-6b6da59b5cc4 github.com/grafana/grafana-plugin-sdk-go v0.279.0 + github.com/grafana/grafana/apps/scope v0.0.0-20251007093103-792853df9134 github.com/json-iterator/go v1.1.12 github.com/prometheus/client_golang v1.23.2 github.com/prometheus/common v0.66.1 @@ -54,6 +55,7 @@ require ( github.com/google/go-cmp v0.7.0 // indirect github.com/google/uuid v1.6.0 // indirect github.com/gorilla/mux v1.8.1 // indirect + github.com/grafana/grafana/pkg/apimachinery v0.0.0-20251007081214-26e147d01f0a // indirect github.com/grafana/otel-profiling-go v0.5.1 // indirect github.com/grafana/pyroscope-go/godeltaprof v0.1.8 // indirect github.com/grafana/regexp v0.0.0-20240518133315-a468a5bfb3bc // indirect @@ -91,10 +93,9 @@ require ( github.com/prometheus/client_model v0.6.2 // indirect github.com/prometheus/procfs v0.16.1 // indirect github.com/rivo/uniseg v0.4.7 // indirect - github.com/rogpeppe/go-internal v1.14.1 // indirect github.com/russross/blackfriday/v2 v2.1.0 // indirect github.com/smartystreets/goconvey v1.8.1 // indirect - github.com/spf13/pflag v1.0.10 // indirect + github.com/stretchr/objx v0.5.2 // indirect github.com/ugorji/go/codec v1.2.11 // indirect github.com/unknwon/bra v0.0.0-20200517080246-1e3013ecaff8 // indirect github.com/unknwon/com v1.0.1 // indirect diff --git a/pkg/promlib/go.sum b/pkg/promlib/go.sum index 59f48deaaba..3351c0f7097 100644 --- a/pkg/promlib/go.sum +++ b/pkg/promlib/go.sum @@ -134,6 +134,10 @@ github.com/grafana/dskit v0.0.0-20250908063411-6b6da59b5cc4 h1:jSojuc7njleS3UOz2 github.com/grafana/dskit v0.0.0-20250908063411-6b6da59b5cc4/go.mod h1:VahT+GtfQIM+o8ht2StR6J9g+Ef+C2Vokh5uuSmOD/4= github.com/grafana/grafana-plugin-sdk-go v0.279.0 h1:/KCrsZkj9pEGwIGovqAz1A8rjI2A2YT+ZpvgfZN0LAA= github.com/grafana/grafana-plugin-sdk-go v0.279.0/go.mod h1:/7oGN6Z7DGTGaLHhgIYrRr6Wvmdsb3BLw5hL4Kbjy88= +github.com/grafana/grafana/apps/scope v0.0.0-20251007093103-792853df9134 h1:xly75v5lFNR37q+wXnwA5yU/fPW9IOSYbhFpt4tQyt8= +github.com/grafana/grafana/apps/scope v0.0.0-20251007093103-792853df9134/go.mod h1:zijsUNa1zi476JJIR2Lcm/Paz1nRCno9XCp6hbS6G9o= +github.com/grafana/grafana/pkg/apimachinery v0.0.0-20251007081214-26e147d01f0a h1:L7xgV9mP6MRF3L2/vDOjNR7heaBPbXPMGTDN9/jXSFQ= +github.com/grafana/grafana/pkg/apimachinery v0.0.0-20251007081214-26e147d01f0a/go.mod h1:OK8NwS87D5YphchOcAsiIWk/feMZ0EzfAGME1Kff860= github.com/grafana/otel-profiling-go v0.5.1 h1:stVPKAFZSa7eGiqbYuG25VcqYksR6iWvF3YH66t4qL8= github.com/grafana/otel-profiling-go v0.5.1/go.mod h1:ftN/t5A/4gQI19/8MoWurBEtC6gFw8Dns1sJZ9W4Tls= github.com/grafana/pyroscope-go/godeltaprof v0.1.8 h1:iwOtYXeeVSAeYefJNaxDytgjKtUuKQbJqgAIjlnicKg= @@ -278,6 +282,7 @@ github.com/spf13/pflag v1.0.10/go.mod h1:McXfInJRrz4CZXVZOBLb0bTZqETkiAhM9Iw0y3A github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= github.com/stretchr/objx v0.4.0/go.mod h1:YvHI0jy2hoMjB+UWwv71VJQ9isScKT/TqJzVSSt89Yw= github.com/stretchr/objx v0.5.0/go.mod h1:Yh+to48EsGEfYuaHDzXPcE3xhTkx73EhmCGUpEOglKo= +github.com/stretchr/objx v0.5.2 h1:xuMeJ0Sdp5ZMRXx/aWO6RZxdr3beISkG5/G/aIRr3pY= github.com/stretchr/objx v0.5.2/go.mod h1:FRsXN1f5AsAjCGJKqEizvkpNtU+EGNCLh3NxZ/8L+MA= github.com/stretchr/testify v1.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UVUgZn+9EI= github.com/stretchr/testify v1.7.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= diff --git a/pkg/promlib/models/query.go b/pkg/promlib/models/query.go index ec32b7bc3cd..a34c1b6aea9 100644 --- a/pkg/promlib/models/query.go +++ b/pkg/promlib/models/query.go @@ -13,6 +13,7 @@ import ( "github.com/grafana/grafana-plugin-sdk-go/backend" "github.com/grafana/grafana-plugin-sdk-go/backend/gtime" sdkapi "github.com/grafana/grafana-plugin-sdk-go/experimental/apis/data/v0alpha1" + scope "github.com/grafana/grafana/apps/scope/pkg/apis/scope/v0alpha1" "go.opentelemetry.io/otel/attribute" "go.opentelemetry.io/otel/trace" @@ -68,47 +69,15 @@ type PrometheusQueryProperties struct { LegendFormat string `json:"legendFormat,omitempty"` // A set of filters applied to apply to the query - Scopes []ScopeSpec `json:"scopes,omitempty"` + Scopes []scope.ScopeSpec `json:"scopes,omitempty"` // Additional Ad-hoc filters that take precedence over Scope on conflict. - AdhocFilters []ScopeFilter `json:"adhocFilters,omitempty"` + AdhocFilters []scope.ScopeFilter `json:"adhocFilters,omitempty"` // Group By parameters to apply to aggregate expressions in the query GroupByKeys []string `json:"groupByKeys,omitempty"` } -// ScopeSpec is a hand copy of the ScopeSpec struct from pkg/apis/scope/v0alpha1/types.go -// to avoid import (temp fix). This also has metadata.name inlined. -type ScopeSpec struct { - Name string `json:"name"` // This is the identifier from metadata.name of the scope model. - Title string `json:"title"` - DefaultPath []string `json:"defaultPath,omitempty"` - Filters []ScopeFilter `json:"filters,omitempty"` -} - -// ScopeFilter is a hand copy of the ScopeFilter struct from pkg/apis/scope/v0alpha1/types.go -// to avoid import (temp fix) -type ScopeFilter struct { - Key string `json:"key"` - Value string `json:"value"` - // Values is used for operators that require multiple values (e.g. one-of and not-one-of). - Values []string `json:"values,omitempty"` - Operator FilterOperator `json:"operator"` -} - -// FilterOperator is a hand copy of the ScopeFilter struct from pkg/apis/scope/v0alpha1/types.go -type FilterOperator string - -// Hand copy of enum from pkg/apis/scope/v0alpha1/types.go -const ( - FilterOperatorEquals FilterOperator = "equals" - FilterOperatorNotEquals FilterOperator = "not-equals" - FilterOperatorRegexMatch FilterOperator = "regex-match" - FilterOperatorRegexNotMatch FilterOperator = "regex-not-match" - FilterOperatorOneOf FilterOperator = "one-of" - FilterOperatorNotOneOf FilterOperator = "not-one-of" -) - // Internal interval and range variables const ( varInterval = "$__interval" @@ -175,7 +144,7 @@ type Query struct { ExemplarQuery bool UtcOffsetSec int64 - Scopes []ScopeSpec + Scopes []scope.ScopeSpec } // This internal query struct is just like QueryModel, except it does not include: @@ -217,7 +186,7 @@ func Parse(ctx context.Context, log glog.Logger, span trace.Span, query backend. ) if enableScope { - var scopeFilters []ScopeFilter + var scopeFilters []scope.ScopeFilter for _, scope := range model.Scopes { scopeFilters = append(scopeFilters, scope.Filters...) } diff --git a/pkg/promlib/models/query.panel.schema.json b/pkg/promlib/models/query.panel.schema.json index c6ca6a172b8..e787ba10a20 100644 --- a/pkg/promlib/models/query.panel.schema.json +++ b/pkg/promlib/models/query.panel.schema.json @@ -18,7 +18,6 @@ "description": "Additional Ad-hoc filters that take precedence over Scope on conflict.", "type": "array", "items": { - "description": "ScopeFilter is a hand copy of the ScopeFilter struct from pkg/apis/scope/v0alpha1/types.go to avoid import (temp fix)", "type": "object", "required": [ "key", @@ -36,7 +35,6 @@ "type": "string" }, "values": { - "description": "Values is used for operators that require multiple values (e.g. one-of and not-one-of).", "type": "array", "items": { "type": "string" @@ -184,10 +182,8 @@ "description": "A set of filters applied to apply to the query", "type": "array", "items": { - "description": "ScopeSpec is a hand copy of the ScopeSpec struct from pkg/apis/scope/v0alpha1/types.go to avoid import (temp fix).", "type": "object", "required": [ - "name", "title" ], "properties": { @@ -200,7 +196,6 @@ "filters": { "type": "array", "items": { - "description": "ScopeFilter is a hand copy of the ScopeFilter struct from pkg/apis/scope/v0alpha1/types.go to avoid import (temp fix)", "type": "object", "required": [ "key", @@ -218,7 +213,6 @@ "type": "string" }, "values": { - "description": "Values is used for operators that require multiple values (e.g. one-of and not-one-of).", "type": "array", "items": { "type": "string" @@ -228,10 +222,6 @@ "additionalProperties": false } }, - "name": { - "description": "This is the identifier from metadata.name of the scope model.", - "type": "string" - }, "title": { "type": "string" } diff --git a/pkg/promlib/models/query.request.schema.json b/pkg/promlib/models/query.request.schema.json index 0e533b8e5c7..899197c97bd 100644 --- a/pkg/promlib/models/query.request.schema.json +++ b/pkg/promlib/models/query.request.schema.json @@ -28,7 +28,6 @@ "description": "Additional Ad-hoc filters that take precedence over Scope on conflict.", "type": "array", "items": { - "description": "ScopeFilter is a hand copy of the ScopeFilter struct from pkg/apis/scope/v0alpha1/types.go to avoid import (temp fix)", "type": "object", "required": [ "key", @@ -46,7 +45,6 @@ "type": "string" }, "values": { - "description": "Values is used for operators that require multiple values (e.g. one-of and not-one-of).", "type": "array", "items": { "type": "string" @@ -194,10 +192,8 @@ "description": "A set of filters applied to apply to the query", "type": "array", "items": { - "description": "ScopeSpec is a hand copy of the ScopeSpec struct from pkg/apis/scope/v0alpha1/types.go to avoid import (temp fix).", "type": "object", "required": [ - "name", "title" ], "properties": { @@ -210,7 +206,6 @@ "filters": { "type": "array", "items": { - "description": "ScopeFilter is a hand copy of the ScopeFilter struct from pkg/apis/scope/v0alpha1/types.go to avoid import (temp fix)", "type": "object", "required": [ "key", @@ -228,7 +223,6 @@ "type": "string" }, "values": { - "description": "Values is used for operators that require multiple values (e.g. one-of and not-one-of).", "type": "array", "items": { "type": "string" @@ -238,10 +232,6 @@ "additionalProperties": false } }, - "name": { - "description": "This is the identifier from metadata.name of the scope model.", - "type": "string" - }, "title": { "type": "string" } diff --git a/pkg/promlib/models/query.types.json b/pkg/promlib/models/query.types.json index 6d29e934973..1a30ec82503 100644 --- a/pkg/promlib/models/query.types.json +++ b/pkg/promlib/models/query.types.json @@ -8,7 +8,7 @@ { "metadata": { "name": "default", - "resourceVersion": "1758739325095", + "resourceVersion": "1759831574256", "creationTimestamp": "2024-03-25T13:19:04Z" }, "spec": { @@ -21,7 +21,6 @@ "description": "Additional Ad-hoc filters that take precedence over Scope on conflict.", "items": { "additionalProperties": false, - "description": "ScopeFilter is a hand copy of the ScopeFilter struct from pkg/apis/scope/v0alpha1/types.go to avoid import (temp fix)", "properties": { "key": { "type": "string" @@ -33,7 +32,6 @@ "type": "string" }, "values": { - "description": "Values is used for operators that require multiple values (e.g. one-of and not-one-of).", "items": { "type": "string" }, @@ -103,7 +101,6 @@ "description": "A set of filters applied to apply to the query", "items": { "additionalProperties": false, - "description": "ScopeSpec is a hand copy of the ScopeSpec struct from pkg/apis/scope/v0alpha1/types.go to avoid import (temp fix).", "properties": { "defaultPath": { "items": { @@ -114,7 +111,6 @@ "filters": { "items": { "additionalProperties": false, - "description": "ScopeFilter is a hand copy of the ScopeFilter struct from pkg/apis/scope/v0alpha1/types.go to avoid import (temp fix)", "properties": { "key": { "type": "string" @@ -126,7 +122,6 @@ "type": "string" }, "values": { - "description": "Values is used for operators that require multiple values (e.g. one-of and not-one-of).", "items": { "type": "string" }, @@ -142,16 +137,11 @@ }, "type": "array" }, - "name": { - "description": "This is the identifier from metadata.name of the scope model.", - "type": "string" - }, "title": { "type": "string" } }, "required": [ - "name", "title" ], "type": "object" diff --git a/pkg/promlib/models/scope.go b/pkg/promlib/models/scope.go index bc2aeb24535..13e9c3c082d 100644 --- a/pkg/promlib/models/scope.go +++ b/pkg/promlib/models/scope.go @@ -4,6 +4,7 @@ import ( "fmt" "strings" + scope "github.com/grafana/grafana/apps/scope/pkg/apis/scope/v0alpha1" "github.com/prometheus/common/model" "github.com/prometheus/prometheus/model/labels" "github.com/prometheus/prometheus/promql/parser" @@ -15,7 +16,7 @@ func init() { } // ApplyFiltersAndGroupBy takes a raw promQL expression, converts the filters into PromQL matchers, and applies these matchers to the parsed expression. It also applies the group by clause to any aggregate expressions in the parsed expression. -func ApplyFiltersAndGroupBy(rawExpr string, scopeFilters, adHocFilters []ScopeFilter, groupBy []string) (string, error) { +func ApplyFiltersAndGroupBy(rawExpr string, scopeFilters, adHocFilters []scope.ScopeFilter, groupBy []string) (string, error) { expr, err := parser.ParseExpr(rawExpr) if err != nil { return "", err @@ -76,7 +77,7 @@ func ApplyFiltersAndGroupBy(rawExpr string, scopeFilters, adHocFilters []ScopeFi return expr.String(), nil } -func FiltersToMatchers(scopeFilters, adhocFilters []ScopeFilter) ([]*labels.Matcher, error) { +func FiltersToMatchers(scopeFilters, adhocFilters []scope.ScopeFilter) ([]*labels.Matcher, error) { filterMap := make(map[string]*labels.Matcher) // scope filters are applied first @@ -115,25 +116,25 @@ func FiltersToMatchers(scopeFilters, adhocFilters []ScopeFilter) ([]*labels.Matc return matchers, nil } -func filterToMatcher(f ScopeFilter) (*labels.Matcher, error) { +func filterToMatcher(f scope.ScopeFilter) (*labels.Matcher, error) { var mt labels.MatchType switch f.Operator { - case FilterOperatorEquals: + case scope.FilterOperatorEquals: mt = labels.MatchEqual - case FilterOperatorNotEquals: + case scope.FilterOperatorNotEquals: mt = labels.MatchNotEqual - case FilterOperatorRegexMatch: + case scope.FilterOperatorRegexMatch: mt = labels.MatchRegexp - case FilterOperatorRegexNotMatch: + case scope.FilterOperatorRegexNotMatch: mt = labels.MatchNotRegexp - case FilterOperatorOneOf: + case scope.FilterOperatorOneOf: mt = labels.MatchRegexp - case FilterOperatorNotOneOf: + case scope.FilterOperatorNotOneOf: mt = labels.MatchNotRegexp default: return nil, fmt.Errorf("unknown operator %q", f.Operator) } - if f.Operator == FilterOperatorOneOf || f.Operator == FilterOperatorNotOneOf { + if f.Operator == scope.FilterOperatorOneOf || f.Operator == scope.FilterOperatorNotOneOf { if len(f.Values) > 0 { return labels.NewMatcher(mt, f.Key, strings.Join(f.Values, "|")) } diff --git a/pkg/promlib/models/scope_test.go b/pkg/promlib/models/scope_test.go index fb6d5f4c1a3..3fa1a7a36e5 100644 --- a/pkg/promlib/models/scope_test.go +++ b/pkg/promlib/models/scope_test.go @@ -3,6 +3,7 @@ package models import ( "testing" + scope "github.com/grafana/grafana/apps/scope/pkg/apis/scope/v0alpha1" "github.com/stretchr/testify/require" ) @@ -10,8 +11,8 @@ func TestApplyQueryFiltersAndGroupBy_Filters(t *testing.T) { tests := []struct { name string query string - adhocFilters []ScopeFilter - scopeFilters []ScopeFilter + adhocFilters []scope.ScopeFilter + scopeFilters []scope.ScopeFilter expected string expectErr bool }{ @@ -30,8 +31,8 @@ func TestApplyQueryFiltersAndGroupBy_Filters(t *testing.T) { { name: "Adhoc filter with existing filter", query: `http_requests_total{job="prometheus"}`, - adhocFilters: []ScopeFilter{ - {Key: "method", Value: "get", Operator: FilterOperatorEquals}, + adhocFilters: []scope.ScopeFilter{ + {Key: "method", Value: "get", Operator: scope.FilterOperatorEquals}, }, expected: `http_requests_total{job="prometheus",method="get"}`, expectErr: false, @@ -39,9 +40,9 @@ func TestApplyQueryFiltersAndGroupBy_Filters(t *testing.T) { { name: "Adhoc filter with no existing filter", query: `http_requests_total`, - adhocFilters: []ScopeFilter{ - {Key: "method", Value: "get", Operator: FilterOperatorEquals}, - {Key: "job", Value: "prometheus", Operator: FilterOperatorEquals}, + adhocFilters: []scope.ScopeFilter{ + {Key: "method", Value: "get", Operator: scope.FilterOperatorEquals}, + {Key: "job", Value: "prometheus", Operator: scope.FilterOperatorEquals}, }, expected: `http_requests_total{job="prometheus",method="get"}`, expectErr: false, @@ -49,8 +50,8 @@ func TestApplyQueryFiltersAndGroupBy_Filters(t *testing.T) { { name: "Scope filter", query: `http_requests_total{job="prometheus"}`, - scopeFilters: []ScopeFilter{ - {Key: "status", Value: "200", Operator: FilterOperatorEquals}, + scopeFilters: []scope.ScopeFilter{ + {Key: "status", Value: "200", Operator: scope.FilterOperatorEquals}, }, expected: `http_requests_total{job="prometheus",status="200"}`, expectErr: false, @@ -58,11 +59,11 @@ func TestApplyQueryFiltersAndGroupBy_Filters(t *testing.T) { { name: "Adhoc and Scope filter no existing filter", query: `http_requests_total`, - scopeFilters: []ScopeFilter{ - {Key: "status", Value: "200", Operator: FilterOperatorEquals}, + scopeFilters: []scope.ScopeFilter{ + {Key: "status", Value: "200", Operator: scope.FilterOperatorEquals}, }, - adhocFilters: []ScopeFilter{ - {Key: "job", Value: "prometheus", Operator: FilterOperatorEquals}, + adhocFilters: []scope.ScopeFilter{ + {Key: "job", Value: "prometheus", Operator: scope.FilterOperatorEquals}, }, expected: `http_requests_total{job="prometheus",status="200"}`, expectErr: false, @@ -70,11 +71,11 @@ func TestApplyQueryFiltersAndGroupBy_Filters(t *testing.T) { { name: "Adhoc and Scope filter conflict - adhoc wins (if not oneOf or notOneOf)", query: `http_requests_total{job="prometheus"}`, - scopeFilters: []ScopeFilter{ - {Key: "status", Value: "404", Operator: FilterOperatorEquals}, + scopeFilters: []scope.ScopeFilter{ + {Key: "status", Value: "404", Operator: scope.FilterOperatorEquals}, }, - adhocFilters: []ScopeFilter{ - {Key: "status", Value: "200", Operator: FilterOperatorEquals}, + adhocFilters: []scope.ScopeFilter{ + {Key: "status", Value: "200", Operator: scope.FilterOperatorEquals}, }, expected: `http_requests_total{job="prometheus",status="200"}`, expectErr: false, @@ -82,8 +83,8 @@ func TestApplyQueryFiltersAndGroupBy_Filters(t *testing.T) { { name: "Adhoc filters with more complex expression", query: `capacity_bytes{job="prometheus"} + available_bytes{job="grafana"} / 1024`, - adhocFilters: []ScopeFilter{ - {Key: "job", Value: "alloy", Operator: FilterOperatorEquals}, + adhocFilters: []scope.ScopeFilter{ + {Key: "job", Value: "alloy", Operator: scope.FilterOperatorEquals}, }, expected: `capacity_bytes{job="alloy"} + available_bytes{job="alloy"} / 1024`, expectErr: false, @@ -91,8 +92,8 @@ func TestApplyQueryFiltersAndGroupBy_Filters(t *testing.T) { { name: "OneOf Operator is combined into a single regex filter", query: `http_requests_total{job="prometheus"}`, - scopeFilters: []ScopeFilter{ - {Key: "status", Values: []string{"404", "400"}, Operator: FilterOperatorOneOf}, + scopeFilters: []scope.ScopeFilter{ + {Key: "status", Values: []string{"404", "400"}, Operator: scope.FilterOperatorOneOf}, }, expected: `http_requests_total{job="prometheus",status=~"404|400"}`, expectErr: false, @@ -100,8 +101,8 @@ func TestApplyQueryFiltersAndGroupBy_Filters(t *testing.T) { { name: "using __name__ as part of the query", query: `{__name__="http_requests_total"}`, - scopeFilters: []ScopeFilter{ - {Key: "namespace", Value: "istio", Operator: FilterOperatorEquals}, + scopeFilters: []scope.ScopeFilter{ + {Key: "namespace", Value: "istio", Operator: scope.FilterOperatorEquals}, }, expected: `{__name__="http_requests_total",namespace="istio"}`, expectErr: false, @@ -109,9 +110,9 @@ func TestApplyQueryFiltersAndGroupBy_Filters(t *testing.T) { { name: "merge scopes filters into using OR if they share filter key", query: `http_requests_total{}`, - scopeFilters: []ScopeFilter{ - {Key: "namespace", Value: "default", Operator: FilterOperatorEquals}, - {Key: "namespace", Value: "kube-system", Operator: FilterOperatorEquals}, + scopeFilters: []scope.ScopeFilter{ + {Key: "namespace", Value: "default", Operator: scope.FilterOperatorEquals}, + {Key: "namespace", Value: "kube-system", Operator: scope.FilterOperatorEquals}, }, expected: `http_requests_total{namespace=~"default|kube-system"}`, expectErr: false, @@ -119,12 +120,12 @@ func TestApplyQueryFiltersAndGroupBy_Filters(t *testing.T) { { name: "adhoc filters win over scope filters if they share filter key", query: `http_requests_total{}`, - scopeFilters: []ScopeFilter{ - {Key: "namespace", Value: "default", Operator: FilterOperatorEquals}, - {Key: "namespace", Value: "kube-system", Operator: FilterOperatorEquals}, + scopeFilters: []scope.ScopeFilter{ + {Key: "namespace", Value: "default", Operator: scope.FilterOperatorEquals}, + {Key: "namespace", Value: "kube-system", Operator: scope.FilterOperatorEquals}, }, - adhocFilters: []ScopeFilter{ - {Key: "namespace", Value: "adhoc-wins", Operator: FilterOperatorEquals}, + adhocFilters: []scope.ScopeFilter{ + {Key: "namespace", Value: "adhoc-wins", Operator: scope.FilterOperatorEquals}, }, expected: `http_requests_total{namespace="adhoc-wins"}`, expectErr: false, @@ -149,8 +150,8 @@ func TestApplyQueryFiltersAndGroupBy_Filters_utf8(t *testing.T) { tests := []struct { name string query string - adhocFilters []ScopeFilter - scopeFilters []ScopeFilter + adhocFilters []scope.ScopeFilter + scopeFilters []scope.ScopeFilter expected string expectErr bool }{ @@ -276,8 +277,8 @@ func TestApplyQueryFiltersAndGroupBy(t *testing.T) { tests := []struct { name string query string - adhocFilters []ScopeFilter - scopeFilters []ScopeFilter + adhocFilters []scope.ScopeFilter + scopeFilters []scope.ScopeFilter groupby []string expected string expectErr bool @@ -286,11 +287,11 @@ func TestApplyQueryFiltersAndGroupBy(t *testing.T) { { name: "Adhoc filters with more complex expression", query: `sum(capacity_bytes{job="prometheus"} + available_bytes{job="grafana"}) / 1024`, - adhocFilters: []ScopeFilter{ - {Key: "job", Value: "alloy", Operator: FilterOperatorEquals}, + adhocFilters: []scope.ScopeFilter{ + {Key: "job", Value: "alloy", Operator: scope.FilterOperatorEquals}, }, - scopeFilters: []ScopeFilter{ - {Key: "vol", Value: "/", Operator: FilterOperatorEquals}, + scopeFilters: []scope.ScopeFilter{ + {Key: "vol", Value: "/", Operator: scope.FilterOperatorEquals}, }, groupby: []string{"job"}, expected: `sum by (job) (capacity_bytes{job="alloy",vol="/"} + available_bytes{job="alloy",vol="/"}) / 1024`, @@ -316,8 +317,8 @@ func TestApplyQueryFiltersAndGroupBy_utf8(t *testing.T) { tests := []struct { name string query string - adhocFilters []ScopeFilter - scopeFilters []ScopeFilter + adhocFilters []scope.ScopeFilter + scopeFilters []scope.ScopeFilter groupby []string expected string expectErr bool @@ -325,11 +326,11 @@ func TestApplyQueryFiltersAndGroupBy_utf8(t *testing.T) { { name: "Adhoc filters with more complex expression and utf8 metric name", query: `sum({"capacity_bytes", job="prometheus"} + {"available_bytes", job="grafana"}) / 1024`, - adhocFilters: []ScopeFilter{ - {Key: "job", Value: "alloy", Operator: FilterOperatorEquals}, + adhocFilters: []scope.ScopeFilter{ + {Key: "job", Value: "alloy", Operator: scope.FilterOperatorEquals}, }, - scopeFilters: []ScopeFilter{ - {Key: "vol", Value: "/", Operator: FilterOperatorEquals}, + scopeFilters: []scope.ScopeFilter{ + {Key: "vol", Value: "/", Operator: scope.FilterOperatorEquals}, }, groupby: []string{"job"}, expected: `sum by (job) ({__name__="capacity_bytes",job="alloy",vol="/"} + {__name__="available_bytes",job="alloy",vol="/"}) / 1024`, @@ -338,11 +339,11 @@ func TestApplyQueryFiltersAndGroupBy_utf8(t *testing.T) { { name: "Adhoc filters with more complex expression with utf8 label", query: `sum(capacity_bytes{job="prometheus", "utf8.label"="value"} + available_bytes{job="grafana"}) / 1024`, - adhocFilters: []ScopeFilter{ - {Key: "job", Value: "alloy", Operator: FilterOperatorEquals}, + adhocFilters: []scope.ScopeFilter{ + {Key: "job", Value: "alloy", Operator: scope.FilterOperatorEquals}, }, - scopeFilters: []ScopeFilter{ - {Key: "vol", Value: "/", Operator: FilterOperatorEquals}, + scopeFilters: []scope.ScopeFilter{ + {Key: "vol", Value: "/", Operator: scope.FilterOperatorEquals}, }, groupby: []string{"job"}, expected: `sum by (job) (capacity_bytes{"utf8.label"="value",job="alloy",vol="/"} + available_bytes{job="alloy",vol="/"}) / 1024`, @@ -351,11 +352,11 @@ func TestApplyQueryFiltersAndGroupBy_utf8(t *testing.T) { { name: "Adhoc filters with more complex expression with utf8 metric and label", query: `sum({"capacity_bytes", job="prometheus", "utf8.label"="value"} + available_bytes{job="grafana"}) / 1024`, - adhocFilters: []ScopeFilter{ - {Key: "job", Value: "alloy", Operator: FilterOperatorEquals}, + adhocFilters: []scope.ScopeFilter{ + {Key: "job", Value: "alloy", Operator: scope.FilterOperatorEquals}, }, - scopeFilters: []ScopeFilter{ - {Key: "vol", Value: "/", Operator: FilterOperatorEquals}, + scopeFilters: []scope.ScopeFilter{ + {Key: "vol", Value: "/", Operator: scope.FilterOperatorEquals}, }, groupby: []string{"job"}, expected: `sum by (job) ({"utf8.label"="value",__name__="capacity_bytes",job="alloy",vol="/"} + available_bytes{job="alloy",vol="/"}) / 1024`, diff --git a/pkg/promlib/resource/resource.go b/pkg/promlib/resource/resource.go index 93846c1074f..c2c6b977b01 100644 --- a/pkg/promlib/resource/resource.go +++ b/pkg/promlib/resource/resource.go @@ -13,6 +13,7 @@ import ( "github.com/grafana/grafana-plugin-sdk-go/backend" "github.com/grafana/grafana-plugin-sdk-go/backend/log" "github.com/grafana/grafana-plugin-sdk-go/data/utils/maputil" + scope "github.com/grafana/grafana/apps/scope/pkg/apis/scope/v0alpha1" "github.com/prometheus/prometheus/promql/parser" "github.com/grafana/grafana/pkg/promlib/client" @@ -116,8 +117,8 @@ type SuggestionRequest struct { Queries []string `json:"queries"` - Scopes []models.ScopeFilter `json:"scopes"` - AdhocFilters []models.ScopeFilter `json:"adhocFilters"` + Scopes []scope.ScopeFilter `json:"scopes"` + AdhocFilters []scope.ScopeFilter `json:"adhocFilters"` // Start and End are proxied directly to the prometheus endpoint (which is rfc3339 | unix_timestamp) Start string `json:"start"` diff --git a/pkg/promlib/resource/resource_test.go b/pkg/promlib/resource/resource_test.go index b569e0004c9..442367fe900 100644 --- a/pkg/promlib/resource/resource_test.go +++ b/pkg/promlib/resource/resource_test.go @@ -11,10 +11,10 @@ import ( "github.com/grafana/grafana-plugin-sdk-go/backend" "github.com/grafana/grafana-plugin-sdk-go/backend/log" + scope "github.com/grafana/grafana/apps/scope/pkg/apis/scope/v0alpha1" "github.com/stretchr/testify/assert" "github.com/stretchr/testify/require" - "github.com/grafana/grafana/pkg/promlib/models" "github.com/grafana/grafana/pkg/promlib/resource" ) @@ -151,11 +151,11 @@ func TestResource_GetSuggestionsWithEmptyQueriesButFilters(t *testing.T) { // Create a request with empty queries but with filters suggestionReq := resource.SuggestionRequest{ Queries: []string{}, // Empty queries - Scopes: []models.ScopeFilter{ - {Key: "job", Operator: models.FilterOperatorEquals, Value: "testjob"}, + Scopes: []scope.ScopeFilter{ + {Key: "job", Operator: scope.FilterOperatorEquals, Value: "testjob"}, }, - AdhocFilters: []models.ScopeFilter{ - {Key: "instance", Operator: models.FilterOperatorEquals, Value: "localhost:9090"}, + AdhocFilters: []scope.ScopeFilter{ + {Key: "instance", Operator: scope.FilterOperatorEquals, Value: "localhost:9090"}, }, } diff --git a/pkg/tsdb/loki/loki.go b/pkg/tsdb/loki/loki.go index 5626ca94ee5..4c07b9e8199 100644 --- a/pkg/tsdb/loki/loki.go +++ b/pkg/tsdb/loki/loki.go @@ -21,8 +21,8 @@ import ( "go.opentelemetry.io/otel/trace" "github.com/grafana/grafana-plugin-sdk-go/backend/httpclient" + scope "github.com/grafana/grafana/apps/scope/pkg/apis/scope/v0alpha1" - "github.com/grafana/grafana/pkg/promlib/models" "github.com/grafana/grafana/pkg/tsdb/loki/kinds/dataquery" ) @@ -73,9 +73,9 @@ type datasourceInfo struct { type QueryJSONModel struct { dataquery.LokiDataQuery - Direction *string `json:"direction,omitempty"` - SupportingQueryType *string `json:"supportingQueryType"` - Scopes []models.ScopeFilter `json:"scopes"` + Direction *string `json:"direction,omitempty"` + SupportingQueryType *string `json:"supportingQueryType"` + Scopes []scope.ScopeFilter `json:"scopes"` } type ResponseOpts struct { diff --git a/pkg/tsdb/loki/scopes.go b/pkg/tsdb/loki/scopes.go index c9f502a780b..2728489d3ff 100644 --- a/pkg/tsdb/loki/scopes.go +++ b/pkg/tsdb/loki/scopes.go @@ -8,6 +8,7 @@ import ( "time" "github.com/grafana/grafana-plugin-sdk-go/backend" + scope "github.com/grafana/grafana/apps/scope/pkg/apis/scope/v0alpha1" "github.com/grafana/grafana/pkg/promlib/models" "github.com/grafana/grafana/pkg/tsdb/loki/kinds/dataquery" "github.com/grafana/loki/v3/pkg/logql/syntax" @@ -21,8 +22,8 @@ type SuggestionRequest struct { Query string `json:"query"` - Scopes []models.ScopeFilter `json:"scopes"` - AdhocFilters []models.ScopeFilter `json:"adhocFilters"` + Scopes []scope.ScopeFilter `json:"scopes"` + AdhocFilters []scope.ScopeFilter `json:"adhocFilters"` // Start and End are proxied directly to the prometheus endpoint (which is rfc3339 | unix_timestamp) Start string `json:"start"` @@ -79,7 +80,7 @@ func GetSuggestions(ctx context.Context, lokiAPI *LokiAPI, req *backend.CallReso } // ApplyScopes applies the given scope filters to the given raw expression. -func ApplyScopes(rawExpr string, scopeFilters []models.ScopeFilter) (string, error) { +func ApplyScopes(rawExpr string, scopeFilters []scope.ScopeFilter) (string, error) { if len(scopeFilters) == 0 { return rawExpr, nil } diff --git a/pkg/tsdb/loki/scopes_test.go b/pkg/tsdb/loki/scopes_test.go index 76c51f0ec73..74c967cadec 100644 --- a/pkg/tsdb/loki/scopes_test.go +++ b/pkg/tsdb/loki/scopes_test.go @@ -3,7 +3,7 @@ package loki import ( "testing" - "github.com/grafana/grafana/pkg/promlib/models" + scope "github.com/grafana/grafana/apps/scope/pkg/apis/scope/v0alpha1" "github.com/stretchr/testify/require" ) @@ -14,7 +14,7 @@ func TestInjectScopesIntoLokiQuery(t *testing.T) { tests := []struct { name string query string - scopeFilters []models.ScopeFilter + scopeFilters []scope.ScopeFilter expected string expectErr bool }{ @@ -33,8 +33,8 @@ func TestInjectScopesIntoLokiQuery(t *testing.T) { { name: "scopes with existing filter", query: `{namespace="default"} |= "an unexpected error"`, - scopeFilters: []models.ScopeFilter{ - {Key: "cluster", Value: "us-central-1", Operator: models.FilterOperatorEquals}, + scopeFilters: []scope.ScopeFilter{ + {Key: "cluster", Value: "us-central-1", Operator: scope.FilterOperatorEquals}, }, expected: `{namespace="default", cluster="us-central-1"} |= "an unexpected error"`, expectErr: false, @@ -42,8 +42,8 @@ func TestInjectScopesIntoLokiQuery(t *testing.T) { { name: "scopes without existing label matchers", query: `{} |= "an unexpected error"`, - scopeFilters: []models.ScopeFilter{ - {Key: "cluster", Value: "us-central-1", Operator: models.FilterOperatorEquals}, + scopeFilters: []scope.ScopeFilter{ + {Key: "cluster", Value: "us-central-1", Operator: scope.FilterOperatorEquals}, }, expected: `{cluster="us-central-1"} |= "an unexpected error"`, expectErr: false, @@ -51,9 +51,9 @@ func TestInjectScopesIntoLokiQuery(t *testing.T) { { name: "scopes with multiple filters", query: `{} |= "an unexpected error"`, - scopeFilters: []models.ScopeFilter{ - {Key: "cluster", Value: "us-central-1", Operator: models.FilterOperatorEquals}, - {Key: "namespace", Value: "default", Operator: models.FilterOperatorEquals}, + scopeFilters: []scope.ScopeFilter{ + {Key: "cluster", Value: "us-central-1", Operator: scope.FilterOperatorEquals}, + {Key: "namespace", Value: "default", Operator: scope.FilterOperatorEquals}, }, expected: `{cluster="us-central-1", namespace="default"} |= "an unexpected error"`, expectErr: false, @@ -61,8 +61,8 @@ func TestInjectScopesIntoLokiQuery(t *testing.T) { { name: "metric query with scopes filters", query: `count_over_time({} |= "error" [1m])`, - scopeFilters: []models.ScopeFilter{ - {Key: "namespace", Value: "default", Operator: models.FilterOperatorEquals}, + scopeFilters: []scope.ScopeFilter{ + {Key: "namespace", Value: "default", Operator: scope.FilterOperatorEquals}, }, expected: `count_over_time({namespace="default"} |= "error"[1m])`, expectErr: false, @@ -70,9 +70,9 @@ func TestInjectScopesIntoLokiQuery(t *testing.T) { { name: "multi range metric query operation", query: `count_over_time({} |= "error" [1m])/count_over_time({} [1m])`, - scopeFilters: []models.ScopeFilter{ - {Key: "cluster", Value: "us-central-1", Operator: models.FilterOperatorEquals}, - {Key: "namespace", Value: "default", Operator: models.FilterOperatorEquals}, + scopeFilters: []scope.ScopeFilter{ + {Key: "cluster", Value: "us-central-1", Operator: scope.FilterOperatorEquals}, + {Key: "namespace", Value: "default", Operator: scope.FilterOperatorEquals}, }, expected: `(count_over_time({cluster="us-central-1", namespace="default"} |= "error"[1m]) / count_over_time({cluster="us-central-1", namespace="default"}[1m]))`, expectErr: false, @@ -80,9 +80,9 @@ func TestInjectScopesIntoLokiQuery(t *testing.T) { { name: "multi range metric query operation with existing label matchers", query: `count_over_time({a="bar"} |= "error" [1m])/count_over_time({a="bar"} [1m])`, - scopeFilters: []models.ScopeFilter{ - {Key: "cluster", Value: "us-central-1", Operator: models.FilterOperatorEquals}, - {Key: "namespace", Value: "default", Operator: models.FilterOperatorEquals}, + scopeFilters: []scope.ScopeFilter{ + {Key: "cluster", Value: "us-central-1", Operator: scope.FilterOperatorEquals}, + {Key: "namespace", Value: "default", Operator: scope.FilterOperatorEquals}, }, expected: `(count_over_time({a="bar", cluster="us-central-1", namespace="default"} |= "error"[1m]) / count_over_time({a="bar", cluster="us-central-1", namespace="default"}[1m]))`, expectErr: false, diff --git a/pkg/tsdb/loki/types.go b/pkg/tsdb/loki/types.go index d6d5d7ce1ca..0d6156ed158 100644 --- a/pkg/tsdb/loki/types.go +++ b/pkg/tsdb/loki/types.go @@ -3,7 +3,8 @@ package loki import ( "time" - "github.com/grafana/grafana/pkg/promlib/models" + scope "github.com/grafana/grafana/apps/scope/pkg/apis/scope/v0alpha1" + "github.com/grafana/grafana/pkg/tsdb/loki/kinds/dataquery" ) @@ -40,5 +41,5 @@ type lokiQuery struct { End time.Time RefID string SupportingQueryType SupportingQueryType - Scopes []models.ScopeFilter + Scopes []scope.ScopeFilter } From 3eadebd950480b4c5e80f84ec5c418eff468ee2e Mon Sep 17 00:00:00 2001 From: Ashley Harrison Date: Thu, 9 Oct 2025 10:09:51 +0100 Subject: [PATCH 028/112] Theme: Add `/theme-playground` route (#111974) * add basic theme-playground page * basic theme playground * generate schema * sort of include the schema * proper json schema * add base theme * bit of tidy up * don't use appEvents.emit * tidy up ThemeDemo * extract translations * add CODEOWNERS --- .github/CODEOWNERS | 1 + package.json | 4 +- packages/grafana-data/src/internal/index.ts | 1 + .../src/components/ThemeDemos/ThemeDemo.tsx | 52 +- packages/grafana-ui/src/internal/index.ts | 1 + project.json | 3 +- public/app/core/utils/ConfigProvider.tsx | 4 + .../app/features/theme-playground/README.md | 3 + .../theme-playground/ThemePlayground.tsx | 160 ++++++ .../theme-playground/schema.generated.json | 511 ++++++++++++++++++ public/app/routes/routes.tsx | 6 + public/locales/en-US/grafana.json | 4 + yarn.lock | 59 +- 13 files changed, 780 insertions(+), 29 deletions(-) create mode 100644 public/app/features/theme-playground/README.md create mode 100644 public/app/features/theme-playground/ThemePlayground.tsx create mode 100644 public/app/features/theme-playground/schema.generated.json diff --git a/.github/CODEOWNERS b/.github/CODEOWNERS index dd7c1d87290..708740f5ce6 100644 --- a/.github/CODEOWNERS +++ b/.github/CODEOWNERS @@ -949,6 +949,7 @@ playwright.storybook.config.ts @grafana/grafana-frontend-platform /public/app/features/serviceaccounts/ @grafana/identity-squad /public/app/features/teams/ @grafana/access-squad /public/app/features/templating/ @grafana/dashboards-squad +/public/app/features/theme-playground/ @grafana/grafana-frontend-platform /public/app/features/trails/ @grafana/observability-metrics /public/app/features/transformers/ @grafana/datapro /public/app/features/transformers/timeSeriesTable/ @grafana/dataviz-squad @grafana/app-o11y-visualizations diff --git a/package.json b/package.json index 3b6c04e0c01..e3614d04384 100644 --- a/package.json +++ b/package.json @@ -62,7 +62,8 @@ "stats": "webpack --mode production --config scripts/webpack/webpack.prod.js --profile --json > compilation-stats.json", "storybook": "yarn workspace @grafana/ui storybook --ci", "storybook:build": "yarn workspace @grafana/ui storybook:build", - "themes-generate": "esbuild --target=es6 ./scripts/cli/generateSassVariableFiles.ts --bundle --platform=node --tsconfig=./scripts/cli/tsconfig.json | node", + "themes-schema": "typescript-json-schema ./tsconfig.json NewThemeOptions --include 'packages/grafana-data/src/themes/createTheme.ts' --out public/app/features/theme-playground/schema.generated.json", + "themes-generate": "yarn themes-schema && esbuild --target=es6 ./scripts/cli/generateSassVariableFiles.ts --bundle --platform=node --tsconfig=./scripts/cli/tsconfig.json | node", "themes:usage": "eslint . --ignore-pattern '*.test.ts*' --ignore-pattern '*.spec.ts*' --cache --plugin '@grafana' --rule '{ @grafana/theme-token-usage: \"error\" }'", "typecheck": "tsc --noEmit && yarn run packages:typecheck", "plugins:build-bundled": "echo 'bundled plugins are no longer supported'", @@ -255,6 +256,7 @@ "ts-jest": "29.4.0", "ts-node": "10.9.2", "typescript": "5.9.2", + "typescript-json-schema": "^0.65.1", "webpack": "5.101.0", "webpack-assets-manifest": "^5.1.0", "webpack-cli": "6.0.1", diff --git a/packages/grafana-data/src/internal/index.ts b/packages/grafana-data/src/internal/index.ts index 8fcb8da4e0d..e2dab753baa 100644 --- a/packages/grafana-data/src/internal/index.ts +++ b/packages/grafana-data/src/internal/index.ts @@ -93,6 +93,7 @@ export { DataTransformerID } from '../transformations/transformers/ids'; export { mergeTransformer } from '../transformations/transformers/merge'; export { getThemeById } from '../themes/registry'; +export * as experimentalThemeDefinitions from '../themes/themeDefinitions'; export { GrafanaEdition } from '../types/config'; export { SIPrefix } from '../valueFormats/symbolFormatters'; diff --git a/packages/grafana-ui/src/components/ThemeDemos/ThemeDemo.tsx b/packages/grafana-ui/src/components/ThemeDemos/ThemeDemo.tsx index 6128326b5e8..36fccb70159 100644 --- a/packages/grafana-ui/src/components/ThemeDemos/ThemeDemo.tsx +++ b/packages/grafana-ui/src/components/ThemeDemos/ThemeDemo.tsx @@ -18,17 +18,20 @@ import { Icon } from '../Icon/Icon'; import { Input } from '../Input/Input'; import { BackgroundColor, BorderColor, Box, BoxShadow } from '../Layout/Box/Box'; import { Stack } from '../Layout/Stack/Stack'; +import { ScrollContainer } from '../ScrollContainer/ScrollContainer'; import { Switch } from '../Switch/Switch'; import { Text, TextProps } from '../Text/Text'; interface DemoBoxProps { bg?: BackgroundColor; border?: BorderColor; + scrollable?: boolean; shadow?: BoxShadow; textColor?: TextProps['color']; } -const DemoBox = ({ bg, border, children, shadow }: React.PropsWithChildren) => { +const DemoBox = ({ bg, border, children, shadow, scrollable }: React.PropsWithChildren) => { + const MaybeScroll = scrollable ? ScrollContainer : React.Fragment; return ( - {children} + {children} ); }; @@ -121,7 +124,7 @@ export const ThemeDemo = () => { - + @@ -134,8 +137,8 @@ export const ThemeDemo = () => { - - + +
@@ -154,8 +157,8 @@ export const ThemeDemo = () => { - -
name
+ +
@@ -229,7 +232,7 @@ export const ThemeDemo = () => { - + {allButtonVariants.map((variant) => ( + {options.map((opt) => ( + + ))} + + + ); +} diff --git a/public/app/features/dashboard-scene/scene/layout-rows/RowsLayoutManager.tsx b/public/app/features/dashboard-scene/scene/layout-rows/RowsLayoutManager.tsx index d021eac7889..fbefcfbbc64 100644 --- a/public/app/features/dashboard-scene/scene/layout-rows/RowsLayoutManager.tsx +++ b/public/app/features/dashboard-scene/scene/layout-rows/RowsLayoutManager.tsx @@ -9,19 +9,25 @@ import { VizPanel, } from '@grafana/scenes'; import { Spec as DashboardV2Spec } from '@grafana/schema/dist/esm/schema/dashboard/v2'; +import appEvents from 'app/core/app_events'; +import { ShowConfirmModalEvent, ShowModalReactEvent } from 'app/types/events'; import { dashboardEditActions, ObjectsReorderedOnCanvasEvent } from '../../edit-pane/shared'; import { serializeRowsLayout } from '../../serialization/layoutSerializers/RowsLayoutSerializer'; import { getDashboardSceneFor } from '../../utils/utils'; +import { AutoGridLayoutManager } from '../layout-auto-grid/AutoGridLayoutManager'; import { DashboardGridItem } from '../layout-default/DashboardGridItem'; import { DefaultGridLayoutManager } from '../layout-default/DefaultGridLayoutManager'; import { RowRepeaterBehavior } from '../layout-default/RowRepeaterBehavior'; import { TabsLayoutManager } from '../layout-tabs/TabsLayoutManager'; +import { findAllGridTypes } from '../layouts-shared/findAllGridTypes'; import { getRowFromClipboard } from '../layouts-shared/paste'; import { generateUniqueTitle, ungroupLayout } from '../layouts-shared/utils'; import { DashboardLayoutManager } from '../types/DashboardLayoutManager'; +import { isLayoutParent } from '../types/LayoutParent'; import { LayoutRegistryItem } from '../types/LayoutRegistryItem'; +import { ConvertMixedGridsModal } from './ConvertMixedGridsModal'; import { RowItem } from './RowItem'; import { RowLayoutManagerRenderer } from './RowsLayoutManagerRenderer'; @@ -29,6 +35,22 @@ interface RowsLayoutManagerState extends SceneObjectState { rows: RowItem[]; } +enum GridLayoutType { + AutoGridLayout = 'AutoGridLayout', + GridLayout = 'GridLayout', +} + +function mapIdToGridLayoutType(id?: string): GridLayoutType | undefined { + switch (id) { + case GridLayoutType.AutoGridLayout: + return GridLayoutType.AutoGridLayout; + case GridLayoutType.GridLayout: + return GridLayoutType.GridLayout; + default: + return undefined; + } +} + export class RowsLayoutManager extends SceneObjectBase implements DashboardLayoutManager { public static Component = RowLayoutManagerRenderer; public readonly isDashboardLayoutManager = true; @@ -128,25 +150,155 @@ export class RowsLayoutManager extends SceneObjectBase i return outlineChildren; } - public removeRow(row: RowItem) { + public convertAllRowsLayouts(gridLayoutType: GridLayoutType) { + for (const row of this.state.rows) { + switch (gridLayoutType) { + case GridLayoutType.AutoGridLayout: + if (!(row.getLayout() instanceof AutoGridLayoutManager)) { + row.switchLayout(AutoGridLayoutManager.createFromLayout(row.getLayout())); + } + break; + case GridLayoutType.GridLayout: + if (!(row.getLayout() instanceof DefaultGridLayoutManager)) { + row.switchLayout(DefaultGridLayoutManager.createFromLayout(row.getLayout())); + } + break; + } + } + } + + public ungroupRows() { + const hasNonGridLayout = this.state.rows.some((row) => !row.getLayout().descriptor.isGridLayout); + const gridTypes = new Set(findAllGridTypes(this)); + + if (hasNonGridLayout) { + appEvents.publish( + new ShowConfirmModalEvent({ + title: t('dashboard.rows-layout.ungroup-nested-title', 'Ungroup nested groups?'), + text: t('dashboard.rows-layout.ungroup-nested-text', 'This will ungroup all nested groups.'), + yesText: t('dashboard.rows-layout.continue', 'Continue'), + noText: t('dashboard.rows-layout.cancel', 'Cancel'), + onConfirm: () => { + if (gridTypes.size > 1) { + requestAnimationFrame(() => { + this._confirmConvertMixedGrids(gridTypes); + }); + } else { + this.wrapUngroupRowsInEdit(mapIdToGridLayoutType(gridTypes.values().next().value)!); + } + }, + }) + ); + return; + } + + if (gridTypes.size > 1) { + this._confirmConvertMixedGrids(gridTypes); + return; + } else { + this.wrapUngroupRowsInEdit(mapIdToGridLayoutType(gridTypes.values().next().value)!); + } + } + + private _confirmConvertMixedGrids(availableIds: Set) { + appEvents.publish( + new ShowModalReactEvent({ + component: ConvertMixedGridsModal, + props: { + availableIds, + onSelect: (id: string) => { + const selected = mapIdToGridLayoutType(id); + if (selected) { + this.wrapUngroupRowsInEdit(selected); + } + }, + }, + }) + ); + } + + private wrapUngroupRowsInEdit(gridLayoutType: GridLayoutType) { + const parent = this.parent; + if (!parent || !isLayoutParent(parent)) { + throw new Error('Ungroup rows failed: parent is not a layout container'); + } + + const previousLayout = this.clone({}); + const scene = getDashboardSceneFor(this); + + dashboardEditActions.edit({ + description: t('dashboard.rows-layout.edit.ungroup-rows', 'Ungroup rows'), + source: scene, + perform: () => { + this._ungroupRows(gridLayoutType); + }, + undo: () => { + parent.switchLayout(previousLayout); + }, + }); + } + + private _ungroupRows(gridLayoutType: GridLayoutType) { + const hasNonGridLayout = this.state.rows.some((row) => !row.getLayout().descriptor.isGridLayout); + + if (hasNonGridLayout) { + for (const row of this.state.rows) { + const layout = row.getLayout(); + if (!layout.descriptor.isGridLayout) { + if (layout instanceof RowsLayoutManager) { + layout._ungroupRows(gridLayoutType); + } else { + throw new Error(`Ungrouping not supported for layout type: ${layout.descriptor.name}`); + } + } + } + } + + this.convertAllRowsLayouts(gridLayoutType); + + const firstRow = this.state.rows[0]; + const firstRowLayout = firstRow.getLayout(); + const otherRows = this.state.rows.slice(1); + + for (const row of otherRows) { + const layout = row.getLayout(); + if (firstRowLayout.merge) { + firstRowLayout.merge(layout); + } else { + throw new Error(`Layout type ${firstRowLayout.descriptor.name} does not support merging`); + } + } + + this.setState({ rows: [firstRow] }); + this.removeRow(firstRow, true); + } + + public removeRow(row: RowItem, skipUndo?: boolean) { // When removing last row replace ourselves with the inner row layout if (this.shouldUngroup()) { - ungroupLayout(this, row.state.layout); + ungroupLayout(this, row.state.layout, skipUndo ?? false); return; } const indexOfRowToRemove = this.state.rows.findIndex((r) => r === row); - dashboardEditActions.removeElement({ - removedObject: row, - source: this, - perform: () => this.setState({ rows: this.state.rows.filter((r) => r !== row) }), - undo: () => { - const rows = [...this.state.rows]; - rows.splice(indexOfRowToRemove, 0, row); - this.setState({ rows }); - }, - }); + const perform = () => this.setState({ rows: this.state.rows.filter((r) => r !== row) }); + const undo = () => { + const rows = [...this.state.rows]; + rows.splice(indexOfRowToRemove, 0, row); + this.setState({ rows }); + }; + + if (skipUndo) { + perform(); + } else { + dashboardEditActions.removeElement({ + removedObject: row, + source: this, + perform, + undo, + }); + } } public moveRow(_rowKey: string, fromIndex: number, toIndex: number) { @@ -212,7 +364,7 @@ export class RowsLayoutManager extends SceneObjectBase i if (child instanceof SceneGridRow) { // Skip repeated row clones - if (child.state.repeatSourceKey) { + if ('repeatSourceKey' in child.state && child.state.repeatSourceKey) { return; } diff --git a/public/app/features/dashboard-scene/scene/layout-rows/RowsLayoutManagerRenderer.tsx b/public/app/features/dashboard-scene/scene/layout-rows/RowsLayoutManagerRenderer.tsx index b18634bdd00..d7de6c42413 100644 --- a/public/app/features/dashboard-scene/scene/layout-rows/RowsLayoutManagerRenderer.tsx +++ b/public/app/features/dashboard-scene/scene/layout-rows/RowsLayoutManagerRenderer.tsx @@ -53,6 +53,9 @@ export function RowLayoutManagerRenderer({ model }: SceneComponentProps + diff --git a/public/app/features/dashboard-scene/scene/layouts-shared/DashboardLayoutSelector.tsx b/public/app/features/dashboard-scene/scene/layouts-shared/DashboardLayoutSelector.tsx index 125bd380256..f4714e7d420 100644 --- a/public/app/features/dashboard-scene/scene/layouts-shared/DashboardLayoutSelector.tsx +++ b/public/app/features/dashboard-scene/scene/layouts-shared/DashboardLayoutSelector.tsx @@ -1,10 +1,12 @@ import { useCallback, useMemo } from 'react'; import { t } from '@grafana/i18n'; +import { config } from '@grafana/runtime'; import { RadioButtonGroup, Box } from '@grafana/ui'; import { OptionsPaneCategoryDescriptor } from 'app/features/dashboard/components/PanelEditor/OptionsPaneCategoryDescriptor'; import { OptionsPaneItemDescriptor } from 'app/features/dashboard/components/PanelEditor/OptionsPaneItemDescriptor'; +import { TabsLayoutManager } from '../layout-tabs/TabsLayoutManager'; import { DashboardLayoutManager } from '../types/DashboardLayoutManager'; import { isLayoutParent } from '../types/LayoutParent'; import { LayoutRegistryItem } from '../types/LayoutRegistryItem'; @@ -19,6 +21,21 @@ export function DashboardLayoutSelector({ layoutManager }: Props) { const isGridLayout = layoutManager.descriptor.isGridLayout; const options = layoutRegistry.list().filter((layout) => layout.isGridLayout === isGridLayout); + const disableTabs = useMemo(() => { + if (config.featureToggles.unlimitedLayoutsNesting) { + return false; + } + let parent = layoutManager.parent; + while (parent) { + if (parent instanceof TabsLayoutManager) { + return true; + } + parent = parent.parent; + } + + return false; + }, [layoutManager]); + const onChangeLayout = useCallback( (newLayout: LayoutRegistryItem) => { const layoutParent = layoutManager.parent; @@ -30,17 +47,33 @@ export function DashboardLayoutSelector({ layoutManager }: Props) { [layoutManager] ); - const radioOptions = options.map((opt) => ({ - value: opt, - label: opt.name, - icon: opt.icon, - description: opt.description, - ariaLabel: `layout-selection-option-${opt.name}`, - })); + const disabledOptions: LayoutRegistryItem[] = []; + + const radioOptions = options.map((opt) => { + let description = opt.description; + if (disableTabs && opt.id === TabsLayoutManager.descriptor.id) { + description = t('dashboard.canvas-actions.disabled-nested-tabs', 'Tabs cannot be nested inside other tabs'); + disabledOptions.push(opt); + } + + return { + value: opt, + label: opt.name, + icon: opt.icon, + description, + ariaLabel: `layout-selection-option-${opt.name}`, + }; + }); return ( - + ); } diff --git a/public/locales/en-US/grafana.json b/public/locales/en-US/grafana.json index 865e80a7934..189ec56399c 100644 --- a/public/locales/en-US/grafana.json +++ b/public/locales/en-US/grafana.json @@ -4496,6 +4496,8 @@ }, "canvas-actions": { "add-panel": "Add panel", + "disabled-nested-grouping": "Grouping is limited to 2 levels", + "disabled-nested-tabs": "Tabs cannot be nested inside other tabs", "group-into-row": "Group into row", "group-into-tab": "Group into tab", "group-panels": "Group panels", From e05cfb676af09414696eceebe95a8d61da08ccc2 Mon Sep 17 00:00:00 2001 From: "grafana-pr-automation[bot]" <140550294+grafana-pr-automation[bot]@users.noreply.github.com> Date: Fri, 10 Oct 2025 12:22:42 +0000 Subject: [PATCH 064/112] I18n: Download translations from Crowdin (#112251) New Crowdin translations by GitHub Action Co-authored-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com> --- public/locales/cs-CZ/grafana.json | 38 +++++++++++++++++------------ public/locales/de-DE/grafana.json | 38 +++++++++++++++++------------ public/locales/es-ES/grafana.json | 38 +++++++++++++++++------------ public/locales/fr-FR/grafana.json | 38 +++++++++++++++++------------ public/locales/hu-HU/grafana.json | 38 +++++++++++++++++------------ public/locales/id-ID/grafana.json | 38 +++++++++++++++++------------ public/locales/it-IT/grafana.json | 38 +++++++++++++++++------------ public/locales/ja-JP/grafana.json | 38 +++++++++++++++++------------ public/locales/ko-KR/grafana.json | 38 +++++++++++++++++------------ public/locales/nl-NL/grafana.json | 38 +++++++++++++++++------------ public/locales/pl-PL/grafana.json | 38 +++++++++++++++++------------ public/locales/pt-BR/grafana.json | 38 +++++++++++++++++------------ public/locales/pt-PT/grafana.json | 38 +++++++++++++++++------------ public/locales/ru-RU/grafana.json | 38 +++++++++++++++++------------ public/locales/sv-SE/grafana.json | 38 +++++++++++++++++------------ public/locales/tr-TR/grafana.json | 38 +++++++++++++++++------------ public/locales/zh-Hans/grafana.json | 38 +++++++++++++++++------------ public/locales/zh-Hant/grafana.json | 38 +++++++++++++++++------------ 18 files changed, 414 insertions(+), 270 deletions(-) diff --git a/public/locales/cs-CZ/grafana.json b/public/locales/cs-CZ/grafana.json index 816818e14df..bde778d53e0 100644 --- a/public/locales/cs-CZ/grafana.json +++ b/public/locales/cs-CZ/grafana.json @@ -27,12 +27,12 @@ }, "permissions": { "add-label": "Přidat oprávnění", + "loading": "", "no-permissions": "Nejsou k dispozici žádná oprávnění", "permissions-change-warning": "Tímto změníte oprávnění pro tuto složku a všechny následné složky. Celkově to ovlivní:", "role": "Role", "serviceaccount": "Účet služby", "team": "Tým", - "title": "Oprávnění", "user": "Uživatel" } }, @@ -3528,7 +3528,7 @@ "delete-button": "Odstranit", "delete-modal-invalid-text": "Jedna nebo více složek obsahuje panely knihovny nebo pravidla výstrah. Chcete-li pokračovat, nejprve je odstraňte.", "delete-modal-invalid-title": "Složku nelze odstranit", - "delete-modal-restore-dashboards-text": "Tato akce okamžitě odstraní vybrané složky, ale vybrané nástěnky budou označeny k odstranění za 30 dnů. Správce organizace může obnovit nástěnky kdykoli před uplynutím 30 dnů. Složky nelze obnovit.", + "delete-modal-restore-dashboards-text": "", "delete-modal-text": "Tato akce odstraní následující obsah:", "delete-modal-title": "Odstranit", "delete-provisioned-folder": "Odstranit poskytnutou složku", @@ -4612,7 +4612,7 @@ "unsupported-object-type": "" }, "overlay": { - "tooltip": "Prvek je skrytý kvůli podmíněnému vykreslení." + "tooltip": "" }, "root": { "title": "Zobrazit/skrýt pravidla" @@ -5861,6 +5861,7 @@ "dynamically-switch-source-multiple-panels": "Dynamicky přepínat zdroj dat pro více panelů", "group": "Přidat klíče do skupiny za chodu", "hidden-constant-variable": "Skrytá konstantní proměnná, užitečná pro metrické předpony na nástěnkách, které chcete sdílet", + "users-enter-arbitrary-strings-switch": "", "users-enter-arbitrary-strings-textbox": "Uživatelé mohou do textového pole zadat libovolné řetězce znaků", "values-are-static-and-defined-manually": "Hodnoty jsou statické a definované ručně", "values-fetched-source-query": "Hodnoty jsou načteny z dotazu na zdroj dat", @@ -5874,6 +5875,7 @@ "group-by": "Seskupit podle", "interval": "Interval", "query": "Dotaz", + "switch": "", "textbox": "Textové pole" } }, @@ -6222,6 +6224,17 @@ "copy-to-clipboard-failed": "Kopírování do schránky se nezdařilo" } }, + "switch-variable-form": { + "disabled-value": "", + "disabled-value-description": "", + "disabled-value-placeholder": "", + "enabled-value": "", + "enabled-value-description": "", + "enabled-value-placeholder": "", + "switch-options": "", + "value-pair-type": "", + "value-pair-type-description": "" + }, "text-box-variable": { "name-default-value": "Výchozí hodnota" }, @@ -10417,7 +10430,7 @@ "title": "Profily" }, "recently-deleted": { - "subtitle": "Všechny tady uvedené položky po dobu delší než 30 dnů budou automaticky odstraněny.", + "subtitle": "", "title": "Nedávno odstraněné" }, "recorded-queries": { @@ -11651,6 +11664,7 @@ "active-jobs": "aktivní práce", "column-action": "Akce", "column-duration": "Doba trvání", + "column-job-id": "", "column-message": "Zpráva", "column-started": "Zahájeno", "column-status": "Stav", @@ -11669,12 +11683,6 @@ "settings": "Nastavení", "view": "Zobrazit" }, - "repository-health": { - "details": "Podrobnosti:", - "no-errors-found": "Nebyly nalezeny žádné chyby", - "title-repository-is-healthy": "Úložiště je zdravé", - "title-repository-is-unhealthy": "Úložiště je nezdravé" - }, "repository-link": { "delete-or-move-job": { "compare-branch": "Porovnat větev", @@ -11689,7 +11697,7 @@ }, "repository-overview": { "checked": "Zkontrolováno:", - "finished": "Dokončeno:", + "finished": "", "health": "Kondice", "healthy": "Zdravý", "job-id": "ID úkolu:", @@ -11698,7 +11706,6 @@ "not-available": "Nelze aplikovat", "pull-status": "Stav stažení", "resources": "Zdroje", - "started": "Zahájeno:", "status": "Stav:", "unhealthy": "Nezdravý", "view-folder": "Zobrazit složku", @@ -12364,9 +12371,6 @@ "tokens": "Tokeny", "tooltip-managed-service-account-cannot-modified": "Toto je spravovaný účet služby a nelze ho upravit" }, - "service-account-permissions": { - "title-permissions": "Oprávnění" - }, "service-account-profile": { "information": "Informace", "label-creation-date": "Datum vytvoření", @@ -13045,6 +13049,10 @@ "name-show-line-numbers": "Zobrazovat čísla řádek", "name-show-mini-map": "Zobrazit mini mapu" }, + "theme-playground": { + "label-base-theme": "", + "title": "" + }, "theme-preview": { "breadcrumbs": { "dashboards": "Nástěnky", diff --git a/public/locales/de-DE/grafana.json b/public/locales/de-DE/grafana.json index af72317801a..1cb6f9fc68f 100644 --- a/public/locales/de-DE/grafana.json +++ b/public/locales/de-DE/grafana.json @@ -27,12 +27,12 @@ }, "permissions": { "add-label": "Berechtigung hinzufügen", + "loading": "", "no-permissions": "Keine Berechtigungen vorhanden", "permissions-change-warning": "Dadurch werden die Berechtigungen für diesen Ordner und alle seine Unterordner geändert. Insgesamt betrifft dies Folgendes:", "role": "Rolle", "serviceaccount": "Service-Konto", "team": "Team", - "title": "Berechtigung", "user": "Nutzer" } }, @@ -3504,7 +3504,7 @@ "delete-button": "Löschen", "delete-modal-invalid-text": "Ein oder mehrere Ordner enthalten Bibliotheksfenster oder Alarmierungsregeln. Löschen Sie diese zuerst, um fortzufahren.", "delete-modal-invalid-title": "Ordner kann nicht gelöscht werden", - "delete-modal-restore-dashboards-text": "Durch diese Aktion werden die ausgewählten Ordner sofort gelöscht, aber die ausgewählten Dashboards werden innerhalb von 30 Tagen zur Löschung markiert. Der Administrator Ihrer Organisation kann die Dashboards jederzeit vor Ablauf der 30 Tage wiederherstellen. Ordner können nicht wiederhergestellt werden.", + "delete-modal-restore-dashboards-text": "", "delete-modal-text": "Diese Aktion wird folgenden Inhalt löschen:", "delete-modal-title": "Löschen", "delete-provisioned-folder": "Bereitgestellten Ordner löschen", @@ -4572,7 +4572,7 @@ "unsupported-object-type": "" }, "overlay": { - "tooltip": "Das Element wird aufgrund von bedingtem Rendering ausgeblendet." + "tooltip": "" }, "root": { "title": "Regeln ein-/ausblenden" @@ -5819,6 +5819,7 @@ "dynamically-switch-source-multiple-panels": "Dynamisches Wechseln der Datenquelle für mehrere Panels", "group": "Fügen Sie spontan Schlüssel zum Gruppieren hinzu", "hidden-constant-variable": "Eine ausgeblendete konstante Variable, nützlich für metrische Präfixe in Dashboards, die Sie teilen möchten", + "users-enter-arbitrary-strings-switch": "", "users-enter-arbitrary-strings-textbox": "Nutzer können beliebige Zeichenfolgen in ein Textfeld eingeben", "values-are-static-and-defined-manually": "Werte sind statisch und manuell festgelegt", "values-fetched-source-query": "Werte werden über eine Datenquellenabfrage abgerufen", @@ -5832,6 +5833,7 @@ "group-by": "Gruppieren nach", "interval": "Intervall", "query": "Abfrage", + "switch": "", "textbox": "Textfeld" } }, @@ -6176,6 +6178,17 @@ "copy-to-clipboard-failed": "Kopieren in die Zwischenablage fehlgeschlagen" } }, + "switch-variable-form": { + "disabled-value": "", + "disabled-value-description": "", + "disabled-value-placeholder": "", + "enabled-value": "", + "enabled-value-description": "", + "enabled-value-placeholder": "", + "switch-options": "", + "value-pair-type": "", + "value-pair-type-description": "" + }, "text-box-variable": { "name-default-value": "Standardwert" }, @@ -10355,7 +10368,7 @@ "title": "Profile" }, "recently-deleted": { - "subtitle": "Alle hier länger als 30 Tage aufgeführten Elemente werden automatisch gelöscht.", + "subtitle": "", "title": "Kürzlich gelöscht" }, "recorded-queries": { @@ -11571,6 +11584,7 @@ "active-jobs": "Aktive Aufträge", "column-action": "Aktion", "column-duration": "Dauer", + "column-job-id": "", "column-message": "Nachricht", "column-started": "Gestartet", "column-status": "Status", @@ -11589,12 +11603,6 @@ "settings": "Einstellungen", "view": "Anzeigen" }, - "repository-health": { - "details": "Details:", - "no-errors-found": "Keine Fehler gefunden", - "title-repository-is-healthy": "Repository in Ordnung", - "title-repository-is-unhealthy": "Repository nicht in Ordnung" - }, "repository-link": { "delete-or-move-job": { "compare-branch": "Branch vergleichen", @@ -11609,7 +11617,7 @@ }, "repository-overview": { "checked": "Geprüft:", - "finished": "Abgeschlossen:", + "finished": "", "health": "Zustand", "healthy": "Guter Zustand", "job-id": "Auftrags-ID:", @@ -11618,7 +11626,6 @@ "not-available": "N/A", "pull-status": "Pull-Status", "resources": "Ressourcen", - "started": "Gestartet:", "status": "Status:", "unhealthy": "Schlechter Zustand", "view-folder": "Ordner anzeigen", @@ -12278,9 +12285,6 @@ "tokens": "Tokens", "tooltip-managed-service-account-cannot-modified": "Dies ist ein verwaltetes Dienstkonto und kann nicht geändert werden" }, - "service-account-permissions": { - "title-permissions": "Berechtigungen" - }, "service-account-profile": { "information": "Informationen", "label-creation-date": "Erstellungsdatum", @@ -12955,6 +12959,10 @@ "name-show-line-numbers": "Zeilennummern anzeigen", "name-show-mini-map": "Minikarte anzeigen" }, + "theme-playground": { + "label-base-theme": "", + "title": "" + }, "theme-preview": { "breadcrumbs": { "dashboards": "Dashboards", diff --git a/public/locales/es-ES/grafana.json b/public/locales/es-ES/grafana.json index f839e66622d..2779072b696 100644 --- a/public/locales/es-ES/grafana.json +++ b/public/locales/es-ES/grafana.json @@ -27,12 +27,12 @@ }, "permissions": { "add-label": "Añadir un permiso", + "loading": "", "no-permissions": "No hay ningún permiso", "permissions-change-warning": "Esto cambiará los permisos para esta carpeta y todos sus descendientes. En total, esto afectará:", "role": "Rol", "serviceaccount": "Cuenta de servicio", "team": "Equipo", - "title": "Permisos", "user": "Usuario" } }, @@ -3504,7 +3504,7 @@ "delete-button": "Eliminar", "delete-modal-invalid-text": "Una o más carpetas contienen paneles de biblioteca o reglas de alerta. Para continuar, primero elimínelas.", "delete-modal-invalid-title": "No se puede eliminar la carpeta", - "delete-modal-restore-dashboards-text": "Esta acción eliminará las carpetas seleccionadas de inmediato, pero los paneles de control seleccionados se marcarán para su eliminación en 30 días. El administrador de tu organización puede restaurar los paneles de control en cualquier momento antes de que transcurran los 30 días. Las carpetas no se pueden restaurar.", + "delete-modal-restore-dashboards-text": "", "delete-modal-text": "Esta acción eliminará el siguiente contenido:", "delete-modal-title": "Eliminar", "delete-provisioned-folder": "Eliminar carpeta aprovisionada", @@ -4572,7 +4572,7 @@ "unsupported-object-type": "" }, "overlay": { - "tooltip": "El elemento está oculto debido al renderizado condicional." + "tooltip": "" }, "root": { "title": "Mostrar/ocultar reglas" @@ -5819,6 +5819,7 @@ "dynamically-switch-source-multiple-panels": "Cambiar dinámicamente la fuente de datos para varios paneles", "group": "Añadir claves para agrupar sobre la marcha", "hidden-constant-variable": "Una variable constante oculta, útil para prefijos métricos en dashboards que quieras compartir", + "users-enter-arbitrary-strings-switch": "", "users-enter-arbitrary-strings-textbox": "Los usuarios pueden introducir cualquier cadena arbitraria en un cuadro de texto", "values-are-static-and-defined-manually": "Los valores son estáticos y se definen manualmente", "values-fetched-source-query": "Los valores se obtienen de una consulta de fuente de datos", @@ -5832,6 +5833,7 @@ "group-by": "Agrupar por", "interval": "Intervalo", "query": "Consulta", + "switch": "", "textbox": "Cuadro de texto" } }, @@ -6176,6 +6178,17 @@ "copy-to-clipboard-failed": "Error al copiar al portapapeles" } }, + "switch-variable-form": { + "disabled-value": "", + "disabled-value-description": "", + "disabled-value-placeholder": "", + "enabled-value": "", + "enabled-value-description": "", + "enabled-value-placeholder": "", + "switch-options": "", + "value-pair-type": "", + "value-pair-type-description": "" + }, "text-box-variable": { "name-default-value": "Valor predeterminado" }, @@ -10355,7 +10368,7 @@ "title": "Perfiles" }, "recently-deleted": { - "subtitle": "Cualquier elemento que aparezca aquí durante más de 30 días se eliminará automáticamente.", + "subtitle": "", "title": "Eliminados recientemente" }, "recorded-queries": { @@ -11571,6 +11584,7 @@ "active-jobs": "trabajos activos", "column-action": "Acción", "column-duration": "Duración", + "column-job-id": "", "column-message": "Mensaje", "column-started": "Iniciados", "column-status": "Estado", @@ -11589,12 +11603,6 @@ "settings": "Configuración", "view": "Vista" }, - "repository-health": { - "details": "Detalles:", - "no-errors-found": "No se han encontrado errores", - "title-repository-is-healthy": "El repositorio está en buen estado", - "title-repository-is-unhealthy": "El repositorio no está en buen estado" - }, "repository-link": { "delete-or-move-job": { "compare-branch": "Comparar rama", @@ -11609,7 +11617,7 @@ }, "repository-overview": { "checked": "Revisado:", - "finished": "Finalizado:", + "finished": "", "health": "Salud", "healthy": "Lineas", "job-id": "ID del trabajo:", @@ -11618,7 +11626,6 @@ "not-available": "N/D", "pull-status": "Estado de extracción", "resources": "Recursos", - "started": "Inicio:", "status": "Estado:", "unhealthy": "En mal estado", "view-folder": "Ver carpeta", @@ -12278,9 +12285,6 @@ "tokens": "Tokens", "tooltip-managed-service-account-cannot-modified": "Esta es una cuenta de servicio gestionada y no se puede modificar" }, - "service-account-permissions": { - "title-permissions": "Permisos" - }, "service-account-profile": { "information": "Información", "label-creation-date": "Fecha de creación", @@ -12955,6 +12959,10 @@ "name-show-line-numbers": "Mostrar números de línea", "name-show-mini-map": "Mostrar minimapa" }, + "theme-playground": { + "label-base-theme": "", + "title": "" + }, "theme-preview": { "breadcrumbs": { "dashboards": "Paneles de control", diff --git a/public/locales/fr-FR/grafana.json b/public/locales/fr-FR/grafana.json index 11ba3782df3..07be7a535f3 100644 --- a/public/locales/fr-FR/grafana.json +++ b/public/locales/fr-FR/grafana.json @@ -27,12 +27,12 @@ }, "permissions": { "add-label": "Ajouter une autorisation", + "loading": "", "no-permissions": "Il n'y a aucune autorisation", "permissions-change-warning": "Cette action modifiera les autorisations pour ce dossier et tous ses descendants. Au total, elle affectera :", "role": "Rôle", "serviceaccount": "Compte de service", "team": "Équipe", - "title": "Autorisations", "user": "Utilisateur" } }, @@ -3504,7 +3504,7 @@ "delete-button": "Supprimer", "delete-modal-invalid-text": "Un ou plusieurs dossiers contiennent des panneaux Bibliothèque ou des règles d'alerte. Vous devez d'abord les supprimer.", "delete-modal-invalid-title": "Impossible de supprimer le dossier", - "delete-modal-restore-dashboards-text": "Cette action supprimera immédiatement les dossiers sélectionnés, mais les tableaux de bord sélectionnés seront marqués pour suppression dans 30 jours. L'administrateur de votre organisation peut restaurer les tableaux de bord à tout moment avant l'expiration des 30 jours. Les dossiers ne peuvent pas être restaurés.", + "delete-modal-restore-dashboards-text": "", "delete-modal-text": "Cette action supprimera le contenu suivant :", "delete-modal-title": "Supprimer", "delete-provisioned-folder": "Supprimer le dossier provisionné", @@ -4572,7 +4572,7 @@ "unsupported-object-type": "" }, "overlay": { - "tooltip": "L’élément est masqué en raison du rendu conditionnel." + "tooltip": "" }, "root": { "title": "Afficher / masquer les règles" @@ -5819,6 +5819,7 @@ "dynamically-switch-source-multiple-panels": "Changer dynamiquement la source de données pour plusieurs panneaux", "group": "Ajouter des clés de regroupement à la volée", "hidden-constant-variable": "Une variable constante masquée, utile pour les préfixes de métriques dans des tableaux de bord à partager", + "users-enter-arbitrary-strings-switch": "", "users-enter-arbitrary-strings-textbox": "Les utilisateurs peuvent saisir des chaînes de caractères libres dans un champ de texte", "values-are-static-and-defined-manually": "Les valeurs sont statiques et définies manuellement", "values-fetched-source-query": "Les valeurs sont récupérées à partir d’une requête de source de données", @@ -5832,6 +5833,7 @@ "group-by": "Regrouper par", "interval": "Intervalle", "query": "Requête", + "switch": "", "textbox": "Zone de texte" } }, @@ -6176,6 +6178,17 @@ "copy-to-clipboard-failed": "Échec de la copie dans le presse-papiers" } }, + "switch-variable-form": { + "disabled-value": "", + "disabled-value-description": "", + "disabled-value-placeholder": "", + "enabled-value": "", + "enabled-value-description": "", + "enabled-value-placeholder": "", + "switch-options": "", + "value-pair-type": "", + "value-pair-type-description": "" + }, "text-box-variable": { "name-default-value": "Valeur par défaut" }, @@ -10355,7 +10368,7 @@ "title": "Profils" }, "recently-deleted": { - "subtitle": "Tous les éléments répertoriés ici depuis plus de 30 jours seront automatiquement supprimés.", + "subtitle": "", "title": "Récemment supprimé" }, "recorded-queries": { @@ -11571,6 +11584,7 @@ "active-jobs": "missions actives", "column-action": "Action", "column-duration": "Durée", + "column-job-id": "", "column-message": "Message", "column-started": "Démarré", "column-status": "Statut", @@ -11589,12 +11603,6 @@ "settings": "Paramètres", "view": "Afficher" }, - "repository-health": { - "details": "Détails :", - "no-errors-found": "Aucune erreur trouvée", - "title-repository-is-healthy": "Le référentiel est en bon état", - "title-repository-is-unhealthy": "Le référentiel est en mauvais état" - }, "repository-link": { "delete-or-move-job": { "compare-branch": "Comparer une branche", @@ -11609,7 +11617,7 @@ }, "repository-overview": { "checked": "Vérifié :", - "finished": "Fini :", + "finished": "", "health": "Santé", "healthy": "Sain", "job-id": "ID de mission :", @@ -11618,7 +11626,6 @@ "not-available": "S/O", "pull-status": "Statut de fusion", "resources": "Ressources", - "started": "Démarré :", "status": "Statut :", "unhealthy": "Non sain", "view-folder": "Afficher le dossier", @@ -12278,9 +12285,6 @@ "tokens": "Jetons", "tooltip-managed-service-account-cannot-modified": "Il s’agit d’un compte de service géré qui ne peut pas être modifié" }, - "service-account-permissions": { - "title-permissions": "Autorisations" - }, "service-account-profile": { "information": "Information", "label-creation-date": "Date de création", @@ -12955,6 +12959,10 @@ "name-show-line-numbers": "Afficher les numéros de ligne", "name-show-mini-map": "Afficher la mini-carte" }, + "theme-playground": { + "label-base-theme": "", + "title": "" + }, "theme-preview": { "breadcrumbs": { "dashboards": "Tableaux de bord", diff --git a/public/locales/hu-HU/grafana.json b/public/locales/hu-HU/grafana.json index 272fdfb1280..68c80aae7d7 100644 --- a/public/locales/hu-HU/grafana.json +++ b/public/locales/hu-HU/grafana.json @@ -27,12 +27,12 @@ }, "permissions": { "add-label": "Engedély hozzáadása", + "loading": "", "no-permissions": "Nincsenek engedélyek", "permissions-change-warning": "Ez módosítja ennek a mappának és minden almappájának az engedélyeit. Összességében ez a következőket érinti:", "role": "Szerepkör", "serviceaccount": "Szolgáltatási fiók", "team": "Csapat", - "title": "Engedélyek", "user": "Felhasználó" } }, @@ -3504,7 +3504,7 @@ "delete-button": "Törlés", "delete-modal-invalid-text": "Egy vagy több mappa könyvtárpaneleket vagy riasztási szabályokat tartalmaz. A folytatáshoz először törölje ezeket.", "delete-modal-invalid-title": "Nem lehet törölni a mappát", - "delete-modal-restore-dashboards-text": "Ez a művelet azonnal törli a kijelölt mappákat, de a kijelölt irányítópultok 30 napon belüli törlésre lesznek megjelölve. A szervezeti rendszergazda a 30 nap lejárta előtt bármikor visszaállíthatja az irányítópultokat. A mappák nem állíthatók vissza.", + "delete-modal-restore-dashboards-text": "", "delete-modal-text": "Ez a művelet törli a következő tartalmat:", "delete-modal-title": "Törlés", "delete-provisioned-folder": "Kiépített mappa törlése", @@ -4572,7 +4572,7 @@ "unsupported-object-type": "" }, "overlay": { - "tooltip": "Az elem rejtve van a feltételes renderelés miatt." + "tooltip": "" }, "root": { "title": "Szabályok megjelenítése/elrejtése" @@ -5819,6 +5819,7 @@ "dynamically-switch-source-multiple-panels": "Adatforrás dinamikus váltása több panelhez", "group": "Csoportosítási kulcsok hozzáadása menet közben", "hidden-constant-variable": "Rejtett konstans változó, mely hasznos metrikai előtagokhoz a megosztani kívánt irányítópultokon", + "users-enter-arbitrary-strings-switch": "", "users-enter-arbitrary-strings-textbox": "A felhasználók tetszőleges szöveget írhatnak be egy szövegmezőbe", "values-are-static-and-defined-manually": "Az értékek statikusak, és manuálisan vannak megadva", "values-fetched-source-query": "Az értékek egy adatforrás-lekérdezéséből származnak", @@ -5832,6 +5833,7 @@ "group-by": "Csoportosítási szempont", "interval": "Intervallum", "query": "Lekérdezés", + "switch": "", "textbox": "Szövegdoboz" } }, @@ -6176,6 +6178,17 @@ "copy-to-clipboard-failed": "Nem sikerült a vágólapra másolás" } }, + "switch-variable-form": { + "disabled-value": "", + "disabled-value-description": "", + "disabled-value-placeholder": "", + "enabled-value": "", + "enabled-value-description": "", + "enabled-value-placeholder": "", + "switch-options": "", + "value-pair-type": "", + "value-pair-type-description": "" + }, "text-box-variable": { "name-default-value": "Alapértelmezett érték" }, @@ -10355,7 +10368,7 @@ "title": "Profilok" }, "recently-deleted": { - "subtitle": "Az itt 30 napnál hosszabb ideig felsorolt elemek automatikusan törlődnek.", + "subtitle": "", "title": "Nemrég törölt" }, "recorded-queries": { @@ -11571,6 +11584,7 @@ "active-jobs": "aktív feladatok", "column-action": "Művelet", "column-duration": "Időtartam", + "column-job-id": "", "column-message": "Üzenet", "column-started": "Elindítva", "column-status": "Állapot", @@ -11589,12 +11603,6 @@ "settings": "Beállítások", "view": "Nézet" }, - "repository-health": { - "details": "Részletek:", - "no-errors-found": "Nem található hiba", - "title-repository-is-healthy": "Az adattár állapota megfelelő", - "title-repository-is-unhealthy": "Az adattár állapota nem megfelelő" - }, "repository-link": { "delete-or-move-job": { "compare-branch": "Ág összehasonlítása", @@ -11609,7 +11617,7 @@ }, "repository-overview": { "checked": "Ellenőrzés:", - "finished": "Befejezés:", + "finished": "", "health": "Állapot", "healthy": "Megfelelő állapotú", "job-id": "Feladatazonosító:", @@ -11618,7 +11626,6 @@ "not-available": "n.a.", "pull-status": "Lekérés állapota", "resources": "Erőforrások", - "started": "Kezdet:", "status": "Állapot:", "unhealthy": "Nem megfelelő állapotú", "view-folder": "Mappa megtekintése", @@ -12278,9 +12285,6 @@ "tokens": "Tokenek", "tooltip-managed-service-account-cannot-modified": "Ez egy felügyelt szolgáltatási fiók, és nem módosítható" }, - "service-account-permissions": { - "title-permissions": "Engedélyek" - }, "service-account-profile": { "information": "Információ", "label-creation-date": "Létrehozás dátuma", @@ -12955,6 +12959,10 @@ "name-show-line-numbers": "Sorszámozás megjelenítése", "name-show-mini-map": "Minitérkép megjelenítése" }, + "theme-playground": { + "label-base-theme": "", + "title": "" + }, "theme-preview": { "breadcrumbs": { "dashboards": "Irányítópultok", diff --git a/public/locales/id-ID/grafana.json b/public/locales/id-ID/grafana.json index 76151588b12..2e42bd349b5 100644 --- a/public/locales/id-ID/grafana.json +++ b/public/locales/id-ID/grafana.json @@ -27,12 +27,12 @@ }, "permissions": { "add-label": "Tambahkan izin", + "loading": "", "no-permissions": "Tidak ada izin", "permissions-change-warning": "Ini akan mengubah izin untuk folder ini dan semua turunannya. Secara keseluruhan, ini akan memengaruhi:", "role": "Peran", "serviceaccount": "Akun Layanan", "team": "Tim", - "title": "Izin", "user": "Pengguna" } }, @@ -3492,7 +3492,7 @@ "delete-button": "Hapus", "delete-modal-invalid-text": "Satu atau beberapa folder berisi panel pustaka atau aturan peringatan. Hapus ini terlebih dahulu untuk melanjutkan.", "delete-modal-invalid-title": "Tidak dapat menghapus folder", - "delete-modal-restore-dashboards-text": "Tindakan ini akan segera menghapus folder yang dipilih tetapi dasbor yang dipilih akan ditandai untuk dihapus dalam 30 hari. Administrator organisasi Anda dapat memulihkan dasbor kapan saja sebelum 30 hari kedaluwarsa. Folder tidak dapat dipulihkan.", + "delete-modal-restore-dashboards-text": "", "delete-modal-text": "Tindakan ini akan menghapus konten berikut:", "delete-modal-title": "Hapus", "delete-provisioned-folder": "Hapus folder yang disediakan", @@ -4552,7 +4552,7 @@ "unsupported-object-type": "" }, "overlay": { - "tooltip": "Elemen disembunyikan karena rendering bersyarat." + "tooltip": "" }, "root": { "title": "Tampilkan/sembunyikan aturan" @@ -5798,6 +5798,7 @@ "dynamically-switch-source-multiple-panels": "Beralih sumber data secara dinamis untuk beberapa panel", "group": "Tambahkan kunci ke grup dengan cepat", "hidden-constant-variable": "Variabel konstan tersembunyi, berguna untuk awalan metrik di dasbor yang ingin Anda bagikan", + "users-enter-arbitrary-strings-switch": "", "users-enter-arbitrary-strings-textbox": "Pengguna dapat memasukkan string acak apa pun dalam kotak teks", "values-are-static-and-defined-manually": "Nilai bersifat statis dan ditentukan secara manual", "values-fetched-source-query": "Nilai diambil dari kueri sumber data", @@ -5811,6 +5812,7 @@ "group-by": "Kelompokkan berdasarkan", "interval": "Interval", "query": "Kueri", + "switch": "", "textbox": "Kotak teks" } }, @@ -6153,6 +6155,17 @@ "copy-to-clipboard-failed": "Gagal menyalin ke papan klip" } }, + "switch-variable-form": { + "disabled-value": "", + "disabled-value-description": "", + "disabled-value-placeholder": "", + "enabled-value": "", + "enabled-value-description": "", + "enabled-value-placeholder": "", + "switch-options": "", + "value-pair-type": "", + "value-pair-type-description": "" + }, "text-box-variable": { "name-default-value": "Nilai default" }, @@ -10324,7 +10337,7 @@ "title": "Profil" }, "recently-deleted": { - "subtitle": "Setiap item yang tercantum di sini selama lebih dari 30 hari akan dihapus secara otomatis.", + "subtitle": "", "title": "Baru dihapus" }, "recorded-queries": { @@ -11531,6 +11544,7 @@ "active-jobs": "pekerjaan aktif", "column-action": "Tindakan", "column-duration": "Durasi", + "column-job-id": "", "column-message": "Pesan", "column-started": "Dimulai", "column-status": "Status", @@ -11549,12 +11563,6 @@ "settings": "Pengaturan", "view": "Lihat" }, - "repository-health": { - "details": "Detail:", - "no-errors-found": "Tidak ada kesalahan yang ditemukan", - "title-repository-is-healthy": "Repositori sehat", - "title-repository-is-unhealthy": "Repositori tidak sehat" - }, "repository-link": { "delete-or-move-job": { "compare-branch": "Bandingkan cabang", @@ -11569,7 +11577,7 @@ }, "repository-overview": { "checked": "Diperiksa:", - "finished": "Selesai:", + "finished": "", "health": "Kesehatan", "healthy": "Sehat", "job-id": "ID Pekerjaan:", @@ -11578,7 +11586,6 @@ "not-available": "N/A", "pull-status": "Status pull", "resources": "Sumber Daya", - "started": "Dimulai:", "status": "Status:", "unhealthy": "Tidak Sehat", "view-folder": "Lihat Folder", @@ -12235,9 +12242,6 @@ "tokens": "Token", "tooltip-managed-service-account-cannot-modified": "Ini adalah akun layanan terkelola dan tidak dapat dimodifikasi" }, - "service-account-permissions": { - "title-permissions": "Izin" - }, "service-account-profile": { "information": "Informasi", "label-creation-date": "Tanggal pembuatan", @@ -12910,6 +12914,10 @@ "name-show-line-numbers": "Tampilkan nomor baris", "name-show-mini-map": "Tampilkan peta mini" }, + "theme-playground": { + "label-base-theme": "", + "title": "" + }, "theme-preview": { "breadcrumbs": { "dashboards": "Dasbor", diff --git a/public/locales/it-IT/grafana.json b/public/locales/it-IT/grafana.json index 70dc218a0bc..bd1b1e48532 100644 --- a/public/locales/it-IT/grafana.json +++ b/public/locales/it-IT/grafana.json @@ -27,12 +27,12 @@ }, "permissions": { "add-label": "Aggiungi un'autorizzazione", + "loading": "", "no-permissions": "Non sono presenti autorizzazioni", "permissions-change-warning": "Questa operazione modificherà le autorizzazioni per questa cartella e tutti i suoi discendenti. In totale, questo influenzerà:", "role": "Ruolo", "serviceaccount": "Account del servizio", "team": "Team", - "title": "Autorizzazioni", "user": "Utente" } }, @@ -3504,7 +3504,7 @@ "delete-button": "Elimina", "delete-modal-invalid-text": "Una o più cartelle contengono pannelli della libreria o regole di avviso. Eliminarli prima di procedere.", "delete-modal-invalid-title": "Impossibile eliminare la cartella", - "delete-modal-restore-dashboards-text": "Questa azione eliminerà immediatamente le cartelle selezionate, ma i dashboard selezionati verranno contrassegnati per l'eliminazione tra 30 giorni. L'amministratore dell'organizzazione può ripristinare i dashboard in qualsiasi momento prima della scadenza dei 30 giorni. Non è possibile ripristinare le cartelle.", + "delete-modal-restore-dashboards-text": "", "delete-modal-text": "Questa azione eliminerà il seguente contenuto:", "delete-modal-title": "Elimina", "delete-provisioned-folder": "Elimina cartella fornita", @@ -4572,7 +4572,7 @@ "unsupported-object-type": "" }, "overlay": { - "tooltip": "L'elemento è nascosto a causa del rendering condizionale." + "tooltip": "" }, "root": { "title": "Mostra/nascondi regole" @@ -5819,6 +5819,7 @@ "dynamically-switch-source-multiple-panels": "Cambia dinamicamente l'origine dati per più pannelli", "group": "Aggiungi chiavi per raggruppare al volo", "hidden-constant-variable": "Una variabile costante nascosta, utile per i prefissi metrici nelle dashboard che desideri condividere", + "users-enter-arbitrary-strings-switch": "", "users-enter-arbitrary-strings-textbox": "Gli utenti possono inserire qualsiasi stringa arbitraria in una casella di testo", "values-are-static-and-defined-manually": "I valori sono statici e definiti manualmente", "values-fetched-source-query": "I valori vengono recuperati da una query dell'origine dati", @@ -5832,6 +5833,7 @@ "group-by": "Raggruppa per", "interval": "Intervallo", "query": "Query", + "switch": "", "textbox": "Casella di testo" } }, @@ -6176,6 +6178,17 @@ "copy-to-clipboard-failed": "Copia negli appunti non riuscita" } }, + "switch-variable-form": { + "disabled-value": "", + "disabled-value-description": "", + "disabled-value-placeholder": "", + "enabled-value": "", + "enabled-value-description": "", + "enabled-value-placeholder": "", + "switch-options": "", + "value-pair-type": "", + "value-pair-type-description": "" + }, "text-box-variable": { "name-default-value": "Valore predefinito" }, @@ -10355,7 +10368,7 @@ "title": "Profili" }, "recently-deleted": { - "subtitle": "Tutti gli elementi elencati qui per più di 30 giorni verranno eliminati automaticamente.", + "subtitle": "", "title": "Eliminati di recente" }, "recorded-queries": { @@ -11571,6 +11584,7 @@ "active-jobs": "attività attive", "column-action": "Azione", "column-duration": "Durata", + "column-job-id": "", "column-message": "Messaggio", "column-started": "Iniziata", "column-status": "Stato", @@ -11589,12 +11603,6 @@ "settings": "Impostazioni", "view": "Visualizza" }, - "repository-health": { - "details": "Dettagli:", - "no-errors-found": "Nessun errore trovato", - "title-repository-is-healthy": "Il repository è integro", - "title-repository-is-unhealthy": "Il repository non è integro" - }, "repository-link": { "delete-or-move-job": { "compare-branch": "Confronta settore", @@ -11609,7 +11617,7 @@ }, "repository-overview": { "checked": "Verificato:", - "finished": "Terminato:", + "finished": "", "health": "Stato", "healthy": "Correttamente funzionante", "job-id": "ID attività:", @@ -11618,7 +11626,6 @@ "not-available": "N/D", "pull-status": "Stato del pull", "resources": "Risorse", - "started": "Iniziato:", "status": "Stato:", "unhealthy": "Malfunzionante", "view-folder": "Visualizza cartella", @@ -12278,9 +12285,6 @@ "tokens": "Token", "tooltip-managed-service-account-cannot-modified": "Questo è un account di servizio gestito e non può essere modificato" }, - "service-account-permissions": { - "title-permissions": "Autorizzazioni" - }, "service-account-profile": { "information": "Informazioni", "label-creation-date": "Data di creazione", @@ -12955,6 +12959,10 @@ "name-show-line-numbers": "Mostra numeri di riga", "name-show-mini-map": "Mostra mini mappa" }, + "theme-playground": { + "label-base-theme": "", + "title": "" + }, "theme-preview": { "breadcrumbs": { "dashboards": "Dashboard", diff --git a/public/locales/ja-JP/grafana.json b/public/locales/ja-JP/grafana.json index aa633bbf3ad..ff9721e1255 100644 --- a/public/locales/ja-JP/grafana.json +++ b/public/locales/ja-JP/grafana.json @@ -27,12 +27,12 @@ }, "permissions": { "add-label": "権限を追加する", + "loading": "", "no-permissions": "権限がありません", "permissions-change-warning": "これにより、このフォルダとそのすべての子孫のアクセス許可が変更されます。合計で、これは次のように影響します:", "role": "役割", "serviceaccount": "サービスアカウント", "team": "チーム", - "title": "権限", "user": "ユーザー" } }, @@ -3492,7 +3492,7 @@ "delete-button": "削除", "delete-modal-invalid-text": "1つ以上のフォルダにライブラリパネルまたはアラートルールが含まれています。続行するには、まずこれらを削除してください。", "delete-modal-invalid-title": "フォルダを削除できません", - "delete-modal-restore-dashboards-text": "このアクションにより、選択したフォルダはすぐに削除されますが、選択したダッシュボードは30日後に削除されます。組織の管理者は、30日が経過する前であればいつでもダッシュボードを復元できます。フォルダは復元できません。", + "delete-modal-restore-dashboards-text": "", "delete-modal-text": "この操作により、次のコンテンツが削除されます:", "delete-modal-title": "削除", "delete-provisioned-folder": "プロビジョニングされたフォルダを削除", @@ -4552,7 +4552,7 @@ "unsupported-object-type": "" }, "overlay": { - "tooltip": "条件付きレンダリング設定のため、要素は非表示になっています。" + "tooltip": "" }, "root": { "title": "表示/非表示ルール" @@ -5798,6 +5798,7 @@ "dynamically-switch-source-multiple-panels": "複数のパネルのデータソースを動的に切り替える", "group": "即時にグループ化キーを追加", "hidden-constant-variable": "共有するダッシュボードのメトリックプレフィックスに便利な非表示の定数変数", + "users-enter-arbitrary-strings-switch": "", "users-enter-arbitrary-strings-textbox": "ユーザーはテキストボックスに任意の文字列を入力できます", "values-are-static-and-defined-manually": "値はスタティックであり、手動で定義されます", "values-fetched-source-query": "値はデータソースクエリから取得されます", @@ -5811,6 +5812,7 @@ "group-by": "グループ化", "interval": "間隔", "query": "クエリ", + "switch": "", "textbox": "テキストボックス" } }, @@ -6153,6 +6155,17 @@ "copy-to-clipboard-failed": "クリップボードへのコピーが失敗しました" } }, + "switch-variable-form": { + "disabled-value": "", + "disabled-value-description": "", + "disabled-value-placeholder": "", + "enabled-value": "", + "enabled-value-description": "", + "enabled-value-placeholder": "", + "switch-options": "", + "value-pair-type": "", + "value-pair-type-description": "" + }, "text-box-variable": { "name-default-value": "既定値" }, @@ -10324,7 +10337,7 @@ "title": "プロフィール" }, "recently-deleted": { - "subtitle": "ここに30日以上リストされているアイテムはすべて自動的に削除されます。", + "subtitle": "", "title": "最近削除された" }, "recorded-queries": { @@ -11531,6 +11544,7 @@ "active-jobs": "アクティブなジョブ", "column-action": "操作", "column-duration": "継続時間", + "column-job-id": "", "column-message": "メッセージ", "column-started": "開始済み", "column-status": "ステータス", @@ -11549,12 +11563,6 @@ "settings": "設定", "view": "表示" }, - "repository-health": { - "details": "詳細:", - "no-errors-found": "エラーは見つかりませんでした", - "title-repository-is-healthy": "リポジトリは正常です", - "title-repository-is-unhealthy": "リポジトリに問題があります" - }, "repository-link": { "delete-or-move-job": { "compare-branch": "ブランチを比較", @@ -11569,7 +11577,7 @@ }, "repository-overview": { "checked": "確認済み:", - "finished": "終了:", + "finished": "", "health": "健康", "healthy": "正常", "job-id": "ジョブID:", @@ -11578,7 +11586,6 @@ "not-available": "該当なし", "pull-status": "プル状態", "resources": "リソース", - "started": "開始:", "status": "ステータス:", "unhealthy": "異常", "view-folder": "フォルダを表示", @@ -12235,9 +12242,6 @@ "tokens": "トークン", "tooltip-managed-service-account-cannot-modified": "これは管理対象サービスアカウントであり、変更できません" }, - "service-account-permissions": { - "title-permissions": "権限" - }, "service-account-profile": { "information": "情報", "label-creation-date": "作成日", @@ -12910,6 +12914,10 @@ "name-show-line-numbers": "行番号を表示", "name-show-mini-map": "ミニマップを表示" }, + "theme-playground": { + "label-base-theme": "", + "title": "" + }, "theme-preview": { "breadcrumbs": { "dashboards": "ダッシュボード", diff --git a/public/locales/ko-KR/grafana.json b/public/locales/ko-KR/grafana.json index 8c22a2d056f..f7ad0f896c4 100644 --- a/public/locales/ko-KR/grafana.json +++ b/public/locales/ko-KR/grafana.json @@ -27,12 +27,12 @@ }, "permissions": { "add-label": "권한 추가", + "loading": "", "no-permissions": "권한 없음", "permissions-change-warning": "이 작업을 수행하면 이 폴더와 모든 하위 폴더에 대한 권한이 변경됩니다. 이는 전체적으로 다음에 영향을 미칩니다.", "role": "역할", "serviceaccount": "서비스 계정", "team": "팀", - "title": "권한", "user": "사용자" } }, @@ -3492,7 +3492,7 @@ "delete-button": "삭제", "delete-modal-invalid-text": "하나 이상의 폴더에 라이브러리 패널 또는 경고 규칙이 포함되어 있습니다. 계속하려면 먼저 이 항목들을 삭제하세요.", "delete-modal-invalid-title": "폴더를 삭제할 수 없음", - "delete-modal-restore-dashboards-text": "이 작업을 수행하면 선택한 폴더가 즉시 삭제되지만 선택한 대시보드는 30일 후에 삭제 대상으로 표시됩니다. 조직 관리자는 30일이 만료되기 전에 언제든지 대시보드를 복구할 수 있습니다. 폴더를 복구할 수 없습니다.", + "delete-modal-restore-dashboards-text": "", "delete-modal-text": "이 작업을 수행하면 다음 콘텐츠가 삭제됩니다.", "delete-modal-title": "삭제", "delete-provisioned-folder": "프로비저닝된 폴더 삭제", @@ -4552,7 +4552,7 @@ "unsupported-object-type": "" }, "overlay": { - "tooltip": "조건부 렌더링으로 인해 요소가 숨겨져 있습니다." + "tooltip": "" }, "root": { "title": "규칙 표시 / 숨기기" @@ -5798,6 +5798,7 @@ "dynamically-switch-source-multiple-panels": "여러 패널의 데이터 소스를 동적으로 전환합니다", "group": "실시간으로 그룹화에 사용할 키를 추가합니다", "hidden-constant-variable": "공유하려는 대시보드의 메트릭 접두사에 유용한, 숨겨진 상수형 변수", + "users-enter-arbitrary-strings-switch": "", "users-enter-arbitrary-strings-textbox": "사용자는 텍스트 상자에 임의의 문자열을 입력할 수 있습니다", "values-are-static-and-defined-manually": "값은 정적이며 수동으로 정의됩니다", "values-fetched-source-query": "값은 데이터 소스 쿼리에서 가져옵니다", @@ -5811,6 +5812,7 @@ "group-by": "그룹화 기준", "interval": "간격", "query": "쿼리", + "switch": "", "textbox": "텍스트 상자" } }, @@ -6153,6 +6155,17 @@ "copy-to-clipboard-failed": "클립보드로 복사 실패" } }, + "switch-variable-form": { + "disabled-value": "", + "disabled-value-description": "", + "disabled-value-placeholder": "", + "enabled-value": "", + "enabled-value-description": "", + "enabled-value-placeholder": "", + "switch-options": "", + "value-pair-type": "", + "value-pair-type-description": "" + }, "text-box-variable": { "name-default-value": "기본값" }, @@ -10324,7 +10337,7 @@ "title": "프로필" }, "recently-deleted": { - "subtitle": "여기에 30일 이상 기재된 모든 항목이 자동으로 삭제됩니다.", + "subtitle": "", "title": "최근에 삭제됨" }, "recorded-queries": { @@ -11531,6 +11544,7 @@ "active-jobs": "활성 작업", "column-action": "동작", "column-duration": "지속 시간", + "column-job-id": "", "column-message": "메시지", "column-started": "시작됨", "column-status": "상태", @@ -11549,12 +11563,6 @@ "settings": "설정", "view": "보기" }, - "repository-health": { - "details": "세부 정보:", - "no-errors-found": "오류를 찾을 수 없습니다", - "title-repository-is-healthy": "리포지토리 상태가 좋습니다", - "title-repository-is-unhealthy": "리포지토리 상태가 좋지 않습니다" - }, "repository-link": { "delete-or-move-job": { "compare-branch": "브랜치 비교", @@ -11569,7 +11577,7 @@ }, "repository-overview": { "checked": "확인됨:", - "finished": "완료됨:", + "finished": "", "health": "상태", "healthy": "좋은 상태", "job-id": "작업 ID:", @@ -11578,7 +11586,6 @@ "not-available": "해당 없음", "pull-status": "풀 상태", "resources": "리소스", - "started": "시작됨:", "status": "상태:", "unhealthy": "좋지 않은 상태", "view-folder": "폴더 보기", @@ -12235,9 +12242,6 @@ "tokens": "토큰", "tooltip-managed-service-account-cannot-modified": "관리되는 서비스 계정이므로 수정할 수 없습니다" }, - "service-account-permissions": { - "title-permissions": "권한" - }, "service-account-profile": { "information": "정보", "label-creation-date": "생성일", @@ -12910,6 +12914,10 @@ "name-show-line-numbers": " 번호 표시", "name-show-mini-map": "미니 지도 표시" }, + "theme-playground": { + "label-base-theme": "", + "title": "" + }, "theme-preview": { "breadcrumbs": { "dashboards": "대시보드", diff --git a/public/locales/nl-NL/grafana.json b/public/locales/nl-NL/grafana.json index 6210cb1355e..b1c96c42c8b 100644 --- a/public/locales/nl-NL/grafana.json +++ b/public/locales/nl-NL/grafana.json @@ -27,12 +27,12 @@ }, "permissions": { "add-label": "Een toestemming toevoegen", + "loading": "", "no-permissions": "Er zijn geen toestemmingen", "permissions-change-warning": "Dit wijzigt de toestemmingen voor deze map en alle onderliggende mappen. In totaal heeft dit invloed op:", "role": "Rol", "serviceaccount": "Serviceaccount", "team": "Team", - "title": "Toestemmingen", "user": "Gebruiker" } }, @@ -3504,7 +3504,7 @@ "delete-button": "Verwijderen", "delete-modal-invalid-text": "Een of meer mappen bevatten bibliotheekpanelen of waarschuwingsregels. Verwijder deze eerst om verder te gaan.", "delete-modal-invalid-title": "Kan de map niet verwijderen", - "delete-modal-restore-dashboards-text": "Deze actie verwijdert de geselecteerde mappen onmiddellijk, maar de geselecteerde dashboards worden gemarkeerd voor verwijdering over 30 dagen. Je organisatiebeheerder kan de dashboards op elk moment herstellen voordat de 30 dagen verlopen. Mappen kunnen niet worden hersteld.", + "delete-modal-restore-dashboards-text": "", "delete-modal-text": "Met deze actie wordt de volgende inhoud verwijderd:", "delete-modal-title": "Verwijderen", "delete-provisioned-folder": "Geprovisioneerde map verwijderen", @@ -4572,7 +4572,7 @@ "unsupported-object-type": "" }, "overlay": { - "tooltip": "Element is verborgen vanwege voorwaardelijke weergave." + "tooltip": "" }, "root": { "title": "Regels tonen / verbergen" @@ -5819,6 +5819,7 @@ "dynamically-switch-source-multiple-panels": "Schakel dynamisch tussen gegevensbronnen voor meerdere panelen", "group": "Voeg direct sleutels toe om te groeperen", "hidden-constant-variable": "Een verborgen constante variabele, handig voor metrische voorvoegsels in dashboards die je wilt delen", + "users-enter-arbitrary-strings-switch": "", "users-enter-arbitrary-strings-textbox": "Gebruikers kunnen willekeurige strings invoeren in een tekstvak", "values-are-static-and-defined-manually": "Waarden zijn statisch en handmatig gedefinieerd", "values-fetched-source-query": "Waarden worden opgehaald uit een gegevensbronquery", @@ -5832,6 +5833,7 @@ "group-by": "Groeperen op", "interval": "Interval", "query": "Query", + "switch": "", "textbox": "Tekstvak" } }, @@ -6176,6 +6178,17 @@ "copy-to-clipboard-failed": "Kopiëren naar klembord mislukt" } }, + "switch-variable-form": { + "disabled-value": "", + "disabled-value-description": "", + "disabled-value-placeholder": "", + "enabled-value": "", + "enabled-value-description": "", + "enabled-value-placeholder": "", + "switch-options": "", + "value-pair-type": "", + "value-pair-type-description": "" + }, "text-box-variable": { "name-default-value": "Standaardwaarde" }, @@ -10355,7 +10368,7 @@ "title": "Profielen" }, "recently-deleted": { - "subtitle": "Alle items die hier langer dan 30 dagen worden vermeld, worden automatisch verwijderd.", + "subtitle": "", "title": "Onlangs verwijderd" }, "recorded-queries": { @@ -11571,6 +11584,7 @@ "active-jobs": "actieve taken", "column-action": "Actie", "column-duration": "Duur", + "column-job-id": "", "column-message": "Bericht", "column-started": "Gestart", "column-status": "Status", @@ -11589,12 +11603,6 @@ "settings": "Instellingen", "view": "Weergave" }, - "repository-health": { - "details": "Details:", - "no-errors-found": "Geen fouten gevonden", - "title-repository-is-healthy": "Repository is gezond", - "title-repository-is-unhealthy": "Repository is ongezond" - }, "repository-link": { "delete-or-move-job": { "compare-branch": "Tak vergelijken", @@ -11609,7 +11617,7 @@ }, "repository-overview": { "checked": "Gecontroleerd:", - "finished": "Beëindigd:", + "finished": "", "health": "Gezondheid", "healthy": "Gezond", "job-id": "Taak-id:", @@ -11618,7 +11626,6 @@ "not-available": "N.v.t.", "pull-status": "Pull-status", "resources": "Bronnen", - "started": "Begonnen:", "status": "Status:", "unhealthy": "Ongezond", "view-folder": "Map bekijken", @@ -12278,9 +12285,6 @@ "tokens": "Tokens", "tooltip-managed-service-account-cannot-modified": "Dit is een beheerd serviceaccount dat niet kan worden gewijzigd" }, - "service-account-permissions": { - "title-permissions": "Toestemmingen" - }, "service-account-profile": { "information": "Informatie", "label-creation-date": "Aanmaakdatum", @@ -12955,6 +12959,10 @@ "name-show-line-numbers": "Regelnummers tonen", "name-show-mini-map": "Minikaart weergeven" }, + "theme-playground": { + "label-base-theme": "", + "title": "" + }, "theme-preview": { "breadcrumbs": { "dashboards": "Dashboards", diff --git a/public/locales/pl-PL/grafana.json b/public/locales/pl-PL/grafana.json index f7ef9d2d7af..5ba95448729 100644 --- a/public/locales/pl-PL/grafana.json +++ b/public/locales/pl-PL/grafana.json @@ -27,12 +27,12 @@ }, "permissions": { "add-label": "Dodaj uprawnienie", + "loading": "", "no-permissions": "Brak uprawnień", "permissions-change-warning": "Spowoduje to zmianę uprawnień dla tego folderu i elementów po nim dziedziczących. Łącznie wpłynie to na następujące elementy:", "role": "Rola", "serviceaccount": "Konto usługi", "team": "Zespół", - "title": "Uprawnienia", "user": "Użytkownik" } }, @@ -3528,7 +3528,7 @@ "delete-button": "Usuń", "delete-modal-invalid-text": "Co najmniej jeden folder zawiera panele bibliotek lub reguły alertów. Usuń je, aby kontynuować.", "delete-modal-invalid-title": "Nie można usunąć folderu", - "delete-modal-restore-dashboards-text": "To działanie spowoduje natychmiastowe usunięcie wybranych folderów, ale wybrane pulpity zostaną oznaczone do usunięcia za 30 dni. Administrator organizacji może przywrócić pulpity w dowolnym momencie przed upływem 30 dni. Nie można przywrócić folderów.", + "delete-modal-restore-dashboards-text": "", "delete-modal-text": "To działanie spowoduje usunięcie następującej zawartości:", "delete-modal-title": "Usuń", "delete-provisioned-folder": "Usuń przydzielony folder", @@ -4612,7 +4612,7 @@ "unsupported-object-type": "" }, "overlay": { - "tooltip": "Element jest ukryty z powodu renderowania warunkowego." + "tooltip": "" }, "root": { "title": "Pokaż/ukryj reguły" @@ -5861,6 +5861,7 @@ "dynamically-switch-source-multiple-panels": "Dynamiczne przełączanie źródła danych dla wielu paneli", "group": "Dodaj klucze do grupowania w czasie rzeczywistym", "hidden-constant-variable": "Ukryta zmienna stała, przydatna do prefiksów metrycznych w przypadku pulpitów, które chcesz udostępnić", + "users-enter-arbitrary-strings-switch": "", "users-enter-arbitrary-strings-textbox": "Użytkownicy mogą wprowadzać dowolne ciągi w polu tekstowym", "values-are-static-and-defined-manually": "Wartości są statyczne i definiowane ręcznie", "values-fetched-source-query": "Wartości są pobierane z zapytania dotyczącego źródła danych", @@ -5874,6 +5875,7 @@ "group-by": "Grupuj według", "interval": "Odstęp czasu", "query": "Zapytanie", + "switch": "", "textbox": "Pole tekstowe" } }, @@ -6222,6 +6224,17 @@ "copy-to-clipboard-failed": "Nie udało się skopiować do schowka" } }, + "switch-variable-form": { + "disabled-value": "", + "disabled-value-description": "", + "disabled-value-placeholder": "", + "enabled-value": "", + "enabled-value-description": "", + "enabled-value-placeholder": "", + "switch-options": "", + "value-pair-type": "", + "value-pair-type-description": "" + }, "text-box-variable": { "name-default-value": "Domyślna wartość" }, @@ -10417,7 +10430,7 @@ "title": "Profile" }, "recently-deleted": { - "subtitle": "Wszystkie pozycje wypisane tutaj przez ponad 30 dni zostaną automatycznie usunięte.", + "subtitle": "", "title": "Ostatnio usunięte" }, "recorded-queries": { @@ -11651,6 +11664,7 @@ "active-jobs": "aktywne zadania", "column-action": "Działanie", "column-duration": "Czas trwania", + "column-job-id": "", "column-message": "Wiadomość", "column-started": "Rozpoczęto", "column-status": "Status", @@ -11669,12 +11683,6 @@ "settings": "Ustawienia", "view": "Wyświetl" }, - "repository-health": { - "details": "Szczegóły:", - "no-errors-found": "Nie znaleziono błędów", - "title-repository-is-healthy": "Repozytorium jest sprawne", - "title-repository-is-unhealthy": "Repozytorium jest niesprawne" - }, "repository-link": { "delete-or-move-job": { "compare-branch": "Porównaj gałąź", @@ -11689,7 +11697,7 @@ }, "repository-overview": { "checked": "Sprawdzone:", - "finished": "Zakończone:", + "finished": "", "health": "Zdrowie", "healthy": "Sprawne", "job-id": "Identyfikator zadania:", @@ -11698,7 +11706,6 @@ "not-available": "Niedostępne", "pull-status": "Status pull", "resources": "Zasoby", - "started": "Rozpoczęte:", "status": "Status:", "unhealthy": "Niesprawne", "view-folder": "Wyświetl folder", @@ -12364,9 +12371,6 @@ "tokens": "Tokeny", "tooltip-managed-service-account-cannot-modified": "To jest zarządzane konto usługi i nie można go zmodyfikować" }, - "service-account-permissions": { - "title-permissions": "Uprawnienia" - }, "service-account-profile": { "information": "Informacje", "label-creation-date": "Data utworzenia", @@ -13045,6 +13049,10 @@ "name-show-line-numbers": "Pokaż numerację wierszy", "name-show-mini-map": "Pokaż minimapę" }, + "theme-playground": { + "label-base-theme": "", + "title": "" + }, "theme-preview": { "breadcrumbs": { "dashboards": "Pulpity", diff --git a/public/locales/pt-BR/grafana.json b/public/locales/pt-BR/grafana.json index 1aa3306ea04..1f4d97d21f4 100644 --- a/public/locales/pt-BR/grafana.json +++ b/public/locales/pt-BR/grafana.json @@ -27,12 +27,12 @@ }, "permissions": { "add-label": "Adicionar uma permissão", + "loading": "", "no-permissions": "Não há permissões", "permissions-change-warning": "Isto irá alterar as permissões para este diretório e todos os seus descendentes. No total, isto afetará:", "role": "Função", "serviceaccount": "Conta de serviço", "team": "Equipe", - "title": "Permissões", "user": "Usuário" } }, @@ -3504,7 +3504,7 @@ "delete-button": "Excluir", "delete-modal-invalid-text": "Uma ou mais pastas contêm painéis de biblioteca, ou regras de alerta. Exclua-as primeiro para prosseguir.", "delete-modal-invalid-title": "Não é possível excluir pasta", - "delete-modal-restore-dashboards-text": "Esta ação excluirá as pastas selecionadas imediatamente, mas os painéis de controle selecionados serão marcados para exclusão em 30 dias. O administrador da sua organização pode restaurar os painéis de controle a qualquer momento antes do término do prazo de 30 dias. As pastas não podem ser restauradas.", + "delete-modal-restore-dashboards-text": "", "delete-modal-text": "Esta ação apagará o seguinte conteúdo:", "delete-modal-title": "Excluir", "delete-provisioned-folder": "Excluir pasta provisionada", @@ -4572,7 +4572,7 @@ "unsupported-object-type": "" }, "overlay": { - "tooltip": "O elemento está oculto devido à renderização condicional." + "tooltip": "" }, "root": { "title": "Exibir/Ocultar regras" @@ -5819,6 +5819,7 @@ "dynamically-switch-source-multiple-panels": "Alternar dinamicamente a fonte de dados para vários painéis", "group": "Adicionar chaves para agrupar em tempo real", "hidden-constant-variable": "Uma variável constante oculta, útil para prefixos métricos em painéis de controle que você deseja compartilhar", + "users-enter-arbitrary-strings-switch": "", "users-enter-arbitrary-strings-textbox": "Os usuários podem inserir qualquer string arbitrária em uma caixa de texto", "values-are-static-and-defined-manually": "Os valores são estáticos e definidos manualmente", "values-fetched-source-query": "Os valores são obtidos a partir de uma consulta de fonte de dados", @@ -5832,6 +5833,7 @@ "group-by": "Agrupar por", "interval": "Intervalo", "query": "Consulta", + "switch": "", "textbox": "Caixa de texto" } }, @@ -6176,6 +6178,17 @@ "copy-to-clipboard-failed": "Falha ao copiar para a área de transferência" } }, + "switch-variable-form": { + "disabled-value": "", + "disabled-value-description": "", + "disabled-value-placeholder": "", + "enabled-value": "", + "enabled-value-description": "", + "enabled-value-placeholder": "", + "switch-options": "", + "value-pair-type": "", + "value-pair-type-description": "" + }, "text-box-variable": { "name-default-value": "Valor padrão" }, @@ -10355,7 +10368,7 @@ "title": "Perfis" }, "recently-deleted": { - "subtitle": "Todos os itens listados aqui por mais de 30 dias serão excluídos automaticamente.", + "subtitle": "", "title": "Excluídos recentemente" }, "recorded-queries": { @@ -11571,6 +11584,7 @@ "active-jobs": "tarefas ativas", "column-action": "Ação", "column-duration": "Duração", + "column-job-id": "", "column-message": "Mensagem", "column-started": "Iniciado", "column-status": "Status", @@ -11589,12 +11603,6 @@ "settings": "Configurações", "view": "Visualizar" }, - "repository-health": { - "details": "Detalhes:", - "no-errors-found": "Nenhum erro encontrado", - "title-repository-is-healthy": "O repositório está estável", - "title-repository-is-unhealthy": "O repositório está instável" - }, "repository-link": { "delete-or-move-job": { "compare-branch": "Comparar branch", @@ -11609,7 +11617,7 @@ }, "repository-overview": { "checked": "Verificado:", - "finished": "Finalizado:", + "finished": "", "health": "Integridade", "healthy": "Estável", "job-id": "ID da tarefa:", @@ -11618,7 +11626,6 @@ "not-available": "Não se aplica", "pull-status": "Status da extração", "resources": "Fontes", - "started": "Iniciado:", "status": "Status:", "unhealthy": "Instável", "view-folder": "Visualizar pasta", @@ -12278,9 +12285,6 @@ "tokens": "Tokens", "tooltip-managed-service-account-cannot-modified": "Esta é uma conta de serviço gerenciada e não pode ser modificada" }, - "service-account-permissions": { - "title-permissions": "Permissões" - }, "service-account-profile": { "information": "Informações", "label-creation-date": "Data de criação", @@ -12955,6 +12959,10 @@ "name-show-line-numbers": "Exibir números das linhas", "name-show-mini-map": "Mostrar minimapa" }, + "theme-playground": { + "label-base-theme": "", + "title": "" + }, "theme-preview": { "breadcrumbs": { "dashboards": "Painéis de controle", diff --git a/public/locales/pt-PT/grafana.json b/public/locales/pt-PT/grafana.json index 3521ee0e58d..aca99fc50ea 100644 --- a/public/locales/pt-PT/grafana.json +++ b/public/locales/pt-PT/grafana.json @@ -27,12 +27,12 @@ }, "permissions": { "add-label": "Adicionar uma permissão", + "loading": "", "no-permissions": "Não existem permissões", "permissions-change-warning": "Isto alterará as permissões para esta pasta e todas as suas descendentes. No total, isto afetará:", "role": "Função", "serviceaccount": "Conta de serviços", "team": "Equipa", - "title": "Permissões", "user": "Utilizador" } }, @@ -3504,7 +3504,7 @@ "delete-button": "Eliminar", "delete-modal-invalid-text": "Uma ou mais pastas contêm painéis de biblioteca ou regras de alerta. Elimine-as primeiro para continuar.", "delete-modal-invalid-title": "Não é possível eliminar a pasta", - "delete-modal-restore-dashboards-text": "Esta ação eliminará as pastas selecionadas imediatamente, mas os painéis selecionados serão marcados para eliminação em 30 dias. O administrador da sua organização pode restaurar os painéis a qualquer momento antes que os 30 dias expirem. Não é possível restaurar as pastas.", + "delete-modal-restore-dashboards-text": "", "delete-modal-text": "Esta ação eliminará o seguinte conteúdo:", "delete-modal-title": "Eliminar", "delete-provisioned-folder": "Eliminar a pasta disponibilizada", @@ -4572,7 +4572,7 @@ "unsupported-object-type": "" }, "overlay": { - "tooltip": "O elemento está oculto devido à renderização condicional." + "tooltip": "" }, "root": { "title": "Mostrar/ocultar regras" @@ -5819,6 +5819,7 @@ "dynamically-switch-source-multiple-panels": "Alternar dinamicamente a origem de dados para vários painéis", "group": "Adicionar chaves para agrupar em tempo real", "hidden-constant-variable": "Uma variável constante oculta, útil para prefixos métricos em painéis que pretende partilhar", + "users-enter-arbitrary-strings-switch": "", "users-enter-arbitrary-strings-textbox": "Os utilizadores podem inserir quaisquer cadeias de caracteres arbitrárias numa caixa de texto", "values-are-static-and-defined-manually": "Os valores são estáticos e definidos manualmente", "values-fetched-source-query": "Os valores são obtidos a partir de uma consulta de origem de dados", @@ -5832,6 +5833,7 @@ "group-by": "Agrupar por", "interval": "Intervalo", "query": "Consulta", + "switch": "", "textbox": "Caixa de texto" } }, @@ -6176,6 +6178,17 @@ "copy-to-clipboard-failed": "A cópia para a área de transferência falhou" } }, + "switch-variable-form": { + "disabled-value": "", + "disabled-value-description": "", + "disabled-value-placeholder": "", + "enabled-value": "", + "enabled-value-description": "", + "enabled-value-placeholder": "", + "switch-options": "", + "value-pair-type": "", + "value-pair-type-description": "" + }, "text-box-variable": { "name-default-value": "Valor predefinido" }, @@ -10355,7 +10368,7 @@ "title": "Profiles" }, "recently-deleted": { - "subtitle": "Quaisquer elementos listados aqui por mais de 30 dias serão eliminados automaticamente.", + "subtitle": "", "title": "Eliminado recentemente" }, "recorded-queries": { @@ -11571,6 +11584,7 @@ "active-jobs": "trabalhos ativos", "column-action": "Ação", "column-duration": "Duração", + "column-job-id": "", "column-message": "Mensagem", "column-started": "Iniciado", "column-status": "Estado", @@ -11589,12 +11603,6 @@ "settings": "Definições", "view": "Ver" }, - "repository-health": { - "details": "Detalhes:", - "no-errors-found": "Nenhum erro encontrado", - "title-repository-is-healthy": "O repositório está em bom estado", - "title-repository-is-unhealthy": "O repositório não está em bom estado" - }, "repository-link": { "delete-or-move-job": { "compare-branch": "Comparar filial", @@ -11609,7 +11617,7 @@ }, "repository-overview": { "checked": "Verificado:", - "finished": "Concluído:", + "finished": "", "health": "Saúde", "healthy": "Em bom estado", "job-id": "ID do trabalho:", @@ -11618,7 +11626,6 @@ "not-available": "N/A", "pull-status": "Estado da extração", "resources": "Recursos", - "started": "Iniciado:", "status": "Estado:", "unhealthy": "Não em bom estado", "view-folder": "Visualizar pasta", @@ -12278,9 +12285,6 @@ "tokens": "Tokens", "tooltip-managed-service-account-cannot-modified": "Esta é uma conta de serviço gerida e não pode ser modificada" }, - "service-account-permissions": { - "title-permissions": "Permissões" - }, "service-account-profile": { "information": "Informação", "label-creation-date": "Data de criação", @@ -12955,6 +12959,10 @@ "name-show-line-numbers": "Mostrar os números das linhas", "name-show-mini-map": "Mostrar o minimapa" }, + "theme-playground": { + "label-base-theme": "", + "title": "" + }, "theme-preview": { "breadcrumbs": { "dashboards": "Painéis de controlo", diff --git a/public/locales/ru-RU/grafana.json b/public/locales/ru-RU/grafana.json index 6ada3fb8075..c6bd50cb927 100644 --- a/public/locales/ru-RU/grafana.json +++ b/public/locales/ru-RU/grafana.json @@ -27,12 +27,12 @@ }, "permissions": { "add-label": "Добавить разрешение", + "loading": "", "no-permissions": "Нет разрешений", "permissions-change-warning": "Изменятся разрешения для этой и всех дочерних папок. В итоге будут затронуты:", "role": "Роль", "serviceaccount": "Служебная учетная запись", "team": "Команда", - "title": "Разрешения", "user": "Пользователь" } }, @@ -3528,7 +3528,7 @@ "delete-button": "Удалить", "delete-modal-invalid-text": "Одна или несколько папок содержат панели библиотеки или правила оповещения. Чтобы продолжить, сначала удалите их.", "delete-modal-invalid-title": "Невозможно удалить папку", - "delete-modal-restore-dashboards-text": "Это действие приведет к немедленному удалению выбранных папок, а выбранные дашборды будут помечены для удаления через 30 дней. Администратор вашей организации может восстановить дашборды в любое время в течение указанных 30 дней. Папки нельзя восстановить.", + "delete-modal-restore-dashboards-text": "", "delete-modal-text": "Это действие приведет к удалению следующего контента:", "delete-modal-title": "Удаление", "delete-provisioned-folder": "Удалить подготовленную папку", @@ -4612,7 +4612,7 @@ "unsupported-object-type": "" }, "overlay": { - "tooltip": "Элемент скрыт из-за условной визуализации." + "tooltip": "" }, "root": { "title": "Отображение/скрытие правил" @@ -5861,6 +5861,7 @@ "dynamically-switch-source-multiple-panels": "Динамическое переключение источника данных для нескольких панелей", "group": "Добавляйте ключи для группировки в процессе работы", "hidden-constant-variable": "Скрытая константная переменная для префиксов метрик на дашбордах, доступ к которым вы хотите открыть", + "users-enter-arbitrary-strings-switch": "", "users-enter-arbitrary-strings-textbox": "Пользователи могут вводить любые произвольные строки в текстовое поле", "values-are-static-and-defined-manually": "Значения являются статическими и задаются вручную", "values-fetched-source-query": "Значения извлекаются из запроса к источнику данных", @@ -5874,6 +5875,7 @@ "group-by": "Группировать по", "interval": "Интервал", "query": "Запрос", + "switch": "", "textbox": "Текстовое поле" } }, @@ -6222,6 +6224,17 @@ "copy-to-clipboard-failed": "Не удалось скопировать в буфер обмена" } }, + "switch-variable-form": { + "disabled-value": "", + "disabled-value-description": "", + "disabled-value-placeholder": "", + "enabled-value": "", + "enabled-value-description": "", + "enabled-value-placeholder": "", + "switch-options": "", + "value-pair-type": "", + "value-pair-type-description": "" + }, "text-box-variable": { "name-default-value": "Значение по умолчанию" }, @@ -10417,7 +10430,7 @@ "title": "Профили" }, "recently-deleted": { - "subtitle": "Все перечисленные элементы по истечении 30 дней будут автоматически удалены", + "subtitle": "", "title": "Последние удаленные" }, "recorded-queries": { @@ -11651,6 +11664,7 @@ "active-jobs": "активные задания", "column-action": "Действие", "column-duration": "Длительность", + "column-job-id": "", "column-message": "Сообщение", "column-started": "Запущено", "column-status": "Статус", @@ -11669,12 +11683,6 @@ "settings": "Параметры", "view": "Просмотр" }, - "repository-health": { - "details": "Сведения:", - "no-errors-found": "Ошибок не обнаружено", - "title-repository-is-healthy": "Репозиторий исправен", - "title-repository-is-unhealthy": "Репозиторий неисправен" - }, "repository-link": { "delete-or-move-job": { "compare-branch": "Сравнить ветвь", @@ -11689,7 +11697,7 @@ }, "repository-overview": { "checked": "Проверено:", - "finished": "Завершено:", + "finished": "", "health": "Работоспособность", "healthy": "Исправен", "job-id": "Идентификатор задания:", @@ -11698,7 +11706,6 @@ "not-available": "Н/Д", "pull-status": "Состояние включения изменений", "resources": "Ресурсы", - "started": "Запущено:", "status": "Состояние:", "unhealthy": "Неисправен", "view-folder": "Просмотр папки", @@ -12364,9 +12371,6 @@ "tokens": "Токены", "tooltip-managed-service-account-cannot-modified": "Это управляемая служебная учетная запись, которую невозможно изменить" }, - "service-account-permissions": { - "title-permissions": "Разрешения" - }, "service-account-profile": { "information": "Информация", "label-creation-date": "Дата создания", @@ -13045,6 +13049,10 @@ "name-show-line-numbers": "Показывать номера строк", "name-show-mini-map": "Показать мини-карту" }, + "theme-playground": { + "label-base-theme": "", + "title": "" + }, "theme-preview": { "breadcrumbs": { "dashboards": "Дашборды", diff --git a/public/locales/sv-SE/grafana.json b/public/locales/sv-SE/grafana.json index 65882cc56df..128f3559270 100644 --- a/public/locales/sv-SE/grafana.json +++ b/public/locales/sv-SE/grafana.json @@ -27,12 +27,12 @@ }, "permissions": { "add-label": "Lägg till en behörighet", + "loading": "", "no-permissions": "Det finns inga behörigheter", "permissions-change-warning": "Detta kommer att ändra behörigheter för den här mappen och alla dess underordnade. Totalt kommer detta att påverka:", "role": "Roll", "serviceaccount": "Servicekonto", "team": "Team", - "title": "Behörigheter", "user": "Användare" } }, @@ -3504,7 +3504,7 @@ "delete-button": "Ta bort", "delete-modal-invalid-text": "En eller flera mappar innehåller bibliotekspaneler eller varningsregler. Ta först bort dessa innan du fortsätter.", "delete-modal-invalid-title": "Det går inte att ta bort mapp", - "delete-modal-restore-dashboards-text": "Denna åtgärd raderar de valda mapparna omedelbart, men de valda instrumentpanelerna markeras för radering om 30 dagar. Din organisationsadministratör kan återställa instrumentpanelerna när som helst innan de 30 dagarna löper ut. Mappar kan inte återställas.", + "delete-modal-restore-dashboards-text": "", "delete-modal-text": "Denna åtgärd raderar följande innehåll:", "delete-modal-title": "Ta bort", "delete-provisioned-folder": "Ta bort provisionerad mapp", @@ -4572,7 +4572,7 @@ "unsupported-object-type": "" }, "overlay": { - "tooltip": "Elementet är dolt på grund av villkorsstyrd rendering." + "tooltip": "" }, "root": { "title": "Visa/dölj regler" @@ -5819,6 +5819,7 @@ "dynamically-switch-source-multiple-panels": "Byt datakälla dynamiskt för flera paneler", "group": "Lägg till nycklar att gruppera efter i farten", "hidden-constant-variable": "En dold konstant variabel, användbar för metriska prefix i kontrollpaneler som du vill dela", + "users-enter-arbitrary-strings-switch": "", "users-enter-arbitrary-strings-textbox": "Användare kan ange valfria strängar i en textruta", "values-are-static-and-defined-manually": "Värdena är statiska och definieras manuellt", "values-fetched-source-query": "Värden hämtas från en datakällfråga", @@ -5832,6 +5833,7 @@ "group-by": "Gruppera efter", "interval": "Intervall", "query": "Fråga", + "switch": "", "textbox": "Textfält" } }, @@ -6176,6 +6178,17 @@ "copy-to-clipboard-failed": "Kopiering till urklipp misslyckades" } }, + "switch-variable-form": { + "disabled-value": "", + "disabled-value-description": "", + "disabled-value-placeholder": "", + "enabled-value": "", + "enabled-value-description": "", + "enabled-value-placeholder": "", + "switch-options": "", + "value-pair-type": "", + "value-pair-type-description": "" + }, "text-box-variable": { "name-default-value": "Standardvärde" }, @@ -10355,7 +10368,7 @@ "title": "Profiler" }, "recently-deleted": { - "subtitle": "Alla objekt som listas här i fler än 30 dagar raderas automatiskt.", + "subtitle": "", "title": "Nyligen raderat" }, "recorded-queries": { @@ -11571,6 +11584,7 @@ "active-jobs": "aktiva jobb", "column-action": "Åtgärd", "column-duration": "Varaktighet", + "column-job-id": "", "column-message": "Meddelande", "column-started": "Startat", "column-status": "Status", @@ -11589,12 +11603,6 @@ "settings": "Inställningar", "view": "Visa" }, - "repository-health": { - "details": "Detaljer:", - "no-errors-found": "Inga fel hittades", - "title-repository-is-healthy": "Lagringsplatsen är hälsosam", - "title-repository-is-unhealthy": "Lagringsplatsen är ohälsosam" - }, "repository-link": { "delete-or-move-job": { "compare-branch": "Jämför gren", @@ -11609,7 +11617,7 @@ }, "repository-overview": { "checked": "Kontrollerad:", - "finished": "Avslutad:", + "finished": "", "health": "Hälsa", "healthy": "Hälsosamma", "job-id": "Jobb-ID:", @@ -11618,7 +11626,6 @@ "not-available": "Ej tillgängligt", "pull-status": "Pull-status", "resources": "Resurser", - "started": "Startad:", "status": "Status:", "unhealthy": "Ohälsosamma", "view-folder": "Visa mapp", @@ -12278,9 +12285,6 @@ "tokens": "Polletter", "tooltip-managed-service-account-cannot-modified": "Detta är ett hanterat servicekonto och kan inte ändras" }, - "service-account-permissions": { - "title-permissions": "Behörigheter" - }, "service-account-profile": { "information": "Information", "label-creation-date": "Skapande datum", @@ -12955,6 +12959,10 @@ "name-show-line-numbers": "Visa radnummer", "name-show-mini-map": "Visa minikarta" }, + "theme-playground": { + "label-base-theme": "", + "title": "" + }, "theme-preview": { "breadcrumbs": { "dashboards": "Instrumentpaneler", diff --git a/public/locales/tr-TR/grafana.json b/public/locales/tr-TR/grafana.json index bc07c4caf4a..b2aa2f13aa2 100644 --- a/public/locales/tr-TR/grafana.json +++ b/public/locales/tr-TR/grafana.json @@ -27,12 +27,12 @@ }, "permissions": { "add-label": "İzin ekle", + "loading": "", "no-permissions": "İzin yok", "permissions-change-warning": "Bu işlem, bu klasör ve tüm alt klasörlerin izinlerini değiştirecektir. Bu işlem toplamda şunları etkileyecektir:", "role": "Rol", "serviceaccount": "Hizmet Hesabı", "team": "Ekip", - "title": "İzinler", "user": "Kullanıcı" } }, @@ -3504,7 +3504,7 @@ "delete-button": "Sil", "delete-modal-invalid-text": "Bir veya daha fazla klasör, kütüphane panelleri veya uyarı kuralları içeriyor. Devam etmek için öncelikle bunları silin.", "delete-modal-invalid-title": "Klasör silinemiyor", - "delete-modal-restore-dashboards-text": "Bu işlem, seçilen klasörleri anında silecektir ancak seçilen panolar 30 gün içinde silinmek üzere işaretlenecektir. Kuruluş yöneticiniz, panoları 30 günlük süre dolmadan önce istediği zaman geri yükleyebilir. Klasörler geri yüklenemez.", + "delete-modal-restore-dashboards-text": "", "delete-modal-text": "Bu işlem aşağıdaki içerikleri silecektir:", "delete-modal-title": "Sil", "delete-provisioned-folder": "Sağlanan klasörü sil", @@ -4572,7 +4572,7 @@ "unsupported-object-type": "" }, "overlay": { - "tooltip": "Koşullu oluşturma nedeniyle öge gizlendi." + "tooltip": "" }, "root": { "title": "Kuralları göster/gizle" @@ -5819,6 +5819,7 @@ "dynamically-switch-source-multiple-panels": "Birden fazla panel için veri kaynağını dinamik olarak değiştirin", "group": "Anında gruplandırma için anahtarlar ekleyin", "hidden-constant-variable": "Paylaşmak istediğiniz panolardaki metrik önekler için kullanışlı olan gizli bir sabit değişken", + "users-enter-arbitrary-strings-switch": "", "users-enter-arbitrary-strings-textbox": "Kullanıcılar bir metin kutusuna herhangi bir dizi girebilir", "values-are-static-and-defined-manually": "Değerler statiktir ve manuel olarak tanımlanır", "values-fetched-source-query": "Değerler bir veri kaynağı sorgusundan alınır", @@ -5832,6 +5833,7 @@ "group-by": "Gruplandır", "interval": "Aralık", "query": "Sorgu", + "switch": "", "textbox": "Metin kutusu" } }, @@ -6176,6 +6178,17 @@ "copy-to-clipboard-failed": "Panoya kopyalanamadı" } }, + "switch-variable-form": { + "disabled-value": "", + "disabled-value-description": "", + "disabled-value-placeholder": "", + "enabled-value": "", + "enabled-value-description": "", + "enabled-value-placeholder": "", + "switch-options": "", + "value-pair-type": "", + "value-pair-type-description": "" + }, "text-box-variable": { "name-default-value": "Varsayılan değer" }, @@ -10355,7 +10368,7 @@ "title": "Profiller" }, "recently-deleted": { - "subtitle": "Burada 30 günden fazla listelenen ögeler otomatik olarak silinecektir.", + "subtitle": "", "title": "Son silinenler" }, "recorded-queries": { @@ -11571,6 +11584,7 @@ "active-jobs": "etkin işler", "column-action": "Eylem", "column-duration": "Süre", + "column-job-id": "", "column-message": "Mesaj", "column-started": "Başlatıldı", "column-status": "Durum", @@ -11589,12 +11603,6 @@ "settings": "Ayarlar", "view": "Görüntüle" }, - "repository-health": { - "details": "Detaylar: ", - "no-errors-found": "Hata bulunmadı", - "title-repository-is-healthy": "Depo iyi durumda", - "title-repository-is-unhealthy": "Depo iyi durumda değil" - }, "repository-link": { "delete-or-move-job": { "compare-branch": "", @@ -11609,7 +11617,7 @@ }, "repository-overview": { "checked": "Kontrol Eden:", - "finished": "Bitiş:", + "finished": "", "health": "Sağlık", "healthy": "İyi durumda", "job-id": "İş Kimliği:", @@ -11618,7 +11626,6 @@ "not-available": "Geçersiz", "pull-status": "Çekme durumu", "resources": "Kaynaklar", - "started": "Başlangıç:", "status": "Durum:", "unhealthy": "İyi durumda değil", "view-folder": "Klasörü Görüntüle", @@ -12278,9 +12285,6 @@ "tokens": "Belirteçler", "tooltip-managed-service-account-cannot-modified": "Bu yönetilen bir hizmet hesabıdır ve değiştirilemez" }, - "service-account-permissions": { - "title-permissions": "İzinler" - }, "service-account-profile": { "information": "Bilgi", "label-creation-date": "Oluşturulma Tarihi", @@ -12955,6 +12959,10 @@ "name-show-line-numbers": "Satır numaralarını göster", "name-show-mini-map": "Mini haritayı göster" }, + "theme-playground": { + "label-base-theme": "", + "title": "" + }, "theme-preview": { "breadcrumbs": { "dashboards": "Panolar", diff --git a/public/locales/zh-Hans/grafana.json b/public/locales/zh-Hans/grafana.json index c4e73e86396..c68822d2b8d 100644 --- a/public/locales/zh-Hans/grafana.json +++ b/public/locales/zh-Hans/grafana.json @@ -27,12 +27,12 @@ }, "permissions": { "add-label": "添加权限", + "loading": "", "no-permissions": "没有任何权限", "permissions-change-warning": "这将更改此文件夹及其所有子文件夹的权限。总体而言,这将影响:", "role": "角色", "serviceaccount": "服务帐户", "team": "团队", - "title": "权限", "user": "用户" } }, @@ -3492,7 +3492,7 @@ "delete-button": "删除", "delete-modal-invalid-text": "一个或多个文件夹包含库面板或警报规则。请首先删除这些以便继续。", "delete-modal-invalid-title": "无法删除文件夹", - "delete-modal-restore-dashboards-text": "此操作将立即删除所选文件夹,但所选数据面板将被标记为在 30 天后删除。您的组织管理员可以在 30 天期限内随时还原数据面板。文件夹无法还原。", + "delete-modal-restore-dashboards-text": "", "delete-modal-text": "此操作将删除以下内容:", "delete-modal-title": "删除", "delete-provisioned-folder": "删除已预置的文件夹", @@ -4552,7 +4552,7 @@ "unsupported-object-type": "" }, "overlay": { - "tooltip": "由于条件渲染,元素已被隐藏。" + "tooltip": "" }, "root": { "title": "显示/隐藏规则" @@ -5798,6 +5798,7 @@ "dynamically-switch-source-multiple-panels": "动态切换多个面板的数据源", "group": "即时添加按键分组", "hidden-constant-variable": "隐藏的常量变量,对于您要共享的数据面板中的指标前缀很有用", + "users-enter-arbitrary-strings-switch": "", "users-enter-arbitrary-strings-textbox": "用户可以在文本框中输入任意字符串", "values-are-static-and-defined-manually": "值为静态,由手动定义", "values-fetched-source-query": "值从数据源查询中获取", @@ -5811,6 +5812,7 @@ "group-by": "分组方式", "interval": "间隔", "query": "查询", + "switch": "", "textbox": "文本框" } }, @@ -6153,6 +6155,17 @@ "copy-to-clipboard-failed": "复制到剪贴板失败" } }, + "switch-variable-form": { + "disabled-value": "", + "disabled-value-description": "", + "disabled-value-placeholder": "", + "enabled-value": "", + "enabled-value-description": "", + "enabled-value-placeholder": "", + "switch-options": "", + "value-pair-type": "", + "value-pair-type-description": "" + }, "text-box-variable": { "name-default-value": "默认值" }, @@ -10324,7 +10337,7 @@ "title": "个人资料" }, "recently-deleted": { - "subtitle": "此处列出的任何项目超过 30 天都将被自动删除。", + "subtitle": "", "title": "最近删除" }, "recorded-queries": { @@ -11531,6 +11544,7 @@ "active-jobs": "活跃的作业", "column-action": "操作", "column-duration": "持续时间", + "column-job-id": "", "column-message": "消息", "column-started": "已启动", "column-status": "状态", @@ -11549,12 +11563,6 @@ "settings": "设置", "view": "查看" }, - "repository-health": { - "details": "详情:", - "no-errors-found": "没有发现错误", - "title-repository-is-healthy": "存储库状态良好", - "title-repository-is-unhealthy": "存储库状态不良" - }, "repository-link": { "delete-or-move-job": { "compare-branch": "比较分支", @@ -11569,7 +11577,7 @@ }, "repository-overview": { "checked": "已检查:", - "finished": "已完成:", + "finished": "", "health": "健康", "healthy": "状态良好", "job-id": "作业 ID:", @@ -11578,7 +11586,6 @@ "not-available": "N/A", "pull-status": "拉取状态", "resources": "资源", - "started": "已开始:", "status": "状态:", "unhealthy": "状态不良", "view-folder": "查看文件夹", @@ -12235,9 +12242,6 @@ "tokens": "令牌", "tooltip-managed-service-account-cannot-modified": "这是托管服务账户,无法修改" }, - "service-account-permissions": { - "title-permissions": "权限" - }, "service-account-profile": { "information": "信息", "label-creation-date": "创建日期", @@ -12910,6 +12914,10 @@ "name-show-line-numbers": "显示行号", "name-show-mini-map": "显示小地图" }, + "theme-playground": { + "label-base-theme": "", + "title": "" + }, "theme-preview": { "breadcrumbs": { "dashboards": "仪表板", diff --git a/public/locales/zh-Hant/grafana.json b/public/locales/zh-Hant/grafana.json index 2e783714b46..91502e2e2d8 100644 --- a/public/locales/zh-Hant/grafana.json +++ b/public/locales/zh-Hant/grafana.json @@ -27,12 +27,12 @@ }, "permissions": { "add-label": "新增權限", + "loading": "", "no-permissions": "沒有權限", "permissions-change-warning": "這將變更此資料夾及其所有子系的權限。這會影響以下項目:", "role": "角色", "serviceaccount": "服務帳戶", "team": "團隊", - "title": "權限", "user": "使用者" } }, @@ -3492,7 +3492,7 @@ "delete-button": "刪除", "delete-modal-invalid-text": "一個或多個資料夾包含資料庫面板或警報規則。請先刪除這些資料,然後再繼續。", "delete-modal-invalid-title": "無法刪除資料夾", - "delete-modal-restore-dashboards-text": "此動作會立即刪除選取的資料夾,但選取的儀表板會標記在 30 天後刪除。您的組織管理員可以在 30 天到期之前隨時還原儀表板。資料夾無法還原。", + "delete-modal-restore-dashboards-text": "", "delete-modal-text": "此動作將刪除以下內容:", "delete-modal-title": "刪除", "delete-provisioned-folder": "刪除已佈建的資料夾", @@ -4552,7 +4552,7 @@ "unsupported-object-type": "" }, "overlay": { - "tooltip": "由於條件轉譯,元素已被隱藏。" + "tooltip": "" }, "root": { "title": "顯示/隱藏規則" @@ -5798,6 +5798,7 @@ "dynamically-switch-source-multiple-panels": "動態切換多個面板的資料來源", "group": "即時新增鍵以便進行分組", "hidden-constant-variable": "隱藏的常數變數,對於您要共用的儀表板中的指標前置詞很有用", + "users-enter-arbitrary-strings-switch": "", "users-enter-arbitrary-strings-textbox": "使用者可以在文字方塊中輸入任何任意字串", "values-are-static-and-defined-manually": "值是靜態的,並且是手動定義的", "values-fetched-source-query": "從資料來源查詢中擷取值", @@ -5811,6 +5812,7 @@ "group-by": "分組依據", "interval": "間隔", "query": "查詢", + "switch": "", "textbox": "文字方塊" } }, @@ -6153,6 +6155,17 @@ "copy-to-clipboard-failed": "複製到剪貼簿失敗" } }, + "switch-variable-form": { + "disabled-value": "", + "disabled-value-description": "", + "disabled-value-placeholder": "", + "enabled-value": "", + "enabled-value-description": "", + "enabled-value-placeholder": "", + "switch-options": "", + "value-pair-type": "", + "value-pair-type-description": "" + }, "text-box-variable": { "name-default-value": "預設值" }, @@ -10324,7 +10337,7 @@ "title": "個人資料" }, "recently-deleted": { - "subtitle": "此處列出超過 30 天的任何項目將被自動刪除。", + "subtitle": "", "title": "最近刪除" }, "recorded-queries": { @@ -11531,6 +11544,7 @@ "active-jobs": "進行中的作業", "column-action": "動作", "column-duration": "持續時間", + "column-job-id": "", "column-message": "訊息", "column-started": "已開始", "column-status": "狀態", @@ -11549,12 +11563,6 @@ "settings": "設定", "view": "檢視" }, - "repository-health": { - "details": "詳細資料:", - "no-errors-found": "沒有發現錯誤", - "title-repository-is-healthy": "儲存庫狀態良好", - "title-repository-is-unhealthy": "儲存庫狀態不佳" - }, "repository-link": { "delete-or-move-job": { "compare-branch": "比較分支", @@ -11569,7 +11577,7 @@ }, "repository-overview": { "checked": "已選取:", - "finished": "結束:", + "finished": "", "health": "使用情況", "healthy": "健全", "job-id": "作業 ID:", @@ -11578,7 +11586,6 @@ "not-available": "不適用", "pull-status": "拉取狀態", "resources": "資源", - "started": "開始:", "status": "狀態:", "unhealthy": "狀況不佳", "view-folder": "檢視資料夾", @@ -12235,9 +12242,6 @@ "tokens": "權杖", "tooltip-managed-service-account-cannot-modified": "這是受管理的服務帳戶,無法修改" }, - "service-account-permissions": { - "title-permissions": "權限" - }, "service-account-profile": { "information": "資訊", "label-creation-date": "建立日期", @@ -12910,6 +12914,10 @@ "name-show-line-numbers": "顯示行號", "name-show-mini-map": "顯示迷你地圖" }, + "theme-playground": { + "label-base-theme": "", + "title": "" + }, "theme-preview": { "breadcrumbs": { "dashboards": "儀表板", From 09a3498552bbf8b2997c737f19fbc1082cd16228 Mon Sep 17 00:00:00 2001 From: Jack Baldry Date: Fri, 10 Oct 2025 13:23:23 +0100 Subject: [PATCH 065/112] Open Grafana Pathfinder instead of the help menu if its installed (#110592) * Link to Grafana Pathfinder if available Signed-off-by: Jack Baldry * refactor: `getComponentIdFromComponentMeta()` only receives the title * Making sure we pass helpNode without Parents to the pathfinder app. * minor refactoring to isolate the code. * Fix tests Signed-off-by: Jack Baldry * cleaned up the structure and exposing the helpNavItem via a hook * added missing files. * Add support for old and new pathfinder IDs Signed-off-by: Jack Baldry * Rename hook for consistency Signed-off-by: Jack Baldry --------- Signed-off-by: Jack Baldry Co-authored-by: Levente Balogh Co-authored-by: Marcus Andersson --- .../grafana-runtime/src/services/index.ts | 1 + .../src/services/navigation/useHelpNavItem.ts | 20 +++++ pkg/services/navtree/navtreeimpl/navtree.go | 16 +++- public/app/app.ts | 3 + .../ExtensionSidebar.test.tsx | 2 +- .../ExtensionSidebarProvider.test.tsx | 10 +-- .../ExtensionSidebarProvider.tsx | 21 +++++- .../ExtensionSidebar/ExtensionToolbarItem.tsx | 18 ++++- .../AppChrome/TopBar/HelpTopBarButton.tsx | 75 +++++++++++++++++++ .../AppChrome/TopBar/SingleTopBar.tsx | 19 ++--- .../AppChrome/TopBar/useHelpNode.tsx | 11 +++ 11 files changed, 168 insertions(+), 28 deletions(-) create mode 100644 packages/grafana-runtime/src/services/navigation/useHelpNavItem.ts create mode 100644 public/app/core/components/AppChrome/TopBar/HelpTopBarButton.tsx create mode 100644 public/app/core/components/AppChrome/TopBar/useHelpNode.tsx diff --git a/packages/grafana-runtime/src/services/index.ts b/packages/grafana-runtime/src/services/index.ts index e9d2eeac852..42ef1c3f655 100644 --- a/packages/grafana-runtime/src/services/index.ts +++ b/packages/grafana-runtime/src/services/index.ts @@ -30,6 +30,7 @@ export { type UsePluginFunctionsOptions, type UsePluginFunctionsResult, } from './pluginExtensions/usePluginFunctions'; +export { setHelpNavItemHook, useHelpNavItem, type UseHelpNavItem } from './navigation/useHelpNavItem'; export { getObservablePluginLinks } from './pluginExtensions/getObservablePluginLinks'; export { getObservablePluginComponents } from './pluginExtensions/getObservablePluginComponents'; export { diff --git a/packages/grafana-runtime/src/services/navigation/useHelpNavItem.ts b/packages/grafana-runtime/src/services/navigation/useHelpNavItem.ts new file mode 100644 index 00000000000..cb39aee2c0e --- /dev/null +++ b/packages/grafana-runtime/src/services/navigation/useHelpNavItem.ts @@ -0,0 +1,20 @@ +import { NavModelItem } from '@grafana/data'; + +export type UseHelpNavItem = () => NavModelItem | undefined; + +let singleton: UseHelpNavItem | undefined; + +export function setHelpNavItemHook(hook: UseHelpNavItem): void { + // We allow overriding the registry in tests + if (singleton && process.env.NODE_ENV !== 'test') { + throw new Error('setHelpNavItemHook() function should only be called once, when Grafana is starting.'); + } + singleton = hook; +} + +export function useHelpNavItem(): NavModelItem | undefined { + if (!singleton) { + throw new Error('useHelpNavItem() can only be used after the Grafana instance has started.'); + } + return singleton(); +} diff --git a/pkg/services/navtree/navtreeimpl/navtree.go b/pkg/services/navtree/navtreeimpl/navtree.go index 000ead39266..08853afa2bf 100644 --- a/pkg/services/navtree/navtreeimpl/navtree.go +++ b/pkg/services/navtree/navtreeimpl/navtree.go @@ -59,7 +59,8 @@ type NavigationAppConfig struct { func ProvideService(cfg *setting.Cfg, accessControl ac.AccessControl, pluginStore pluginstore.Store, pluginSettings pluginsettings.Service, starService star.Service, features featuremgmt.FeatureToggles, dashboardService dashboards.DashboardService, accesscontrolService ac.Service, kvStore kvstore.KVStore, apiKeyService apikey.Service, - license licensing.Licensing, authnService authn.Service) navtree.Service { + license licensing.Licensing, authnService authn.Service, +) navtree.Service { service := &ServiceImpl{ cfg: cfg, log: log.New("navtree service"), @@ -243,9 +244,10 @@ func isSupportBundlesEnabled(s *ServiceImpl) bool { return s.cfg.SectionWithEnvOverrides("support_bundles").Key("enabled").MustBool(true) } +// addHelpLinks adds a help menu item to the navigation bar. +// If the Grafana Pathfinder plugin is installed, it will handle enriching the help menu. func (s *ServiceImpl) addHelpLinks(treeRoot *navtree.NavTreeRoot, c *contextmodel.ReqContext) { if s.cfg.HelpEnabled { - // The version subtitle is set later by NavTree.ApplyHelpVersion helpNode := &navtree.NavLink{ Text: "Help", Id: "help", @@ -257,6 +259,16 @@ func (s *ServiceImpl) addHelpLinks(treeRoot *navtree.NavTreeRoot, c *contextmode treeRoot.AddSection(helpNode) + ctx := c.Req.Context() + // The docs plugin ID is going to transition from grafana-grafanadocsplugin-app to grafana-pathfinder-app. + // Support both until that migration is complete. + _, oldPathfinderInstalled := s.pluginStore.Plugin(ctx, "grafana-grafanadocsplugin-app") + _, newPathfinderInstalled := s.pluginStore.Plugin(ctx, "grafana-pathfinder-app") + if oldPathfinderInstalled || newPathfinderInstalled { + // Add a custom property to indicate this should open Grafana Pathfinder. + helpNode.HideFromTabs = true + } + hasAccess := ac.HasAccess(s.accessControl, c) supportBundleAccess := ac.EvalAny( ac.EvalPermission(supportbundlesimpl.ActionRead), diff --git a/public/app/app.ts b/public/app/app.ts index 7896117c1e7..48b97f40024 100644 --- a/public/app/app.ts +++ b/public/app/app.ts @@ -37,6 +37,7 @@ import { setCurrentUser, setChromeHeaderHeightHook, setPluginLinksHook, + setHelpNavItemHook, setFolderPicker, setCorrelationsService, setPluginFunctionsHook, @@ -60,6 +61,7 @@ import { AppWrapper } from './AppWrapper'; import appEvents from './core/app_events'; import { AppChromeService } from './core/components/AppChrome/AppChromeService'; import { useChromeHeaderHeight } from './core/components/AppChrome/TopBar/useChromeHeaderHeight'; +import { useHelpNode } from './core/components/AppChrome/TopBar/useHelpNode'; import { LazyFolderPicker } from './core/components/NestedFolderPicker/LazyFolderPicker'; import { getAllOptionEditors, getAllStandardFieldConfigs } from './core/components/OptionsUI/registry'; import { PluginPage } from './core/components/Page/PluginPage'; @@ -260,6 +262,7 @@ export class GrafanaApp { await preloadPlugins(appPluginsToAwait); } + setHelpNavItemHook(useHelpNode); setPluginLinksHook(usePluginLinks); setPluginComponentHook(usePluginComponent); setPluginComponentsHook(usePluginComponents); diff --git a/public/app/core/components/AppChrome/ExtensionSidebar/ExtensionSidebar.test.tsx b/public/app/core/components/AppChrome/ExtensionSidebar/ExtensionSidebar.test.tsx index 3891dac8e50..e08a37fda26 100644 --- a/public/app/core/components/AppChrome/ExtensionSidebar/ExtensionSidebar.test.tsx +++ b/public/app/core/components/AppChrome/ExtensionSidebar/ExtensionSidebar.test.tsx @@ -43,7 +43,7 @@ const addedComponentConfigMock: ExtensionInfo = { }; const extensionSidebarContextMock: ExtensionSidebarContextType = { - dockedComponentId: getComponentIdFromComponentMeta(pluginId, addedComponentConfigMock), + dockedComponentId: getComponentIdFromComponentMeta(pluginId, addedComponentConfigMock.title), props: {}, isOpen: true, setDockedComponentId: jest.fn(), diff --git a/public/app/core/components/AppChrome/ExtensionSidebar/ExtensionSidebarProvider.test.tsx b/public/app/core/components/AppChrome/ExtensionSidebar/ExtensionSidebarProvider.test.tsx index 7839207632b..955c85923b6 100644 --- a/public/app/core/components/AppChrome/ExtensionSidebar/ExtensionSidebarProvider.test.tsx +++ b/public/app/core/components/AppChrome/ExtensionSidebar/ExtensionSidebarProvider.test.tsx @@ -121,7 +121,7 @@ describe('ExtensionSidebarProvider', () => { }); it('should load docked component from storage if available', () => { - const componentId = getComponentIdFromComponentMeta(mockPluginMeta.pluginId, mockComponent); + const componentId = getComponentIdFromComponentMeta(mockPluginMeta.pluginId, mockComponent.title); (store.get as jest.Mock).mockReturnValue(componentId); render( @@ -135,7 +135,7 @@ describe('ExtensionSidebarProvider', () => { }); it('should update storage when docked component changes', () => { - const componentId = getComponentIdFromComponentMeta(mockPluginMeta.pluginId, mockComponent); + const componentId = getComponentIdFromComponentMeta(mockPluginMeta.pluginId, mockComponent.title); const TestComponentWithActions = () => { const context = useExtensionSidebarContext(); @@ -291,7 +291,7 @@ describe('ExtensionSidebarProvider', () => { }); it('should close sidebar when receiving a CloseExtensionSidebarEvent', () => { - const componentId = getComponentIdFromComponentMeta(mockPluginMeta.pluginId, mockComponent); + const componentId = getComponentIdFromComponentMeta(mockPluginMeta.pluginId, mockComponent.title); const TestComponentWithProps = () => { const context = useExtensionSidebarContext(); @@ -433,7 +433,7 @@ describe('ExtensionSidebarProvider', () => { describe('Utility Functions', () => { describe('getComponentIdFromComponentMeta', () => { it('should create a valid component ID', () => { - const componentId = getComponentIdFromComponentMeta(mockPluginMeta.pluginId, mockComponent); + const componentId = getComponentIdFromComponentMeta(mockPluginMeta.pluginId, mockComponent.title); expect(componentId).toBe( JSON.stringify({ pluginId: mockPluginMeta.pluginId, componentTitle: mockComponent.title }) @@ -443,7 +443,7 @@ describe('Utility Functions', () => { describe('getComponentMetaFromComponentId', () => { it('should parse a valid component ID', () => { - const componentId = getComponentIdFromComponentMeta(mockPluginMeta.pluginId, mockComponent); + const componentId = getComponentIdFromComponentMeta(mockPluginMeta.pluginId, mockComponent.title); const meta = getComponentMetaFromComponentId(componentId); expect(meta).toEqual({ diff --git a/public/app/core/components/AppChrome/ExtensionSidebar/ExtensionSidebarProvider.tsx b/public/app/core/components/AppChrome/ExtensionSidebar/ExtensionSidebarProvider.tsx index ca5ff926169..9dcb1cf1edb 100644 --- a/public/app/core/components/AppChrome/ExtensionSidebar/ExtensionSidebarProvider.tsx +++ b/public/app/core/components/AppChrome/ExtensionSidebar/ExtensionSidebarProvider.tsx @@ -1,7 +1,7 @@ import { createContext, ReactNode, useCallback, useContext, useEffect, useState, useMemo } from 'react'; import { useLocalStorage } from 'react-use'; -import { PluginExtensionPoints, store, type ExtensionInfo } from '@grafana/data'; +import { PluginExtensionPoints, store } from '@grafana/data'; import { getAppEvents, reportInteraction, usePluginLinks, locationService } from '@grafana/runtime'; import { ExtensionPointPluginMeta, getExtensionPointPluginMeta } from 'app/features/plugins/extensions/utils'; import { CloseExtensionSidebarEvent, OpenExtensionSidebarEvent } from 'app/types/events'; @@ -228,8 +228,8 @@ export const ExtensionSidebarContextProvider = ({ children }: ExtensionSidebarCo ); }; -export function getComponentIdFromComponentMeta(pluginId: string, component: ExtensionInfo) { - return JSON.stringify({ pluginId, componentTitle: component.title }); +export function getComponentIdFromComponentMeta(pluginId: string, componentTitle: string) { + return JSON.stringify({ pluginId, componentTitle }); } export function getComponentMetaFromComponentId( @@ -252,3 +252,18 @@ export function getComponentMetaFromComponentId( return undefined; } } + +// The docs plugin ID is going to transition from grafana-grafanadocsplugin-app to grafana-pathfinder-app. +// Support both until that migration is complete. +// Prioritize the new plugin ID (grafana-pathfinder-app). +export function getPathfinderPluginId(availableComponents: ExtensionPointPluginMeta): string | undefined { + if (availableComponents.has('grafana-pathfinder-app')) { + return 'grafana-pathfinder-app'; + } + + if (availableComponents.has('grafana-grafanadocsplugin-app')) { + return 'grafana-grafanadocsplugin-app'; + } + + return undefined; +} diff --git a/public/app/core/components/AppChrome/ExtensionSidebar/ExtensionToolbarItem.tsx b/public/app/core/components/AppChrome/ExtensionSidebar/ExtensionToolbarItem.tsx index 3839ebcfcaa..cee522a2e85 100644 --- a/public/app/core/components/AppChrome/ExtensionSidebar/ExtensionToolbarItem.tsx +++ b/public/app/core/components/AppChrome/ExtensionSidebar/ExtensionToolbarItem.tsx @@ -17,20 +17,32 @@ type Props = { }; const compactAllowedComponents = ['grafana-assistant-app']; +const pathfinderPluginIds = ['grafana-pathfinder-app', 'grafana-grafanadocsplugin-app']; export function ExtensionToolbarItem({ compact }: Props) { const { availableComponents, dockedComponentId, setDockedComponentId } = useExtensionSidebarContext(); - if (availableComponents.size === 0) { + // Don't render the toolbar if the only available plugins are Grafana Pathfinder. + // It's opened by the help menu. + const nonPathfinderPlugins = Array.from(availableComponents.keys()).filter( + (pluginId) => !pathfinderPluginIds.includes(pluginId) + ); + if (nonPathfinderPlugins.length === 0) { return null; } const dockedMeta = dockedComponentId ? getComponentMetaFromComponentId(dockedComponentId) : null; const renderPluginButton = (pluginId: string, components: ComponentWithPluginId[]) => { + // Don't render the Grafana Pathfinder button. + // It's opened by the help menu button. + if (pathfinderPluginIds.includes(pluginId)) { + return null; + } + if (components.length === 1) { const component = components[0]; - const componentId = getComponentIdFromComponentMeta(pluginId, component); + const componentId = getComponentIdFromComponentMeta(pluginId, component.title); const isActive = dockedComponentId === componentId; // we now allow more components in the extension sidebar @@ -54,7 +66,7 @@ export function ExtensionToolbarItem({ compact }: Props) { const MenuItems = ( {components.map((c) => { - const id = getComponentIdFromComponentMeta(pluginId, c); + const id = getComponentIdFromComponentMeta(pluginId, c.title); return ( } placement="bottom-end"> + + + ); + } + + const componentId = getComponentIdFromComponentMeta(pathfinderPluginId, 'Grafana Pathfinder'); + const isOpen = dockedComponentId === componentId; + + return ( + { + if (isOpen) { + setDockedComponentId(undefined); + } else { + const appEvents = getAppEvents(); + appEvents.publish( + new OpenExtensionSidebarEvent({ + pluginId: pathfinderPluginId, + componentTitle: 'Grafana Pathfinder', + }) + ); + } + }} + /> + ); +}); + +const getStyles = (theme: GrafanaTheme2) => ({ + helpButtonActive: css({ + borderRadius: theme.shape.radius.circle, + backgroundColor: theme.colors.primary.transparent, + border: `1px solid ${theme.colors.primary.borderTransparent}`, + color: theme.colors.text.primary, + }), +}); diff --git a/public/app/core/components/AppChrome/TopBar/SingleTopBar.tsx b/public/app/core/components/AppChrome/TopBar/SingleTopBar.tsx index 71211e4b535..f04222533f9 100644 --- a/public/app/core/components/AppChrome/TopBar/SingleTopBar.tsx +++ b/public/app/core/components/AppChrome/TopBar/SingleTopBar.tsx @@ -1,12 +1,11 @@ import { css } from '@emotion/css'; -import { cloneDeep } from 'lodash'; -import { memo } from 'react'; +import React, { memo } from 'react'; import { GrafanaTheme2, NavModelItem } from '@grafana/data'; import { Components } from '@grafana/e2e-selectors'; import { t } from '@grafana/i18n'; import { ScopesContextValue } from '@grafana/runtime'; -import { Dropdown, Icon, Stack, ToolbarButton, useStyles2 } from '@grafana/ui'; +import { Icon, Stack, ToolbarButton, useStyles2 } from '@grafana/ui'; import { config } from 'app/core/config'; import { MEGA_MENU_TOGGLE_ID } from 'app/core/constants'; import { useGrafana } from 'app/core/context/GrafanaContext'; @@ -20,15 +19,14 @@ import { Breadcrumbs } from '../../Breadcrumbs/Breadcrumbs'; import { buildBreadcrumbs } from '../../Breadcrumbs/utils'; import { ExtensionToolbarItem } from '../ExtensionSidebar/ExtensionToolbarItem'; import { HistoryContainer } from '../History/HistoryContainer'; -import { enrichHelpItem } from '../MegaMenu/utils'; import { NavToolbarSeparator } from '../NavToolbar/NavToolbarSeparator'; import { QuickAdd } from '../QuickAdd/QuickAdd'; +import { HelpTopBarButton } from './HelpTopBarButton'; import { InviteUserButton } from './InviteUserButton'; import { ProfileButton } from './ProfileButton'; import { SignInLink } from './SignInLink'; import { SingleTopBarActions } from './SingleTopBarActions'; -import { TopNavBarMenu } from './TopNavBarMenu'; import { TopSearchBarCommandPaletteTrigger } from './TopSearchBarCommandPaletteTrigger'; import { getChromeHeaderLevelHeight } from './useChromeHeaderHeight'; @@ -57,10 +55,7 @@ export const SingleTopBar = memo(function SingleTopBar({ const state = chrome.useState(); const menuDockedAndOpen = !state.chromeless && state.megaMenuDocked && state.megaMenuOpen; const styles = useStyles2(getStyles, menuDockedAndOpen); - const navIndex = useSelector((state) => state.navIndex); - const helpNode = cloneDeep(navIndex['help']); - const enrichedHelpNode = helpNode ? enrichHelpItem(helpNode) : undefined; - const profileNode = navIndex['profile']; + const profileNode = useSelector((state) => state.navIndex['profile']); const homeNav = useSelector((state) => state.navIndex)[HOME_NAV_ID]; const breadcrumbs = buildBreadcrumbs(sectionNav, pageNav, homeNav); const unifiedHistoryEnabled = config.featureToggles.unifiedHistory; @@ -98,11 +93,7 @@ export const SingleTopBar = memo(function SingleTopBar({ {unifiedHistoryEnabled && !isSmallScreen && } {!isSmallScreen && } - {enrichedHelpNode && ( - } placement="bottom-end"> - - - )} + {!isSmallScreen && } {!showToolbarLevel && actions} diff --git a/public/app/core/components/AppChrome/TopBar/useHelpNode.tsx b/public/app/core/components/AppChrome/TopBar/useHelpNode.tsx new file mode 100644 index 00000000000..52beedffb56 --- /dev/null +++ b/public/app/core/components/AppChrome/TopBar/useHelpNode.tsx @@ -0,0 +1,11 @@ +import { cloneDeep } from 'lodash'; + +import { useSelector } from 'app/types/store'; + +import { enrichHelpItem } from '../MegaMenu/utils'; + +export function useHelpNode() { + const navIndex = useSelector((state) => state.navIndex); + const helpNode = cloneDeep(navIndex['help']); + return helpNode ? enrichHelpItem(helpNode) : undefined; +} From fdccc3e33c535e5fff5c9960c8ed8a23e1c335b2 Mon Sep 17 00:00:00 2001 From: Ryan McKinley Date: Fri, 10 Oct 2025 15:37:32 +0300 Subject: [PATCH 066/112] Dashboards/API: Keep original when conversion fails (#109912) * keep original * openapi * update tests * workspace * workspace * fix ts * merge main * update snapshots --- apps/dashboard/kinds/dashboard.cue | 3 + .../v0alpha1/dashboard_status_gen.go | 2 + .../v0alpha1/zz_generated.openapi.go | 7 + .../dashboard/v1beta1/dashboard_status_gen.go | 2 + .../dashboard/v1beta1/zz_generated.openapi.go | 7 + .../v2alpha1/dashboard_status_gen.go | 2 + .../v2alpha1/zz_generated.openapi.go | 7 + .../dashboard/v2beta1/dashboard_status_gen.go | 2 + .../dashboard/v2beta1/zz_generated.openapi.go | 7 + .../output/v2alpha1.complete.v0alpha1.json | 500 +++++++- .../output/v2alpha1.complete.v1beta1.json | 500 +++++++- .../v2alpha1.ds-data-query.v0alpha1.json | 1050 ++++++++++++++++- .../v2alpha1.ds-data-query.v1beta1.json | 1050 ++++++++++++++++- .../v2alpha1.groupby-adhoc-vars.v0alpha1.json | 95 +- .../v2alpha1.groupby-adhoc-vars.v1beta1.json | 95 +- .../output/v2alpha1.viz-config.v0alpha1.json | 214 +++- .../output/v2alpha1.viz-config.v1beta1.json | 214 +++- apps/dashboard/pkg/migration/conversion/v0.go | 2 + apps/dashboard/pkg/migration/conversion/v1.go | 2 + apps/dashboard/pkg/migration/conversion/v2.go | 6 + .../dashboard/v0alpha1/types.status.gen.ts | 2 + .../dashboard/v1beta1/types.status.gen.ts | 2 + .../dashboard/v2alpha1/types.status.gen.ts | 2 + .../dashboard/v2beta1/types.status.gen.ts | 2 + .../dashboard.grafana.app-v0alpha1.json | 4 + .../dashboard.grafana.app-v1beta1.json | 4 + .../dashboard.grafana.app-v2alpha1.json | 4 + 27 files changed, 3779 insertions(+), 8 deletions(-) diff --git a/apps/dashboard/kinds/dashboard.cue b/apps/dashboard/kinds/dashboard.cue index 905b9606336..e8dfea3bf98 100644 --- a/apps/dashboard/kinds/dashboard.cue +++ b/apps/dashboard/kinds/dashboard.cue @@ -27,6 +27,9 @@ ConversionStatus: { // The version which was stored when the dashboard was created / updated. // Fetching this version should always succeed. storedVersion?: string + + // The original value map[string]any + source?: _ } dashboard: { diff --git a/apps/dashboard/pkg/apis/dashboard/v0alpha1/dashboard_status_gen.go b/apps/dashboard/pkg/apis/dashboard/v0alpha1/dashboard_status_gen.go index 2dec6aea951..1db6c8e3467 100644 --- a/apps/dashboard/pkg/apis/dashboard/v0alpha1/dashboard_status_gen.go +++ b/apps/dashboard/pkg/apis/dashboard/v0alpha1/dashboard_status_gen.go @@ -15,6 +15,8 @@ type DashboardConversionStatus struct { // The version which was stored when the dashboard was created / updated. // Fetching this version should always succeed. StoredVersion *string `json:"storedVersion,omitempty"` + // The original value map[string]any + Source interface{} `json:"source,omitempty"` } // NewDashboardConversionStatus creates a new DashboardConversionStatus object. diff --git a/apps/dashboard/pkg/apis/dashboard/v0alpha1/zz_generated.openapi.go b/apps/dashboard/pkg/apis/dashboard/v0alpha1/zz_generated.openapi.go index 0027a8b8cc4..63af5e26b7e 100644 --- a/apps/dashboard/pkg/apis/dashboard/v0alpha1/zz_generated.openapi.go +++ b/apps/dashboard/pkg/apis/dashboard/v0alpha1/zz_generated.openapi.go @@ -272,6 +272,13 @@ func schema_pkg_apis_dashboard_v0alpha1_DashboardConversionStatus(ref common.Ref Format: "", }, }, + "source": { + SchemaProps: spec.SchemaProps{ + Description: "The original value map[string]any", + Type: []string{"object"}, + Format: "", + }, + }, }, Required: []string{"failed"}, }, diff --git a/apps/dashboard/pkg/apis/dashboard/v1beta1/dashboard_status_gen.go b/apps/dashboard/pkg/apis/dashboard/v1beta1/dashboard_status_gen.go index a2ac9850b09..5fa18c8f821 100644 --- a/apps/dashboard/pkg/apis/dashboard/v1beta1/dashboard_status_gen.go +++ b/apps/dashboard/pkg/apis/dashboard/v1beta1/dashboard_status_gen.go @@ -15,6 +15,8 @@ type DashboardConversionStatus struct { // The version which was stored when the dashboard was created / updated. // Fetching this version should always succeed. StoredVersion *string `json:"storedVersion,omitempty"` + // The original value map[string]any + Source interface{} `json:"source,omitempty"` } // NewDashboardConversionStatus creates a new DashboardConversionStatus object. diff --git a/apps/dashboard/pkg/apis/dashboard/v1beta1/zz_generated.openapi.go b/apps/dashboard/pkg/apis/dashboard/v1beta1/zz_generated.openapi.go index ab68539f1fe..be747be2ae3 100644 --- a/apps/dashboard/pkg/apis/dashboard/v1beta1/zz_generated.openapi.go +++ b/apps/dashboard/pkg/apis/dashboard/v1beta1/zz_generated.openapi.go @@ -264,6 +264,13 @@ func schema_pkg_apis_dashboard_v1beta1_DashboardConversionStatus(ref common.Refe Format: "", }, }, + "source": { + SchemaProps: spec.SchemaProps{ + Description: "The original value map[string]any", + Type: []string{"object"}, + Format: "", + }, + }, }, Required: []string{"failed"}, }, diff --git a/apps/dashboard/pkg/apis/dashboard/v2alpha1/dashboard_status_gen.go b/apps/dashboard/pkg/apis/dashboard/v2alpha1/dashboard_status_gen.go index 9544b902dd8..6fad54db0b4 100644 --- a/apps/dashboard/pkg/apis/dashboard/v2alpha1/dashboard_status_gen.go +++ b/apps/dashboard/pkg/apis/dashboard/v2alpha1/dashboard_status_gen.go @@ -15,6 +15,8 @@ type DashboardConversionStatus struct { // The version which was stored when the dashboard was created / updated. // Fetching this version should always succeed. StoredVersion *string `json:"storedVersion,omitempty"` + // The original value map[string]any + Source interface{} `json:"source,omitempty"` } // NewDashboardConversionStatus creates a new DashboardConversionStatus object. diff --git a/apps/dashboard/pkg/apis/dashboard/v2alpha1/zz_generated.openapi.go b/apps/dashboard/pkg/apis/dashboard/v2alpha1/zz_generated.openapi.go index 1c030f705c9..548a8c93268 100644 --- a/apps/dashboard/pkg/apis/dashboard/v2alpha1/zz_generated.openapi.go +++ b/apps/dashboard/pkg/apis/dashboard/v2alpha1/zz_generated.openapi.go @@ -1353,6 +1353,13 @@ func schema_pkg_apis_dashboard_v2alpha1_DashboardConversionStatus(ref common.Ref Format: "", }, }, + "source": { + SchemaProps: spec.SchemaProps{ + Description: "The original value map[string]any", + Type: []string{"object"}, + Format: "", + }, + }, }, Required: []string{"failed"}, }, diff --git a/apps/dashboard/pkg/apis/dashboard/v2beta1/dashboard_status_gen.go b/apps/dashboard/pkg/apis/dashboard/v2beta1/dashboard_status_gen.go index 62d39eb766d..a6bd3ee5e18 100644 --- a/apps/dashboard/pkg/apis/dashboard/v2beta1/dashboard_status_gen.go +++ b/apps/dashboard/pkg/apis/dashboard/v2beta1/dashboard_status_gen.go @@ -15,6 +15,8 @@ type DashboardConversionStatus struct { // The version which was stored when the dashboard was created / updated. // Fetching this version should always succeed. StoredVersion *string `json:"storedVersion,omitempty"` + // The original value map[string]any + Source interface{} `json:"source,omitempty"` } // NewDashboardConversionStatus creates a new DashboardConversionStatus object. diff --git a/apps/dashboard/pkg/apis/dashboard/v2beta1/zz_generated.openapi.go b/apps/dashboard/pkg/apis/dashboard/v2beta1/zz_generated.openapi.go index 39e6e37503f..5e51134e80c 100644 --- a/apps/dashboard/pkg/apis/dashboard/v2beta1/zz_generated.openapi.go +++ b/apps/dashboard/pkg/apis/dashboard/v2beta1/zz_generated.openapi.go @@ -1360,6 +1360,13 @@ func schema_pkg_apis_dashboard_v2beta1_DashboardConversionStatus(ref common.Refe Format: "", }, }, + "source": { + SchemaProps: spec.SchemaProps{ + Description: "The original value map[string]any", + Type: []string{"object"}, + Format: "", + }, + }, }, Required: []string{"failed"}, }, diff --git a/apps/dashboard/pkg/migration/conversion/testdata/output/v2alpha1.complete.v0alpha1.json b/apps/dashboard/pkg/migration/conversion/testdata/output/v2alpha1.complete.v0alpha1.json index 1191a6aef3b..3306c334c5e 100644 --- a/apps/dashboard/pkg/migration/conversion/testdata/output/v2alpha1.complete.v0alpha1.json +++ b/apps/dashboard/pkg/migration/conversion/testdata/output/v2alpha1.complete.v0alpha1.json @@ -15,7 +15,505 @@ "conversion": { "failed": true, "error": "backend conversion not yet implemented", - "storedVersion": "v2alpha1" + "storedVersion": "v2alpha1", + "source": { + "kind": "Dashboard", + "apiVersion": "dashboard.grafana.app/v2alpha1", + "metadata": { + "name": "test-v2alpha1-complete", + "labels": { + "category": "test" + }, + "annotations": { + "description": "Complete example of v2alpha1 dashboard features" + } + }, + "spec": { + "annotations": [ + { + "kind": "AnnotationQuery", + "spec": { + "datasource": { + "type": "grafana", + "uid": "-- Grafana --" + }, + "query": { + "kind": "grafana", + "spec": { + "limit": 100, + "matchAny": false, + "tags": [], + "type": "dashboard" + } + }, + "enable": true, + "hide": false, + "iconColor": "rgba(0, 211, 255, 1)", + "name": "Annotations \u0026 Alerts", + "builtIn": true + } + }, + { + "kind": "AnnotationQuery", + "spec": { + "datasource": { + "type": "prometheus", + "uid": "gdev-prometheus" + }, + "query": { + "kind": "prometheus", + "spec": { + "expr": "changes(process_start_time_seconds[1m])", + "refId": "Anno" + } + }, + "enable": true, + "hide": false, + "iconColor": "yellow", + "name": "Prometheus Annotations", + "builtIn": false + } + } + ], + "cursorSync": "Tooltip", + "description": "This dashboard demonstrates all features that need to be converted from v2alpha1 to v2beta1", + "editable": true, + "elements": { + "panel-1": { + "kind": "Panel", + "spec": { + "id": 1, + "title": "Panel with Conditional Rendering", + "description": "This panel demonstrates conditional rendering features", + "links": null, + "data": { + "kind": "QueryGroup", + "spec": { + "queries": [ + { + "kind": "PanelQuery", + "spec": { + "query": { + "kind": "prometheus", + "spec": { + "expr": "up{job=\"grafana\"}" + } + }, + "datasource": { + "uid": "gdev-prometheus" + }, + "refId": "A", + "hidden": false + } + } + ], + "transformations": [ + { + "kind": "reduce", + "spec": { + "id": "reduce", + "options": { + "includeTimeField": false, + "mode": "reduceFields", + "reducers": [ + "mean" + ] + } + } + } + ], + "queryOptions": {} + } + }, + "vizConfig": { + "kind": "stat", + "spec": { + "pluginVersion": "12.1.0-pre", + "options": { + "colorMode": "value", + "graphMode": "area", + "justifyMode": "auto", + "textMode": "auto" + }, + "fieldConfig": { + "defaults": { + "mappings": [ + { + "type": "value", + "options": { + "0": { + "text": "Down", + "color": "red" + }, + "1": { + "text": "Up", + "color": "green" + } + } + } + ], + "thresholds": { + "mode": "absolute", + "steps": [ + { + "value": 0, + "color": "red" + }, + { + "value": 1, + "color": "green" + } + ] + }, + "color": { + "mode": "thresholds" + } + }, + "overrides": [] + } + } + } + } + } + }, + "layout": { + "kind": "RowsLayout", + "spec": { + "rows": [ + { + "kind": "Row", + "spec": { + "title": "Conditional Row", + "collapse": false, + "hideHeader": false, + "fillScreen": false, + "conditionalRendering": { + "kind": "ConditionalRenderingGroup", + "spec": { + "visibility": "show", + "condition": "and", + "items": [ + { + "kind": "ConditionalRenderingVariable", + "spec": { + "variable": "group_by", + "operator": "includes", + "value": "instance" + } + }, + { + "kind": "ConditionalRenderingData", + "spec": { + "value": true + } + }, + { + "kind": "ConditionalRenderingTimeRangeSize", + "spec": { + "value": "1h" + } + } + ] + } + }, + "layout": { + "kind": "GridLayout", + "spec": { + "items": [ + { + "kind": "GridLayoutItem", + "spec": { + "x": 0, + "y": 0, + "width": 24, + "height": 8, + "element": { + "kind": "ElementReference", + "name": "panel-1" + } + } + } + ] + } + } + } + } + ] + } + }, + "links": null, + "liveNow": true, + "preload": true, + "tags": [ + "test", + "example", + "migration" + ], + "timeSettings": { + "timezone": "browser", + "from": "now-6h", + "to": "now", + "autoRefresh": "10s", + "autoRefreshIntervals": [ + "5s", + "10s", + "30s", + "1m", + "5m", + "15m", + "30m", + "1h", + "2h", + "1d" + ], + "hideTimepicker": false, + "weekStart": "monday", + "fiscalYearStartMonth": 0 + }, + "title": "Test: Complete V2alpha1 Dashboard Example", + "variables": [ + { + "kind": "QueryVariable", + "spec": { + "name": "prometheus_query", + "current": { + "text": "All", + "value": [ + "$__all" + ] + }, + "label": "Prometheus Query", + "hide": "dontHide", + "refresh": "time", + "skipUrlSync": false, + "description": "Shows all up metrics", + "datasource": { + "type": "prometheus", + "uid": "gdev-prometheus" + }, + "query": { + "kind": "prometheus", + "spec": { + "expr": "up" + } + }, + "regex": "", + "sort": "alphabetical", + "definition": "up", + "options": null, + "multi": true, + "includeAll": true, + "allowCustomValue": false + } + }, + { + "kind": "TextVariable", + "spec": { + "name": "text_var", + "current": { + "selected": true, + "text": "server1", + "value": "server1" + }, + "query": "server1,server2,server3", + "label": "Text Variable", + "hide": "dontHide", + "skipUrlSync": false, + "description": "A simple text variable" + } + }, + { + "kind": "ConstantVariable", + "spec": { + "name": "constant_var", + "query": "production", + "current": { + "selected": true, + "text": "production", + "value": "production" + }, + "label": "Constant", + "hide": "dontHide", + "skipUrlSync": true, + "description": "A constant value" + } + }, + { + "kind": "DatasourceVariable", + "spec": { + "name": "ds_var", + "pluginId": "prometheus", + "refresh": "load", + "regex": "/^gdev-/", + "current": { + "text": "gdev-prometheus", + "value": "gdev-prometheus" + }, + "options": [ + { + "text": "gdev-prometheus", + "value": "gdev-prometheus" + } + ], + "multi": false, + "includeAll": false, + "label": "Datasource", + "hide": "dontHide", + "skipUrlSync": false, + "description": "Select a datasource", + "allowCustomValue": false + } + }, + { + "kind": "IntervalVariable", + "spec": { + "name": "interval", + "query": "1m,5m,10m,30m,1h,6h,12h,1d", + "current": { + "selected": true, + "text": "5m", + "value": "5m" + }, + "options": [ + { + "text": "1m", + "value": "1m" + }, + { + "text": "5m", + "value": "5m" + }, + { + "text": "10m", + "value": "10m" + }, + { + "text": "30m", + "value": "30m" + }, + { + "text": "1h", + "value": "1h" + }, + { + "text": "6h", + "value": "6h" + }, + { + "text": "12h", + "value": "12h" + }, + { + "text": "1d", + "value": "1d" + } + ], + "auto": true, + "auto_min": "10s", + "auto_count": 30, + "refresh": "load", + "label": "Interval", + "hide": "dontHide", + "skipUrlSync": false, + "description": "Time interval selection" + } + }, + { + "kind": "CustomVariable", + "spec": { + "name": "custom_var", + "query": "prod : Production, staging : Staging, dev : Development", + "current": { + "text": [ + "Production" + ], + "value": [ + "prod" + ] + }, + "options": [ + { + "text": "Production", + "value": "prod" + }, + { + "text": "Staging", + "value": "staging" + }, + { + "text": "Development", + "value": "dev" + } + ], + "multi": true, + "includeAll": true, + "allValue": "*", + "label": "Custom Options", + "hide": "dontHide", + "skipUrlSync": false, + "description": "Custom multi-value variable", + "allowCustomValue": true + } + }, + { + "kind": "GroupByVariable", + "spec": { + "name": "group_by", + "datasource": { + "type": "prometheus", + "uid": "gdev-prometheus" + }, + "current": { + "text": "instance", + "value": "instance" + }, + "options": null, + "multi": false, + "label": "Group By", + "hide": "dontHide", + "skipUrlSync": false, + "description": "Group metrics by label" + } + }, + { + "kind": "AdhocVariable", + "spec": { + "name": "filters", + "datasource": { + "type": "prometheus", + "uid": "gdev-prometheus" + }, + "baseFilters": [ + { + "key": "job", + "operator": "=", + "value": "grafana", + "condition": "AND" + } + ], + "filters": [], + "defaultKeys": [ + { + "text": "job", + "value": "job", + "expandable": true + }, + { + "text": "instance", + "value": "instance", + "expandable": true + } + ], + "label": "Filters", + "hide": "dontHide", + "skipUrlSync": false, + "allowCustomValue": false + } + } + ] + }, + "status": {} + } } } } \ No newline at end of file diff --git a/apps/dashboard/pkg/migration/conversion/testdata/output/v2alpha1.complete.v1beta1.json b/apps/dashboard/pkg/migration/conversion/testdata/output/v2alpha1.complete.v1beta1.json index ed784b45834..c55f325789d 100644 --- a/apps/dashboard/pkg/migration/conversion/testdata/output/v2alpha1.complete.v1beta1.json +++ b/apps/dashboard/pkg/migration/conversion/testdata/output/v2alpha1.complete.v1beta1.json @@ -15,7 +15,505 @@ "conversion": { "failed": true, "error": "backend conversion not yet implemented", - "storedVersion": "v2alpha1" + "storedVersion": "v2alpha1", + "source": { + "kind": "Dashboard", + "apiVersion": "dashboard.grafana.app/v2alpha1", + "metadata": { + "name": "test-v2alpha1-complete", + "labels": { + "category": "test" + }, + "annotations": { + "description": "Complete example of v2alpha1 dashboard features" + } + }, + "spec": { + "annotations": [ + { + "kind": "AnnotationQuery", + "spec": { + "datasource": { + "type": "grafana", + "uid": "-- Grafana --" + }, + "query": { + "kind": "grafana", + "spec": { + "limit": 100, + "matchAny": false, + "tags": [], + "type": "dashboard" + } + }, + "enable": true, + "hide": false, + "iconColor": "rgba(0, 211, 255, 1)", + "name": "Annotations \u0026 Alerts", + "builtIn": true + } + }, + { + "kind": "AnnotationQuery", + "spec": { + "datasource": { + "type": "prometheus", + "uid": "gdev-prometheus" + }, + "query": { + "kind": "prometheus", + "spec": { + "expr": "changes(process_start_time_seconds[1m])", + "refId": "Anno" + } + }, + "enable": true, + "hide": false, + "iconColor": "yellow", + "name": "Prometheus Annotations", + "builtIn": false + } + } + ], + "cursorSync": "Tooltip", + "description": "This dashboard demonstrates all features that need to be converted from v2alpha1 to v2beta1", + "editable": true, + "elements": { + "panel-1": { + "kind": "Panel", + "spec": { + "id": 1, + "title": "Panel with Conditional Rendering", + "description": "This panel demonstrates conditional rendering features", + "links": null, + "data": { + "kind": "QueryGroup", + "spec": { + "queries": [ + { + "kind": "PanelQuery", + "spec": { + "query": { + "kind": "prometheus", + "spec": { + "expr": "up{job=\"grafana\"}" + } + }, + "datasource": { + "uid": "gdev-prometheus" + }, + "refId": "A", + "hidden": false + } + } + ], + "transformations": [ + { + "kind": "reduce", + "spec": { + "id": "reduce", + "options": { + "includeTimeField": false, + "mode": "reduceFields", + "reducers": [ + "mean" + ] + } + } + } + ], + "queryOptions": {} + } + }, + "vizConfig": { + "kind": "stat", + "spec": { + "pluginVersion": "12.1.0-pre", + "options": { + "colorMode": "value", + "graphMode": "area", + "justifyMode": "auto", + "textMode": "auto" + }, + "fieldConfig": { + "defaults": { + "mappings": [ + { + "type": "value", + "options": { + "0": { + "text": "Down", + "color": "red" + }, + "1": { + "text": "Up", + "color": "green" + } + } + } + ], + "thresholds": { + "mode": "absolute", + "steps": [ + { + "value": 0, + "color": "red" + }, + { + "value": 1, + "color": "green" + } + ] + }, + "color": { + "mode": "thresholds" + } + }, + "overrides": [] + } + } + } + } + } + }, + "layout": { + "kind": "RowsLayout", + "spec": { + "rows": [ + { + "kind": "Row", + "spec": { + "title": "Conditional Row", + "collapse": false, + "hideHeader": false, + "fillScreen": false, + "conditionalRendering": { + "kind": "ConditionalRenderingGroup", + "spec": { + "visibility": "show", + "condition": "and", + "items": [ + { + "kind": "ConditionalRenderingVariable", + "spec": { + "variable": "group_by", + "operator": "includes", + "value": "instance" + } + }, + { + "kind": "ConditionalRenderingData", + "spec": { + "value": true + } + }, + { + "kind": "ConditionalRenderingTimeRangeSize", + "spec": { + "value": "1h" + } + } + ] + } + }, + "layout": { + "kind": "GridLayout", + "spec": { + "items": [ + { + "kind": "GridLayoutItem", + "spec": { + "x": 0, + "y": 0, + "width": 24, + "height": 8, + "element": { + "kind": "ElementReference", + "name": "panel-1" + } + } + } + ] + } + } + } + } + ] + } + }, + "links": null, + "liveNow": true, + "preload": true, + "tags": [ + "test", + "example", + "migration" + ], + "timeSettings": { + "timezone": "browser", + "from": "now-6h", + "to": "now", + "autoRefresh": "10s", + "autoRefreshIntervals": [ + "5s", + "10s", + "30s", + "1m", + "5m", + "15m", + "30m", + "1h", + "2h", + "1d" + ], + "hideTimepicker": false, + "weekStart": "monday", + "fiscalYearStartMonth": 0 + }, + "title": "Test: Complete V2alpha1 Dashboard Example", + "variables": [ + { + "kind": "QueryVariable", + "spec": { + "name": "prometheus_query", + "current": { + "text": "All", + "value": [ + "$__all" + ] + }, + "label": "Prometheus Query", + "hide": "dontHide", + "refresh": "time", + "skipUrlSync": false, + "description": "Shows all up metrics", + "datasource": { + "type": "prometheus", + "uid": "gdev-prometheus" + }, + "query": { + "kind": "prometheus", + "spec": { + "expr": "up" + } + }, + "regex": "", + "sort": "alphabetical", + "definition": "up", + "options": null, + "multi": true, + "includeAll": true, + "allowCustomValue": false + } + }, + { + "kind": "TextVariable", + "spec": { + "name": "text_var", + "current": { + "selected": true, + "text": "server1", + "value": "server1" + }, + "query": "server1,server2,server3", + "label": "Text Variable", + "hide": "dontHide", + "skipUrlSync": false, + "description": "A simple text variable" + } + }, + { + "kind": "ConstantVariable", + "spec": { + "name": "constant_var", + "query": "production", + "current": { + "selected": true, + "text": "production", + "value": "production" + }, + "label": "Constant", + "hide": "dontHide", + "skipUrlSync": true, + "description": "A constant value" + } + }, + { + "kind": "DatasourceVariable", + "spec": { + "name": "ds_var", + "pluginId": "prometheus", + "refresh": "load", + "regex": "/^gdev-/", + "current": { + "text": "gdev-prometheus", + "value": "gdev-prometheus" + }, + "options": [ + { + "text": "gdev-prometheus", + "value": "gdev-prometheus" + } + ], + "multi": false, + "includeAll": false, + "label": "Datasource", + "hide": "dontHide", + "skipUrlSync": false, + "description": "Select a datasource", + "allowCustomValue": false + } + }, + { + "kind": "IntervalVariable", + "spec": { + "name": "interval", + "query": "1m,5m,10m,30m,1h,6h,12h,1d", + "current": { + "selected": true, + "text": "5m", + "value": "5m" + }, + "options": [ + { + "text": "1m", + "value": "1m" + }, + { + "text": "5m", + "value": "5m" + }, + { + "text": "10m", + "value": "10m" + }, + { + "text": "30m", + "value": "30m" + }, + { + "text": "1h", + "value": "1h" + }, + { + "text": "6h", + "value": "6h" + }, + { + "text": "12h", + "value": "12h" + }, + { + "text": "1d", + "value": "1d" + } + ], + "auto": true, + "auto_min": "10s", + "auto_count": 30, + "refresh": "load", + "label": "Interval", + "hide": "dontHide", + "skipUrlSync": false, + "description": "Time interval selection" + } + }, + { + "kind": "CustomVariable", + "spec": { + "name": "custom_var", + "query": "prod : Production, staging : Staging, dev : Development", + "current": { + "text": [ + "Production" + ], + "value": [ + "prod" + ] + }, + "options": [ + { + "text": "Production", + "value": "prod" + }, + { + "text": "Staging", + "value": "staging" + }, + { + "text": "Development", + "value": "dev" + } + ], + "multi": true, + "includeAll": true, + "allValue": "*", + "label": "Custom Options", + "hide": "dontHide", + "skipUrlSync": false, + "description": "Custom multi-value variable", + "allowCustomValue": true + } + }, + { + "kind": "GroupByVariable", + "spec": { + "name": "group_by", + "datasource": { + "type": "prometheus", + "uid": "gdev-prometheus" + }, + "current": { + "text": "instance", + "value": "instance" + }, + "options": null, + "multi": false, + "label": "Group By", + "hide": "dontHide", + "skipUrlSync": false, + "description": "Group metrics by label" + } + }, + { + "kind": "AdhocVariable", + "spec": { + "name": "filters", + "datasource": { + "type": "prometheus", + "uid": "gdev-prometheus" + }, + "baseFilters": [ + { + "key": "job", + "operator": "=", + "value": "grafana", + "condition": "AND" + } + ], + "filters": [], + "defaultKeys": [ + { + "text": "job", + "value": "job", + "expandable": true + }, + { + "text": "instance", + "value": "instance", + "expandable": true + } + ], + "label": "Filters", + "hide": "dontHide", + "skipUrlSync": false, + "allowCustomValue": false + } + } + ] + }, + "status": {} + } } } } \ No newline at end of file diff --git a/apps/dashboard/pkg/migration/conversion/testdata/output/v2alpha1.ds-data-query.v0alpha1.json b/apps/dashboard/pkg/migration/conversion/testdata/output/v2alpha1.ds-data-query.v0alpha1.json index 46365be1c23..4497423cc53 100644 --- a/apps/dashboard/pkg/migration/conversion/testdata/output/v2alpha1.ds-data-query.v0alpha1.json +++ b/apps/dashboard/pkg/migration/conversion/testdata/output/v2alpha1.ds-data-query.v0alpha1.json @@ -9,7 +9,1055 @@ "conversion": { "failed": true, "error": "backend conversion not yet implemented", - "storedVersion": "v2alpha1" + "storedVersion": "v2alpha1", + "source": { + "kind": "Dashboard", + "apiVersion": "dashboard.grafana.app/v2alpha1", + "metadata": { + "name": "test-v2alpha1-annotations" + }, + "spec": { + "annotations": [ + { + "kind": "AnnotationQuery", + "spec": { + "datasource": { + "type": "grafana", + "uid": "-- Grafana --" + }, + "query": { + "kind": "grafana", + "spec": { + "limit": 100, + "matchAny": false, + "tags": [], + "type": "dashboard" + } + }, + "enable": true, + "hide": false, + "iconColor": "rgba(0, 211, 255, 1)", + "name": "Annotations \u0026 Alerts", + "builtIn": true + } + }, + { + "kind": "AnnotationQuery", + "spec": { + "datasource": { + "type": "grafana-testdata-datasource", + "uid": "PD8C576611E62080A" + }, + "query": { + "kind": "grafana-testdata-datasource", + "spec": { + "lines": 10, + "refId": "Anno", + "scenarioId": "annotations" + } + }, + "enable": true, + "hide": false, + "iconColor": "blue", + "name": "testdata-annos", + "builtIn": false + } + }, + { + "kind": "AnnotationQuery", + "spec": { + "query": { + "kind": "prometheus", + "spec": { + "lines": 10, + "refId": "Anno", + "scenarioId": "annotations" + } + }, + "enable": true, + "hide": false, + "iconColor": "blue", + "name": "no-ds-testdata-annos", + "builtIn": false + } + }, + { + "kind": "AnnotationQuery", + "spec": { + "datasource": { + "type": "prometheus", + "uid": "gdev-prometheus" + }, + "query": { + "kind": "prometheus", + "spec": { + "expr": "{action=\"add_client\"}", + "interval": "", + "lines": 10, + "refId": "Anno", + "scenarioId": "annotations" + } + }, + "enable": true, + "hide": false, + "iconColor": "yellow", + "name": "prom-annos", + "builtIn": false + } + }, + { + "kind": "AnnotationQuery", + "spec": { + "query": { + "kind": "prometheus", + "spec": { + "expr": "{action=\"add_client\"}", + "interval": "", + "lines": 10, + "refId": "Anno", + "scenarioId": "annotations" + } + }, + "enable": true, + "hide": false, + "iconColor": "yellow", + "name": "no-ds-prom-annos", + "builtIn": false + } + }, + { + "kind": "AnnotationQuery", + "spec": { + "datasource": { + "type": "grafana-postgresql-datasource", + "uid": "PBBCEC2D313BC06C3" + }, + "query": { + "kind": "grafana-postgresql-datasource", + "spec": { + "editorMode": "builder", + "format": "table", + "lines": 10, + "rawSql": "", + "refId": "Anno", + "scenarioId": "annotations", + "sql": { + "columns": [ + { + "parameters": [], + "type": "function" + } + ], + "groupBy": [ + { + "property": { + "type": "string" + }, + "type": "groupBy" + } + ], + "limit": 50 + } + } + }, + "enable": true, + "hide": false, + "iconColor": "red", + "name": "postgress-annos", + "builtIn": false + } + }, + { + "kind": "AnnotationQuery", + "spec": { + "datasource": { + "type": "elasticsearch", + "uid": "gdev-elasticsearch" + }, + "query": { + "kind": "elasticsearch", + "spec": { + "lines": 10, + "query": "test query", + "refId": "Anno", + "scenarioId": "annotations" + } + }, + "enable": true, + "hide": false, + "iconColor": "red", + "name": "elastic - annos", + "builtIn": false, + "legacyOptions": { + "tagsField": "asd", + "textField": "asd", + "timeEndField": "asdas", + "timeField": "asd" + } + } + } + ], + "cursorSync": "Off", + "editable": true, + "elements": { + "panel-1": { + "kind": "Panel", + "spec": { + "id": 1, + "title": "Simple timeseries (WITH DS REF)", + "description": "", + "links": [], + "data": { + "kind": "QueryGroup", + "spec": { + "queries": [ + { + "kind": "PanelQuery", + "spec": { + "query": { + "kind": "grafana-testdata-datasource", + "spec": { + "scenarioId": "random_walk", + "seriesCount": 3 + } + }, + "datasource": { + "uid": "gdev-testdata" + }, + "refId": "A", + "hidden": false + } + } + ], + "transformations": [], + "queryOptions": {} + } + }, + "vizConfig": { + "kind": "timeseries", + "spec": { + "pluginVersion": "12.1.0-pre", + "options": { + "legend": { + "calcs": [], + "displayMode": "list", + "placement": "bottom", + "showLegend": true + }, + "tooltip": { + "hideZeros": false, + "mode": "single", + "sort": "none" + } + }, + "fieldConfig": { + "defaults": { + "thresholds": { + "mode": "absolute", + "steps": [ + { + "value": 0, + "color": "green" + }, + { + "value": 80, + "color": "red" + } + ] + }, + "color": { + "mode": "palette-classic" + }, + "custom": { + "axisBorderShow": false, + "axisCenteredZero": false, + "axisColorMode": "text", + "axisLabel": "", + "axisPlacement": "auto", + "barAlignment": 0, + "barWidthFactor": 0.6, + "drawStyle": "line", + "fillOpacity": 0, + "gradientMode": "none", + "hideFrom": { + "legend": false, + "tooltip": false, + "viz": false + }, + "insertNulls": false, + "lineInterpolation": "linear", + "lineWidth": 1, + "pointSize": 5, + "scaleDistribution": { + "type": "linear" + }, + "showPoints": "auto", + "spanNulls": false, + "stacking": { + "group": "A", + "mode": "none" + }, + "thresholdsStyle": { + "mode": "off" + } + } + }, + "overrides": [] + } + } + } + } + }, + "panel-2": { + "kind": "Panel", + "spec": { + "id": 2, + "title": "Simple stat (NO DS REF)", + "description": "", + "links": [], + "data": { + "kind": "QueryGroup", + "spec": { + "queries": [ + { + "kind": "PanelQuery", + "spec": { + "query": { + "kind": "grafana-testdata-datasource", + "spec": { + "scenarioId": "random_walk", + "seriesCount": 4 + } + }, + "refId": "A", + "hidden": false + } + } + ], + "transformations": [], + "queryOptions": {} + } + }, + "vizConfig": { + "kind": "stat", + "spec": { + "pluginVersion": "12.1.0-pre", + "options": { + "colorMode": "value", + "graphMode": "area", + "justifyMode": "auto", + "orientation": "auto", + "percentChangeColorMode": "standard", + "reduceOptions": { + "calcs": [ + "lastNotNull" + ], + "fields": "", + "values": false + }, + "showPercentChange": false, + "textMode": "auto", + "wideLayout": true + }, + "fieldConfig": { + "defaults": { + "thresholds": { + "mode": "absolute", + "steps": [ + { + "value": 0, + "color": "green" + }, + { + "value": 80, + "color": "red" + } + ] + }, + "color": { + "mode": "thresholds" + } + }, + "overrides": [] + } + } + } + } + }, + "panel-3": { + "kind": "Panel", + "spec": { + "id": 3, + "title": "Panel with NO REF to gdev-prometheus", + "description": "", + "links": [], + "data": { + "kind": "QueryGroup", + "spec": { + "queries": [ + { + "kind": "PanelQuery", + "spec": { + "query": { + "kind": "prometheus", + "spec": { + "disableTextWrap": false, + "editorMode": "builder", + "expr": "rate(counters_requests[$__rate_interval])", + "fullMetaSearch": false, + "includeNullMetadata": false, + "instant": false, + "legendFormat": "__auto", + "range": true, + "useBackend": false + } + }, + "refId": "A", + "hidden": false + } + } + ], + "transformations": [], + "queryOptions": {} + } + }, + "vizConfig": { + "kind": "timeseries", + "spec": { + "pluginVersion": "12.1.0-pre", + "options": { + "legend": { + "calcs": [], + "displayMode": "list", + "placement": "bottom", + "showLegend": true + }, + "tooltip": { + "hideZeros": false, + "mode": "single", + "sort": "none" + } + }, + "fieldConfig": { + "defaults": { + "thresholds": { + "mode": "absolute", + "steps": [ + { + "value": 0, + "color": "green" + }, + { + "value": 80, + "color": "red" + } + ] + }, + "color": { + "mode": "palette-classic" + }, + "custom": { + "axisBorderShow": false, + "axisCenteredZero": false, + "axisColorMode": "text", + "axisLabel": "", + "axisPlacement": "auto", + "barAlignment": 0, + "barWidthFactor": 0.6, + "drawStyle": "line", + "fillOpacity": 0, + "gradientMode": "none", + "hideFrom": { + "legend": false, + "tooltip": false, + "viz": false + }, + "insertNulls": false, + "lineInterpolation": "linear", + "lineWidth": 1, + "pointSize": 5, + "scaleDistribution": { + "type": "linear" + }, + "showPoints": "auto", + "spanNulls": false, + "stacking": { + "group": "A", + "mode": "none" + }, + "thresholdsStyle": { + "mode": "off" + } + } + }, + "overrides": [] + } + } + } + } + }, + "panel-4": { + "kind": "Panel", + "spec": { + "id": 4, + "title": "Panel with ref to gdev-prometheus", + "description": "", + "links": [], + "data": { + "kind": "QueryGroup", + "spec": { + "queries": [ + { + "kind": "PanelQuery", + "spec": { + "query": { + "kind": "prometheus", + "spec": { + "disableTextWrap": false, + "editorMode": "builder", + "expr": "rate(counters_requests[$__rate_interval])", + "fullMetaSearch": false, + "includeNullMetadata": false, + "instant": false, + "legendFormat": "__auto", + "range": true, + "useBackend": false + } + }, + "datasource": { + "uid": "gdev-prometheus" + }, + "refId": "A", + "hidden": false + } + } + ], + "transformations": [], + "queryOptions": {} + } + }, + "vizConfig": { + "kind": "timeseries", + "spec": { + "pluginVersion": "12.1.0-pre", + "options": { + "legend": { + "calcs": [], + "displayMode": "list", + "placement": "bottom", + "showLegend": true + }, + "tooltip": { + "hideZeros": false, + "mode": "single", + "sort": "none" + } + }, + "fieldConfig": { + "defaults": { + "thresholds": { + "mode": "absolute", + "steps": [ + { + "value": 0, + "color": "green" + }, + { + "value": 80, + "color": "red" + } + ] + }, + "color": { + "mode": "palette-classic" + }, + "custom": { + "axisBorderShow": false, + "axisCenteredZero": false, + "axisColorMode": "text", + "axisLabel": "", + "axisPlacement": "auto", + "barAlignment": 0, + "barWidthFactor": 0.6, + "drawStyle": "line", + "fillOpacity": 0, + "gradientMode": "none", + "hideFrom": { + "legend": false, + "tooltip": false, + "viz": false + }, + "insertNulls": false, + "lineInterpolation": "linear", + "lineWidth": 1, + "pointSize": 5, + "scaleDistribution": { + "type": "linear" + }, + "showPoints": "auto", + "spanNulls": false, + "stacking": { + "group": "A", + "mode": "none" + }, + "thresholdsStyle": { + "mode": "off" + } + } + }, + "overrides": [] + } + } + } + } + }, + "panel-5": { + "kind": "Panel", + "spec": { + "id": 5, + "title": "Mixed DS WITH REFS", + "description": "", + "links": [], + "data": { + "kind": "QueryGroup", + "spec": { + "queries": [ + { + "kind": "PanelQuery", + "spec": { + "query": { + "kind": "prometheus", + "spec": { + "disableTextWrap": false, + "editorMode": "builder", + "expr": "rate(counters_requests{server=\"backend-01\"}[$__rate_interval])", + "fullMetaSearch": false, + "includeNullMetadata": false, + "legendFormat": "__auto", + "range": true, + "useBackend": false + } + }, + "datasource": { + "uid": "gdev-prometheus" + }, + "refId": "A", + "hidden": false + } + }, + { + "kind": "PanelQuery", + "spec": { + "query": { + "kind": "grafana-testdata-datasource", + "spec": {} + }, + "datasource": { + "uid": "gdev-testdata" + }, + "refId": "B", + "hidden": false + } + } + ], + "transformations": [], + "queryOptions": {} + } + }, + "vizConfig": { + "kind": "timeseries", + "spec": { + "pluginVersion": "12.1.0-pre", + "options": { + "legend": { + "calcs": [], + "displayMode": "list", + "placement": "bottom", + "showLegend": true + }, + "tooltip": { + "hideZeros": false, + "mode": "single", + "sort": "none" + } + }, + "fieldConfig": { + "defaults": { + "thresholds": { + "mode": "absolute", + "steps": [ + { + "value": 0, + "color": "green" + }, + { + "value": 80, + "color": "red" + } + ] + }, + "color": { + "mode": "palette-classic" + }, + "custom": { + "axisBorderShow": false, + "axisCenteredZero": false, + "axisColorMode": "text", + "axisLabel": "", + "axisPlacement": "auto", + "barAlignment": 0, + "barWidthFactor": 0.6, + "drawStyle": "line", + "fillOpacity": 0, + "gradientMode": "none", + "hideFrom": { + "legend": false, + "tooltip": false, + "viz": false + }, + "insertNulls": false, + "lineInterpolation": "linear", + "lineWidth": 1, + "pointSize": 5, + "scaleDistribution": { + "type": "linear" + }, + "showPoints": "auto", + "spanNulls": false, + "stacking": { + "group": "A", + "mode": "none" + }, + "thresholdsStyle": { + "mode": "off" + } + } + }, + "overrides": [] + } + } + } + } + }, + "panel-6": { + "kind": "Panel", + "spec": { + "id": 6, + "title": "Mixed DS WITHOUT REFS", + "description": "", + "links": [], + "data": { + "kind": "QueryGroup", + "spec": { + "queries": [ + { + "kind": "PanelQuery", + "spec": { + "query": { + "kind": "prometheus", + "spec": { + "disableTextWrap": false, + "editorMode": "builder", + "expr": "rate(counters_requests{server=\"backend-01\"}[$__rate_interval])", + "fullMetaSearch": false, + "includeNullMetadata": false, + "legendFormat": "__auto", + "range": true, + "useBackend": false + } + }, + "refId": "A", + "hidden": false + } + }, + { + "kind": "PanelQuery", + "spec": { + "query": { + "kind": "grafana-testdata-datasource", + "spec": {} + }, + "refId": "B", + "hidden": false + } + } + ], + "transformations": [], + "queryOptions": {} + } + }, + "vizConfig": { + "kind": "timeseries", + "spec": { + "pluginVersion": "12.1.0-pre", + "options": { + "legend": { + "calcs": [], + "displayMode": "list", + "placement": "bottom", + "showLegend": true + }, + "tooltip": { + "hideZeros": false, + "mode": "single", + "sort": "none" + } + }, + "fieldConfig": { + "defaults": { + "thresholds": { + "mode": "absolute", + "steps": [ + { + "value": 0, + "color": "green" + }, + { + "value": 80, + "color": "red" + } + ] + }, + "color": { + "mode": "palette-classic" + }, + "custom": { + "axisBorderShow": false, + "axisCenteredZero": false, + "axisColorMode": "text", + "axisLabel": "", + "axisPlacement": "auto", + "barAlignment": 0, + "barWidthFactor": 0.6, + "drawStyle": "line", + "fillOpacity": 0, + "gradientMode": "none", + "hideFrom": { + "legend": false, + "tooltip": false, + "viz": false + }, + "insertNulls": false, + "lineInterpolation": "linear", + "lineWidth": 1, + "pointSize": 5, + "scaleDistribution": { + "type": "linear" + }, + "showPoints": "auto", + "spanNulls": false, + "stacking": { + "group": "A", + "mode": "none" + }, + "thresholdsStyle": { + "mode": "off" + } + } + }, + "overrides": [] + } + } + } + } + } + }, + "layout": { + "kind": "AutoGridLayout", + "spec": { + "maxColumnCount": 3, + "columnWidthMode": "standard", + "rowHeightMode": "standard", + "items": [ + { + "kind": "AutoGridLayoutItem", + "spec": { + "element": { + "kind": "ElementReference", + "name": "panel-3" + } + } + }, + { + "kind": "AutoGridLayoutItem", + "spec": { + "element": { + "kind": "ElementReference", + "name": "panel-4" + } + } + }, + { + "kind": "AutoGridLayoutItem", + "spec": { + "element": { + "kind": "ElementReference", + "name": "panel-2" + } + } + }, + { + "kind": "AutoGridLayoutItem", + "spec": { + "element": { + "kind": "ElementReference", + "name": "panel-1" + } + } + }, + { + "kind": "AutoGridLayoutItem", + "spec": { + "element": { + "kind": "ElementReference", + "name": "panel-5" + } + } + }, + { + "kind": "AutoGridLayoutItem", + "spec": { + "element": { + "kind": "ElementReference", + "name": "panel-6" + } + } + } + ] + } + }, + "links": [], + "liveNow": false, + "preload": false, + "tags": [], + "timeSettings": { + "timezone": "browser", + "from": "now-6h", + "to": "now", + "autoRefresh": "", + "autoRefreshIntervals": [ + "5s", + "10s", + "30s", + "1m", + "5m", + "15m", + "30m", + "1h", + "2h", + "1d" + ], + "hideTimepicker": false, + "fiscalYearStartMonth": 0 + }, + "title": "Test: V2alpha1 dashboard with annotations", + "variables": [ + { + "kind": "QueryVariable", + "spec": { + "name": "variable-ds-prometheus", + "current": { + "text": null, + "value": null + }, + "hide": "dontHide", + "refresh": "", + "skipUrlSync": false, + "datasource": { + "uid": "gdev-prometheus" + }, + "query": { + "kind": "prometheus", + "spec": { + "expr": "up" + } + }, + "regex": "", + "sort": "", + "options": null, + "multi": false, + "includeAll": false, + "allowCustomValue": false + } + }, + { + "kind": "QueryVariable", + "spec": { + "name": "variable-no-ds", + "current": { + "text": null, + "value": null + }, + "hide": "dontHide", + "refresh": "", + "skipUrlSync": false, + "query": { + "kind": "grafana-testdata-datasource", + "spec": { + "csv": "1,2,3,4", + "scenarioId": "csv_metric_values" + } + }, + "regex": "", + "sort": "", + "options": null, + "multi": false, + "includeAll": false, + "allowCustomValue": false + } + }, + { + "kind": "QueryVariable", + "spec": { + "name": "variable-no-ds-empty-query", + "current": { + "text": null, + "value": null + }, + "hide": "dontHide", + "refresh": "", + "skipUrlSync": false, + "query": { + "kind": "grafana-testdata-datasource", + "spec": {} + }, + "regex": "", + "sort": "", + "options": null, + "multi": false, + "includeAll": false, + "allowCustomValue": false + } + }, + { + "kind": "QueryVariable", + "spec": { + "name": "variable-no-default-ds", + "current": { + "text": null, + "value": null + }, + "hide": "dontHide", + "refresh": "", + "skipUrlSync": false, + "query": { + "kind": "prometheus", + "spec": { + "expr": "up" + } + }, + "regex": "", + "sort": "", + "options": null, + "multi": false, + "includeAll": false, + "allowCustomValue": false + } + } + ] + }, + "status": {} + } } } } \ No newline at end of file diff --git a/apps/dashboard/pkg/migration/conversion/testdata/output/v2alpha1.ds-data-query.v1beta1.json b/apps/dashboard/pkg/migration/conversion/testdata/output/v2alpha1.ds-data-query.v1beta1.json index a0cf65052a7..11151fc1345 100644 --- a/apps/dashboard/pkg/migration/conversion/testdata/output/v2alpha1.ds-data-query.v1beta1.json +++ b/apps/dashboard/pkg/migration/conversion/testdata/output/v2alpha1.ds-data-query.v1beta1.json @@ -9,7 +9,1055 @@ "conversion": { "failed": true, "error": "backend conversion not yet implemented", - "storedVersion": "v2alpha1" + "storedVersion": "v2alpha1", + "source": { + "kind": "Dashboard", + "apiVersion": "dashboard.grafana.app/v2alpha1", + "metadata": { + "name": "test-v2alpha1-annotations" + }, + "spec": { + "annotations": [ + { + "kind": "AnnotationQuery", + "spec": { + "datasource": { + "type": "grafana", + "uid": "-- Grafana --" + }, + "query": { + "kind": "grafana", + "spec": { + "limit": 100, + "matchAny": false, + "tags": [], + "type": "dashboard" + } + }, + "enable": true, + "hide": false, + "iconColor": "rgba(0, 211, 255, 1)", + "name": "Annotations \u0026 Alerts", + "builtIn": true + } + }, + { + "kind": "AnnotationQuery", + "spec": { + "datasource": { + "type": "grafana-testdata-datasource", + "uid": "PD8C576611E62080A" + }, + "query": { + "kind": "grafana-testdata-datasource", + "spec": { + "lines": 10, + "refId": "Anno", + "scenarioId": "annotations" + } + }, + "enable": true, + "hide": false, + "iconColor": "blue", + "name": "testdata-annos", + "builtIn": false + } + }, + { + "kind": "AnnotationQuery", + "spec": { + "query": { + "kind": "prometheus", + "spec": { + "lines": 10, + "refId": "Anno", + "scenarioId": "annotations" + } + }, + "enable": true, + "hide": false, + "iconColor": "blue", + "name": "no-ds-testdata-annos", + "builtIn": false + } + }, + { + "kind": "AnnotationQuery", + "spec": { + "datasource": { + "type": "prometheus", + "uid": "gdev-prometheus" + }, + "query": { + "kind": "prometheus", + "spec": { + "expr": "{action=\"add_client\"}", + "interval": "", + "lines": 10, + "refId": "Anno", + "scenarioId": "annotations" + } + }, + "enable": true, + "hide": false, + "iconColor": "yellow", + "name": "prom-annos", + "builtIn": false + } + }, + { + "kind": "AnnotationQuery", + "spec": { + "query": { + "kind": "prometheus", + "spec": { + "expr": "{action=\"add_client\"}", + "interval": "", + "lines": 10, + "refId": "Anno", + "scenarioId": "annotations" + } + }, + "enable": true, + "hide": false, + "iconColor": "yellow", + "name": "no-ds-prom-annos", + "builtIn": false + } + }, + { + "kind": "AnnotationQuery", + "spec": { + "datasource": { + "type": "grafana-postgresql-datasource", + "uid": "PBBCEC2D313BC06C3" + }, + "query": { + "kind": "grafana-postgresql-datasource", + "spec": { + "editorMode": "builder", + "format": "table", + "lines": 10, + "rawSql": "", + "refId": "Anno", + "scenarioId": "annotations", + "sql": { + "columns": [ + { + "parameters": [], + "type": "function" + } + ], + "groupBy": [ + { + "property": { + "type": "string" + }, + "type": "groupBy" + } + ], + "limit": 50 + } + } + }, + "enable": true, + "hide": false, + "iconColor": "red", + "name": "postgress-annos", + "builtIn": false + } + }, + { + "kind": "AnnotationQuery", + "spec": { + "datasource": { + "type": "elasticsearch", + "uid": "gdev-elasticsearch" + }, + "query": { + "kind": "elasticsearch", + "spec": { + "lines": 10, + "query": "test query", + "refId": "Anno", + "scenarioId": "annotations" + } + }, + "enable": true, + "hide": false, + "iconColor": "red", + "name": "elastic - annos", + "builtIn": false, + "legacyOptions": { + "tagsField": "asd", + "textField": "asd", + "timeEndField": "asdas", + "timeField": "asd" + } + } + } + ], + "cursorSync": "Off", + "editable": true, + "elements": { + "panel-1": { + "kind": "Panel", + "spec": { + "id": 1, + "title": "Simple timeseries (WITH DS REF)", + "description": "", + "links": [], + "data": { + "kind": "QueryGroup", + "spec": { + "queries": [ + { + "kind": "PanelQuery", + "spec": { + "query": { + "kind": "grafana-testdata-datasource", + "spec": { + "scenarioId": "random_walk", + "seriesCount": 3 + } + }, + "datasource": { + "uid": "gdev-testdata" + }, + "refId": "A", + "hidden": false + } + } + ], + "transformations": [], + "queryOptions": {} + } + }, + "vizConfig": { + "kind": "timeseries", + "spec": { + "pluginVersion": "12.1.0-pre", + "options": { + "legend": { + "calcs": [], + "displayMode": "list", + "placement": "bottom", + "showLegend": true + }, + "tooltip": { + "hideZeros": false, + "mode": "single", + "sort": "none" + } + }, + "fieldConfig": { + "defaults": { + "thresholds": { + "mode": "absolute", + "steps": [ + { + "value": 0, + "color": "green" + }, + { + "value": 80, + "color": "red" + } + ] + }, + "color": { + "mode": "palette-classic" + }, + "custom": { + "axisBorderShow": false, + "axisCenteredZero": false, + "axisColorMode": "text", + "axisLabel": "", + "axisPlacement": "auto", + "barAlignment": 0, + "barWidthFactor": 0.6, + "drawStyle": "line", + "fillOpacity": 0, + "gradientMode": "none", + "hideFrom": { + "legend": false, + "tooltip": false, + "viz": false + }, + "insertNulls": false, + "lineInterpolation": "linear", + "lineWidth": 1, + "pointSize": 5, + "scaleDistribution": { + "type": "linear" + }, + "showPoints": "auto", + "spanNulls": false, + "stacking": { + "group": "A", + "mode": "none" + }, + "thresholdsStyle": { + "mode": "off" + } + } + }, + "overrides": [] + } + } + } + } + }, + "panel-2": { + "kind": "Panel", + "spec": { + "id": 2, + "title": "Simple stat (NO DS REF)", + "description": "", + "links": [], + "data": { + "kind": "QueryGroup", + "spec": { + "queries": [ + { + "kind": "PanelQuery", + "spec": { + "query": { + "kind": "grafana-testdata-datasource", + "spec": { + "scenarioId": "random_walk", + "seriesCount": 4 + } + }, + "refId": "A", + "hidden": false + } + } + ], + "transformations": [], + "queryOptions": {} + } + }, + "vizConfig": { + "kind": "stat", + "spec": { + "pluginVersion": "12.1.0-pre", + "options": { + "colorMode": "value", + "graphMode": "area", + "justifyMode": "auto", + "orientation": "auto", + "percentChangeColorMode": "standard", + "reduceOptions": { + "calcs": [ + "lastNotNull" + ], + "fields": "", + "values": false + }, + "showPercentChange": false, + "textMode": "auto", + "wideLayout": true + }, + "fieldConfig": { + "defaults": { + "thresholds": { + "mode": "absolute", + "steps": [ + { + "value": 0, + "color": "green" + }, + { + "value": 80, + "color": "red" + } + ] + }, + "color": { + "mode": "thresholds" + } + }, + "overrides": [] + } + } + } + } + }, + "panel-3": { + "kind": "Panel", + "spec": { + "id": 3, + "title": "Panel with NO REF to gdev-prometheus", + "description": "", + "links": [], + "data": { + "kind": "QueryGroup", + "spec": { + "queries": [ + { + "kind": "PanelQuery", + "spec": { + "query": { + "kind": "prometheus", + "spec": { + "disableTextWrap": false, + "editorMode": "builder", + "expr": "rate(counters_requests[$__rate_interval])", + "fullMetaSearch": false, + "includeNullMetadata": false, + "instant": false, + "legendFormat": "__auto", + "range": true, + "useBackend": false + } + }, + "refId": "A", + "hidden": false + } + } + ], + "transformations": [], + "queryOptions": {} + } + }, + "vizConfig": { + "kind": "timeseries", + "spec": { + "pluginVersion": "12.1.0-pre", + "options": { + "legend": { + "calcs": [], + "displayMode": "list", + "placement": "bottom", + "showLegend": true + }, + "tooltip": { + "hideZeros": false, + "mode": "single", + "sort": "none" + } + }, + "fieldConfig": { + "defaults": { + "thresholds": { + "mode": "absolute", + "steps": [ + { + "value": 0, + "color": "green" + }, + { + "value": 80, + "color": "red" + } + ] + }, + "color": { + "mode": "palette-classic" + }, + "custom": { + "axisBorderShow": false, + "axisCenteredZero": false, + "axisColorMode": "text", + "axisLabel": "", + "axisPlacement": "auto", + "barAlignment": 0, + "barWidthFactor": 0.6, + "drawStyle": "line", + "fillOpacity": 0, + "gradientMode": "none", + "hideFrom": { + "legend": false, + "tooltip": false, + "viz": false + }, + "insertNulls": false, + "lineInterpolation": "linear", + "lineWidth": 1, + "pointSize": 5, + "scaleDistribution": { + "type": "linear" + }, + "showPoints": "auto", + "spanNulls": false, + "stacking": { + "group": "A", + "mode": "none" + }, + "thresholdsStyle": { + "mode": "off" + } + } + }, + "overrides": [] + } + } + } + } + }, + "panel-4": { + "kind": "Panel", + "spec": { + "id": 4, + "title": "Panel with ref to gdev-prometheus", + "description": "", + "links": [], + "data": { + "kind": "QueryGroup", + "spec": { + "queries": [ + { + "kind": "PanelQuery", + "spec": { + "query": { + "kind": "prometheus", + "spec": { + "disableTextWrap": false, + "editorMode": "builder", + "expr": "rate(counters_requests[$__rate_interval])", + "fullMetaSearch": false, + "includeNullMetadata": false, + "instant": false, + "legendFormat": "__auto", + "range": true, + "useBackend": false + } + }, + "datasource": { + "uid": "gdev-prometheus" + }, + "refId": "A", + "hidden": false + } + } + ], + "transformations": [], + "queryOptions": {} + } + }, + "vizConfig": { + "kind": "timeseries", + "spec": { + "pluginVersion": "12.1.0-pre", + "options": { + "legend": { + "calcs": [], + "displayMode": "list", + "placement": "bottom", + "showLegend": true + }, + "tooltip": { + "hideZeros": false, + "mode": "single", + "sort": "none" + } + }, + "fieldConfig": { + "defaults": { + "thresholds": { + "mode": "absolute", + "steps": [ + { + "value": 0, + "color": "green" + }, + { + "value": 80, + "color": "red" + } + ] + }, + "color": { + "mode": "palette-classic" + }, + "custom": { + "axisBorderShow": false, + "axisCenteredZero": false, + "axisColorMode": "text", + "axisLabel": "", + "axisPlacement": "auto", + "barAlignment": 0, + "barWidthFactor": 0.6, + "drawStyle": "line", + "fillOpacity": 0, + "gradientMode": "none", + "hideFrom": { + "legend": false, + "tooltip": false, + "viz": false + }, + "insertNulls": false, + "lineInterpolation": "linear", + "lineWidth": 1, + "pointSize": 5, + "scaleDistribution": { + "type": "linear" + }, + "showPoints": "auto", + "spanNulls": false, + "stacking": { + "group": "A", + "mode": "none" + }, + "thresholdsStyle": { + "mode": "off" + } + } + }, + "overrides": [] + } + } + } + } + }, + "panel-5": { + "kind": "Panel", + "spec": { + "id": 5, + "title": "Mixed DS WITH REFS", + "description": "", + "links": [], + "data": { + "kind": "QueryGroup", + "spec": { + "queries": [ + { + "kind": "PanelQuery", + "spec": { + "query": { + "kind": "prometheus", + "spec": { + "disableTextWrap": false, + "editorMode": "builder", + "expr": "rate(counters_requests{server=\"backend-01\"}[$__rate_interval])", + "fullMetaSearch": false, + "includeNullMetadata": false, + "legendFormat": "__auto", + "range": true, + "useBackend": false + } + }, + "datasource": { + "uid": "gdev-prometheus" + }, + "refId": "A", + "hidden": false + } + }, + { + "kind": "PanelQuery", + "spec": { + "query": { + "kind": "grafana-testdata-datasource", + "spec": {} + }, + "datasource": { + "uid": "gdev-testdata" + }, + "refId": "B", + "hidden": false + } + } + ], + "transformations": [], + "queryOptions": {} + } + }, + "vizConfig": { + "kind": "timeseries", + "spec": { + "pluginVersion": "12.1.0-pre", + "options": { + "legend": { + "calcs": [], + "displayMode": "list", + "placement": "bottom", + "showLegend": true + }, + "tooltip": { + "hideZeros": false, + "mode": "single", + "sort": "none" + } + }, + "fieldConfig": { + "defaults": { + "thresholds": { + "mode": "absolute", + "steps": [ + { + "value": 0, + "color": "green" + }, + { + "value": 80, + "color": "red" + } + ] + }, + "color": { + "mode": "palette-classic" + }, + "custom": { + "axisBorderShow": false, + "axisCenteredZero": false, + "axisColorMode": "text", + "axisLabel": "", + "axisPlacement": "auto", + "barAlignment": 0, + "barWidthFactor": 0.6, + "drawStyle": "line", + "fillOpacity": 0, + "gradientMode": "none", + "hideFrom": { + "legend": false, + "tooltip": false, + "viz": false + }, + "insertNulls": false, + "lineInterpolation": "linear", + "lineWidth": 1, + "pointSize": 5, + "scaleDistribution": { + "type": "linear" + }, + "showPoints": "auto", + "spanNulls": false, + "stacking": { + "group": "A", + "mode": "none" + }, + "thresholdsStyle": { + "mode": "off" + } + } + }, + "overrides": [] + } + } + } + } + }, + "panel-6": { + "kind": "Panel", + "spec": { + "id": 6, + "title": "Mixed DS WITHOUT REFS", + "description": "", + "links": [], + "data": { + "kind": "QueryGroup", + "spec": { + "queries": [ + { + "kind": "PanelQuery", + "spec": { + "query": { + "kind": "prometheus", + "spec": { + "disableTextWrap": false, + "editorMode": "builder", + "expr": "rate(counters_requests{server=\"backend-01\"}[$__rate_interval])", + "fullMetaSearch": false, + "includeNullMetadata": false, + "legendFormat": "__auto", + "range": true, + "useBackend": false + } + }, + "refId": "A", + "hidden": false + } + }, + { + "kind": "PanelQuery", + "spec": { + "query": { + "kind": "grafana-testdata-datasource", + "spec": {} + }, + "refId": "B", + "hidden": false + } + } + ], + "transformations": [], + "queryOptions": {} + } + }, + "vizConfig": { + "kind": "timeseries", + "spec": { + "pluginVersion": "12.1.0-pre", + "options": { + "legend": { + "calcs": [], + "displayMode": "list", + "placement": "bottom", + "showLegend": true + }, + "tooltip": { + "hideZeros": false, + "mode": "single", + "sort": "none" + } + }, + "fieldConfig": { + "defaults": { + "thresholds": { + "mode": "absolute", + "steps": [ + { + "value": 0, + "color": "green" + }, + { + "value": 80, + "color": "red" + } + ] + }, + "color": { + "mode": "palette-classic" + }, + "custom": { + "axisBorderShow": false, + "axisCenteredZero": false, + "axisColorMode": "text", + "axisLabel": "", + "axisPlacement": "auto", + "barAlignment": 0, + "barWidthFactor": 0.6, + "drawStyle": "line", + "fillOpacity": 0, + "gradientMode": "none", + "hideFrom": { + "legend": false, + "tooltip": false, + "viz": false + }, + "insertNulls": false, + "lineInterpolation": "linear", + "lineWidth": 1, + "pointSize": 5, + "scaleDistribution": { + "type": "linear" + }, + "showPoints": "auto", + "spanNulls": false, + "stacking": { + "group": "A", + "mode": "none" + }, + "thresholdsStyle": { + "mode": "off" + } + } + }, + "overrides": [] + } + } + } + } + } + }, + "layout": { + "kind": "AutoGridLayout", + "spec": { + "maxColumnCount": 3, + "columnWidthMode": "standard", + "rowHeightMode": "standard", + "items": [ + { + "kind": "AutoGridLayoutItem", + "spec": { + "element": { + "kind": "ElementReference", + "name": "panel-3" + } + } + }, + { + "kind": "AutoGridLayoutItem", + "spec": { + "element": { + "kind": "ElementReference", + "name": "panel-4" + } + } + }, + { + "kind": "AutoGridLayoutItem", + "spec": { + "element": { + "kind": "ElementReference", + "name": "panel-2" + } + } + }, + { + "kind": "AutoGridLayoutItem", + "spec": { + "element": { + "kind": "ElementReference", + "name": "panel-1" + } + } + }, + { + "kind": "AutoGridLayoutItem", + "spec": { + "element": { + "kind": "ElementReference", + "name": "panel-5" + } + } + }, + { + "kind": "AutoGridLayoutItem", + "spec": { + "element": { + "kind": "ElementReference", + "name": "panel-6" + } + } + } + ] + } + }, + "links": [], + "liveNow": false, + "preload": false, + "tags": [], + "timeSettings": { + "timezone": "browser", + "from": "now-6h", + "to": "now", + "autoRefresh": "", + "autoRefreshIntervals": [ + "5s", + "10s", + "30s", + "1m", + "5m", + "15m", + "30m", + "1h", + "2h", + "1d" + ], + "hideTimepicker": false, + "fiscalYearStartMonth": 0 + }, + "title": "Test: V2alpha1 dashboard with annotations", + "variables": [ + { + "kind": "QueryVariable", + "spec": { + "name": "variable-ds-prometheus", + "current": { + "text": null, + "value": null + }, + "hide": "dontHide", + "refresh": "", + "skipUrlSync": false, + "datasource": { + "uid": "gdev-prometheus" + }, + "query": { + "kind": "prometheus", + "spec": { + "expr": "up" + } + }, + "regex": "", + "sort": "", + "options": null, + "multi": false, + "includeAll": false, + "allowCustomValue": false + } + }, + { + "kind": "QueryVariable", + "spec": { + "name": "variable-no-ds", + "current": { + "text": null, + "value": null + }, + "hide": "dontHide", + "refresh": "", + "skipUrlSync": false, + "query": { + "kind": "grafana-testdata-datasource", + "spec": { + "csv": "1,2,3,4", + "scenarioId": "csv_metric_values" + } + }, + "regex": "", + "sort": "", + "options": null, + "multi": false, + "includeAll": false, + "allowCustomValue": false + } + }, + { + "kind": "QueryVariable", + "spec": { + "name": "variable-no-ds-empty-query", + "current": { + "text": null, + "value": null + }, + "hide": "dontHide", + "refresh": "", + "skipUrlSync": false, + "query": { + "kind": "grafana-testdata-datasource", + "spec": {} + }, + "regex": "", + "sort": "", + "options": null, + "multi": false, + "includeAll": false, + "allowCustomValue": false + } + }, + { + "kind": "QueryVariable", + "spec": { + "name": "variable-no-default-ds", + "current": { + "text": null, + "value": null + }, + "hide": "dontHide", + "refresh": "", + "skipUrlSync": false, + "query": { + "kind": "prometheus", + "spec": { + "expr": "up" + } + }, + "regex": "", + "sort": "", + "options": null, + "multi": false, + "includeAll": false, + "allowCustomValue": false + } + } + ] + }, + "status": {} + } } } } \ No newline at end of file diff --git a/apps/dashboard/pkg/migration/conversion/testdata/output/v2alpha1.groupby-adhoc-vars.v0alpha1.json b/apps/dashboard/pkg/migration/conversion/testdata/output/v2alpha1.groupby-adhoc-vars.v0alpha1.json index add335f4b5e..ae50ed46c5d 100644 --- a/apps/dashboard/pkg/migration/conversion/testdata/output/v2alpha1.groupby-adhoc-vars.v0alpha1.json +++ b/apps/dashboard/pkg/migration/conversion/testdata/output/v2alpha1.groupby-adhoc-vars.v0alpha1.json @@ -9,7 +9,100 @@ "conversion": { "failed": true, "error": "backend conversion not yet implemented", - "storedVersion": "v2alpha1" + "storedVersion": "v2alpha1", + "source": { + "kind": "Dashboard", + "apiVersion": "dashboard.grafana.app/v2alpha1", + "metadata": { + "name": "test-v2alpha1-groupby-adhoc-vars" + }, + "spec": { + "annotations": null, + "cursorSync": "", + "elements": null, + "layout": null, + "links": null, + "preload": false, + "tags": null, + "timeSettings": { + "from": "", + "to": "", + "autoRefresh": "", + "autoRefreshIntervals": null, + "hideTimepicker": false, + "fiscalYearStartMonth": 0 + }, + "title": "Test: V2alpha1 dashboard with group by and adhoc variables", + "variables": [ + { + "kind": "GroupByVariable", + "spec": { + "name": "", + "datasource": { + "type": "prometheus", + "uid": "gdev-prometheus" + }, + "current": { + "text": "text7", + "value": "value7" + }, + "options": null, + "multi": false, + "label": "Group By Variable", + "hide": "dontHide", + "skipUrlSync": false, + "description": "A group by variable" + } + }, + { + "kind": "AdhocVariable", + "spec": { + "name": "adhocVar", + "datasource": { + "type": "prometheus", + "uid": "datasource-3" + }, + "baseFilters": [ + { + "key": "key1", + "operator": "=", + "value": "value1", + "condition": "AND" + }, + { + "key": "key2", + "operator": "=", + "value": "value2", + "condition": "OR" + } + ], + "filters": [ + { + "key": "key3", + "operator": "=", + "value": "value3", + "condition": "AND" + } + ], + "defaultKeys": [ + { + "text": "defaultKey1", + "value": "defaultKey1", + "group": "defaultGroup1", + "expandable": true + } + ], + "label": "Adhoc Variable", + "hide": "dontHide", + "skipUrlSync": false, + "description": "An adhoc variable", + "allowCustomValue": true + } + } + ] + }, + "status": {} + } } } } \ No newline at end of file diff --git a/apps/dashboard/pkg/migration/conversion/testdata/output/v2alpha1.groupby-adhoc-vars.v1beta1.json b/apps/dashboard/pkg/migration/conversion/testdata/output/v2alpha1.groupby-adhoc-vars.v1beta1.json index 4656a14bc7b..4f7ce909299 100644 --- a/apps/dashboard/pkg/migration/conversion/testdata/output/v2alpha1.groupby-adhoc-vars.v1beta1.json +++ b/apps/dashboard/pkg/migration/conversion/testdata/output/v2alpha1.groupby-adhoc-vars.v1beta1.json @@ -9,7 +9,100 @@ "conversion": { "failed": true, "error": "backend conversion not yet implemented", - "storedVersion": "v2alpha1" + "storedVersion": "v2alpha1", + "source": { + "kind": "Dashboard", + "apiVersion": "dashboard.grafana.app/v2alpha1", + "metadata": { + "name": "test-v2alpha1-groupby-adhoc-vars" + }, + "spec": { + "annotations": null, + "cursorSync": "", + "elements": null, + "layout": null, + "links": null, + "preload": false, + "tags": null, + "timeSettings": { + "from": "", + "to": "", + "autoRefresh": "", + "autoRefreshIntervals": null, + "hideTimepicker": false, + "fiscalYearStartMonth": 0 + }, + "title": "Test: V2alpha1 dashboard with group by and adhoc variables", + "variables": [ + { + "kind": "GroupByVariable", + "spec": { + "name": "", + "datasource": { + "type": "prometheus", + "uid": "gdev-prometheus" + }, + "current": { + "text": "text7", + "value": "value7" + }, + "options": null, + "multi": false, + "label": "Group By Variable", + "hide": "dontHide", + "skipUrlSync": false, + "description": "A group by variable" + } + }, + { + "kind": "AdhocVariable", + "spec": { + "name": "adhocVar", + "datasource": { + "type": "prometheus", + "uid": "datasource-3" + }, + "baseFilters": [ + { + "key": "key1", + "operator": "=", + "value": "value1", + "condition": "AND" + }, + { + "key": "key2", + "operator": "=", + "value": "value2", + "condition": "OR" + } + ], + "filters": [ + { + "key": "key3", + "operator": "=", + "value": "value3", + "condition": "AND" + } + ], + "defaultKeys": [ + { + "text": "defaultKey1", + "value": "defaultKey1", + "group": "defaultGroup1", + "expandable": true + } + ], + "label": "Adhoc Variable", + "hide": "dontHide", + "skipUrlSync": false, + "description": "An adhoc variable", + "allowCustomValue": true + } + } + ] + }, + "status": {} + } } } } \ No newline at end of file diff --git a/apps/dashboard/pkg/migration/conversion/testdata/output/v2alpha1.viz-config.v0alpha1.json b/apps/dashboard/pkg/migration/conversion/testdata/output/v2alpha1.viz-config.v0alpha1.json index 86e7f731622..acde79a0bcc 100644 --- a/apps/dashboard/pkg/migration/conversion/testdata/output/v2alpha1.viz-config.v0alpha1.json +++ b/apps/dashboard/pkg/migration/conversion/testdata/output/v2alpha1.viz-config.v0alpha1.json @@ -9,7 +9,219 @@ "conversion": { "failed": true, "error": "backend conversion not yet implemented", - "storedVersion": "v2alpha1" + "storedVersion": "v2alpha1", + "source": { + "kind": "Dashboard", + "apiVersion": "dashboard.grafana.app/v2alpha1", + "metadata": { + "name": "test-v2alpha1-viz-config" + }, + "spec": { + "annotations": null, + "cursorSync": "", + "elements": { + "panel-1": { + "kind": "Panel", + "spec": { + "id": 1, + "title": "Simple timeseries (WITH DS REF)", + "description": "", + "links": [], + "data": { + "kind": "QueryGroup", + "spec": { + "queries": [ + { + "kind": "PanelQuery", + "spec": { + "query": { + "kind": "grafana-testdata-datasource", + "spec": { + "scenarioId": "random_walk", + "seriesCount": 3 + } + }, + "datasource": { + "uid": "gdev-testdata" + }, + "refId": "A", + "hidden": false + } + } + ], + "transformations": [], + "queryOptions": {} + } + }, + "vizConfig": { + "kind": "timeseries", + "spec": { + "pluginVersion": "12.1.0-pre", + "options": { + "legend": { + "calcs": [], + "displayMode": "list", + "placement": "bottom", + "showLegend": true + }, + "tooltip": { + "hideZeros": false, + "mode": "single", + "sort": "none" + } + }, + "fieldConfig": { + "defaults": { + "thresholds": { + "mode": "absolute", + "steps": [ + { + "value": 0, + "color": "green" + }, + { + "value": 80, + "color": "red" + } + ] + }, + "color": { + "mode": "palette-classic" + }, + "custom": { + "axisBorderShow": false, + "axisCenteredZero": false, + "axisColorMode": "text", + "axisLabel": "", + "axisPlacement": "auto", + "barAlignment": 0, + "barWidthFactor": 0.6, + "drawStyle": "line", + "fillOpacity": 0, + "gradientMode": "none", + "hideFrom": { + "legend": false, + "tooltip": false, + "viz": false + }, + "insertNulls": false, + "lineInterpolation": "linear", + "lineWidth": 1, + "pointSize": 5, + "scaleDistribution": { + "type": "linear" + }, + "showPoints": "auto", + "spanNulls": false, + "stacking": { + "group": "A", + "mode": "none" + }, + "thresholdsStyle": { + "mode": "off" + } + } + }, + "overrides": [] + } + } + } + } + } + }, + "layout": { + "kind": "AutoGridLayout", + "spec": { + "maxColumnCount": 3, + "columnWidthMode": "standard", + "rowHeightMode": "standard", + "items": [ + { + "kind": "AutoGridLayoutItem", + "spec": { + "element": { + "kind": "ElementReference", + "name": "panel-3" + } + } + }, + { + "kind": "AutoGridLayoutItem", + "spec": { + "element": { + "kind": "ElementReference", + "name": "panel-4" + } + } + }, + { + "kind": "AutoGridLayoutItem", + "spec": { + "element": { + "kind": "ElementReference", + "name": "panel-2" + } + } + }, + { + "kind": "AutoGridLayoutItem", + "spec": { + "element": { + "kind": "ElementReference", + "name": "panel-1" + } + } + }, + { + "kind": "AutoGridLayoutItem", + "spec": { + "element": { + "kind": "ElementReference", + "name": "panel-5" + } + } + }, + { + "kind": "AutoGridLayoutItem", + "spec": { + "element": { + "kind": "ElementReference", + "name": "panel-6" + } + } + } + ] + } + }, + "links": [], + "liveNow": false, + "preload": false, + "tags": [], + "timeSettings": { + "timezone": "browser", + "from": "now-6h", + "to": "now", + "autoRefresh": "", + "autoRefreshIntervals": [ + "5s", + "10s", + "30s", + "1m", + "5m", + "15m", + "30m", + "1h", + "2h", + "1d" + ], + "hideTimepicker": false, + "fiscalYearStartMonth": 0 + }, + "title": "Test: V2alpha1 dashboard with viz config", + "variables": null + }, + "status": {} + } } } } \ No newline at end of file diff --git a/apps/dashboard/pkg/migration/conversion/testdata/output/v2alpha1.viz-config.v1beta1.json b/apps/dashboard/pkg/migration/conversion/testdata/output/v2alpha1.viz-config.v1beta1.json index cc538fe2f92..e0812fa1a2b 100644 --- a/apps/dashboard/pkg/migration/conversion/testdata/output/v2alpha1.viz-config.v1beta1.json +++ b/apps/dashboard/pkg/migration/conversion/testdata/output/v2alpha1.viz-config.v1beta1.json @@ -9,7 +9,219 @@ "conversion": { "failed": true, "error": "backend conversion not yet implemented", - "storedVersion": "v2alpha1" + "storedVersion": "v2alpha1", + "source": { + "kind": "Dashboard", + "apiVersion": "dashboard.grafana.app/v2alpha1", + "metadata": { + "name": "test-v2alpha1-viz-config" + }, + "spec": { + "annotations": null, + "cursorSync": "", + "elements": { + "panel-1": { + "kind": "Panel", + "spec": { + "id": 1, + "title": "Simple timeseries (WITH DS REF)", + "description": "", + "links": [], + "data": { + "kind": "QueryGroup", + "spec": { + "queries": [ + { + "kind": "PanelQuery", + "spec": { + "query": { + "kind": "grafana-testdata-datasource", + "spec": { + "scenarioId": "random_walk", + "seriesCount": 3 + } + }, + "datasource": { + "uid": "gdev-testdata" + }, + "refId": "A", + "hidden": false + } + } + ], + "transformations": [], + "queryOptions": {} + } + }, + "vizConfig": { + "kind": "timeseries", + "spec": { + "pluginVersion": "12.1.0-pre", + "options": { + "legend": { + "calcs": [], + "displayMode": "list", + "placement": "bottom", + "showLegend": true + }, + "tooltip": { + "hideZeros": false, + "mode": "single", + "sort": "none" + } + }, + "fieldConfig": { + "defaults": { + "thresholds": { + "mode": "absolute", + "steps": [ + { + "value": 0, + "color": "green" + }, + { + "value": 80, + "color": "red" + } + ] + }, + "color": { + "mode": "palette-classic" + }, + "custom": { + "axisBorderShow": false, + "axisCenteredZero": false, + "axisColorMode": "text", + "axisLabel": "", + "axisPlacement": "auto", + "barAlignment": 0, + "barWidthFactor": 0.6, + "drawStyle": "line", + "fillOpacity": 0, + "gradientMode": "none", + "hideFrom": { + "legend": false, + "tooltip": false, + "viz": false + }, + "insertNulls": false, + "lineInterpolation": "linear", + "lineWidth": 1, + "pointSize": 5, + "scaleDistribution": { + "type": "linear" + }, + "showPoints": "auto", + "spanNulls": false, + "stacking": { + "group": "A", + "mode": "none" + }, + "thresholdsStyle": { + "mode": "off" + } + } + }, + "overrides": [] + } + } + } + } + } + }, + "layout": { + "kind": "AutoGridLayout", + "spec": { + "maxColumnCount": 3, + "columnWidthMode": "standard", + "rowHeightMode": "standard", + "items": [ + { + "kind": "AutoGridLayoutItem", + "spec": { + "element": { + "kind": "ElementReference", + "name": "panel-3" + } + } + }, + { + "kind": "AutoGridLayoutItem", + "spec": { + "element": { + "kind": "ElementReference", + "name": "panel-4" + } + } + }, + { + "kind": "AutoGridLayoutItem", + "spec": { + "element": { + "kind": "ElementReference", + "name": "panel-2" + } + } + }, + { + "kind": "AutoGridLayoutItem", + "spec": { + "element": { + "kind": "ElementReference", + "name": "panel-1" + } + } + }, + { + "kind": "AutoGridLayoutItem", + "spec": { + "element": { + "kind": "ElementReference", + "name": "panel-5" + } + } + }, + { + "kind": "AutoGridLayoutItem", + "spec": { + "element": { + "kind": "ElementReference", + "name": "panel-6" + } + } + } + ] + } + }, + "links": [], + "liveNow": false, + "preload": false, + "tags": [], + "timeSettings": { + "timezone": "browser", + "from": "now-6h", + "to": "now", + "autoRefresh": "", + "autoRefreshIntervals": [ + "5s", + "10s", + "30s", + "1m", + "5m", + "15m", + "30m", + "1h", + "2h", + "1d" + ], + "hideTimepicker": false, + "fiscalYearStartMonth": 0 + }, + "title": "Test: V2alpha1 dashboard with viz config", + "variables": null + }, + "status": {} + } } } } \ No newline at end of file diff --git a/apps/dashboard/pkg/migration/conversion/v0.go b/apps/dashboard/pkg/migration/conversion/v0.go index a1ccc0e5a6c..9eb42167072 100644 --- a/apps/dashboard/pkg/migration/conversion/v0.go +++ b/apps/dashboard/pkg/migration/conversion/v0.go @@ -78,6 +78,7 @@ func Convert_V0_to_V2alpha1(in *dashv0.Dashboard, out *dashv2alpha1.Dashboard, s StoredVersion: ptr.To(dashv0.VERSION), Failed: true, Error: ptr.To("backend conversion not yet implemented"), + Source: in, }, } @@ -94,6 +95,7 @@ func Convert_V0_to_V2beta1(in *dashv0.Dashboard, out *dashv2beta1.Dashboard, sco StoredVersion: ptr.To(dashv0.VERSION), Failed: true, Error: ptr.To("backend conversion not yet implemented"), + Source: in, }, } diff --git a/apps/dashboard/pkg/migration/conversion/v1.go b/apps/dashboard/pkg/migration/conversion/v1.go index 2dd51ffff03..86b3827af27 100644 --- a/apps/dashboard/pkg/migration/conversion/v1.go +++ b/apps/dashboard/pkg/migration/conversion/v1.go @@ -49,6 +49,7 @@ func Convert_V1_to_V2alpha1(in *dashv1.Dashboard, out *dashv2alpha1.Dashboard, s StoredVersion: ptr.To(dashv1.VERSION), Failed: true, Error: ptr.To("backend conversion not yet implemented"), + Source: in, }, } @@ -65,6 +66,7 @@ func Convert_V1_to_V2beta1(in *dashv1.Dashboard, out *dashv2beta1.Dashboard, sco StoredVersion: ptr.To(dashv1.VERSION), Failed: true, Error: ptr.To("backend conversion not yet implemented"), + Source: in, }, } diff --git a/apps/dashboard/pkg/migration/conversion/v2.go b/apps/dashboard/pkg/migration/conversion/v2.go index a7dfb9f39fd..91fcd8cf07a 100644 --- a/apps/dashboard/pkg/migration/conversion/v2.go +++ b/apps/dashboard/pkg/migration/conversion/v2.go @@ -20,6 +20,7 @@ func Convert_V2alpha1_to_V0(in *dashv2alpha1.Dashboard, out *dashv0.Dashboard, s StoredVersion: ptr.To(dashv2alpha1.VERSION), Failed: true, Error: ptr.To("backend conversion not yet implemented"), + Source: in, }, } @@ -36,6 +37,7 @@ func Convert_V2alpha1_to_V1(in *dashv2alpha1.Dashboard, out *dashv1.Dashboard, s StoredVersion: ptr.To(dashv2alpha1.VERSION), Failed: true, Error: ptr.To("backend conversion not yet implemented"), + Source: in, }, } @@ -52,6 +54,7 @@ func Convert_V2alpha1_to_V2beta1(in *dashv2alpha1.Dashboard, out *dashv2beta1.Da StoredVersion: ptr.To(dashv2alpha1.VERSION), Failed: true, Error: ptr.To(err.Error()), + Source: in, }, } @@ -79,6 +82,7 @@ func Convert_V2beta1_to_V0(in *dashv2beta1.Dashboard, out *dashv0.Dashboard, sco StoredVersion: ptr.To(dashv2beta1.VERSION), Failed: true, Error: ptr.To("backend conversion not yet implemented"), + Source: in, }, } @@ -95,6 +99,7 @@ func Convert_V2beta1_to_V1(in *dashv2beta1.Dashboard, out *dashv1.Dashboard, sco StoredVersion: ptr.To(dashv2beta1.VERSION), Failed: true, Error: ptr.To("backend conversion not yet implemented"), + Source: in, }, } @@ -111,6 +116,7 @@ func Convert_V2beta1_to_V2alpha1(in *dashv2beta1.Dashboard, out *dashv2alpha1.Da StoredVersion: ptr.To(dashv2beta1.VERSION), Failed: true, Error: ptr.To("backend conversion not yet implemented"), + Source: in, }, } diff --git a/packages/grafana-schema/src/schema/dashboard/v0alpha1/types.status.gen.ts b/packages/grafana-schema/src/schema/dashboard/v0alpha1/types.status.gen.ts index 91bf7909fd3..978b71f39b3 100644 --- a/packages/grafana-schema/src/schema/dashboard/v0alpha1/types.status.gen.ts +++ b/packages/grafana-schema/src/schema/dashboard/v0alpha1/types.status.gen.ts @@ -12,6 +12,8 @@ export interface ConversionStatus { // The version which was stored when the dashboard was created / updated. // Fetching this version should always succeed. storedVersion?: string; + // The original value map[string]any + source?: any; } export const defaultConversionStatus = (): ConversionStatus => ({ diff --git a/packages/grafana-schema/src/schema/dashboard/v1beta1/types.status.gen.ts b/packages/grafana-schema/src/schema/dashboard/v1beta1/types.status.gen.ts index 91bf7909fd3..978b71f39b3 100644 --- a/packages/grafana-schema/src/schema/dashboard/v1beta1/types.status.gen.ts +++ b/packages/grafana-schema/src/schema/dashboard/v1beta1/types.status.gen.ts @@ -12,6 +12,8 @@ export interface ConversionStatus { // The version which was stored when the dashboard was created / updated. // Fetching this version should always succeed. storedVersion?: string; + // The original value map[string]any + source?: any; } export const defaultConversionStatus = (): ConversionStatus => ({ diff --git a/packages/grafana-schema/src/schema/dashboard/v2alpha1/types.status.gen.ts b/packages/grafana-schema/src/schema/dashboard/v2alpha1/types.status.gen.ts index 91bf7909fd3..978b71f39b3 100644 --- a/packages/grafana-schema/src/schema/dashboard/v2alpha1/types.status.gen.ts +++ b/packages/grafana-schema/src/schema/dashboard/v2alpha1/types.status.gen.ts @@ -12,6 +12,8 @@ export interface ConversionStatus { // The version which was stored when the dashboard was created / updated. // Fetching this version should always succeed. storedVersion?: string; + // The original value map[string]any + source?: any; } export const defaultConversionStatus = (): ConversionStatus => ({ diff --git a/packages/grafana-schema/src/schema/dashboard/v2beta1/types.status.gen.ts b/packages/grafana-schema/src/schema/dashboard/v2beta1/types.status.gen.ts index 91bf7909fd3..978b71f39b3 100644 --- a/packages/grafana-schema/src/schema/dashboard/v2beta1/types.status.gen.ts +++ b/packages/grafana-schema/src/schema/dashboard/v2beta1/types.status.gen.ts @@ -12,6 +12,8 @@ export interface ConversionStatus { // The version which was stored when the dashboard was created / updated. // Fetching this version should always succeed. storedVersion?: string; + // The original value map[string]any + source?: any; } export const defaultConversionStatus = (): ConversionStatus => ({ diff --git a/pkg/tests/apis/openapi_snapshots/dashboard.grafana.app-v0alpha1.json b/pkg/tests/apis/openapi_snapshots/dashboard.grafana.app-v0alpha1.json index c9ba9823986..e68f94dda3e 100644 --- a/pkg/tests/apis/openapi_snapshots/dashboard.grafana.app-v0alpha1.json +++ b/pkg/tests/apis/openapi_snapshots/dashboard.grafana.app-v0alpha1.json @@ -2381,6 +2381,10 @@ "type": "boolean", "default": false }, + "source": { + "description": "The original value map[string]any", + "type": "object" + }, "storedVersion": { "description": "The version which was stored when the dashboard was created / updated. Fetching this version should always succeed.", "type": "string" diff --git a/pkg/tests/apis/openapi_snapshots/dashboard.grafana.app-v1beta1.json b/pkg/tests/apis/openapi_snapshots/dashboard.grafana.app-v1beta1.json index f025701ec73..4ae094ff867 100644 --- a/pkg/tests/apis/openapi_snapshots/dashboard.grafana.app-v1beta1.json +++ b/pkg/tests/apis/openapi_snapshots/dashboard.grafana.app-v1beta1.json @@ -1075,6 +1075,10 @@ "type": "boolean", "default": false }, + "source": { + "description": "The original value map[string]any", + "type": "object" + }, "storedVersion": { "description": "The version which was stored when the dashboard was created / updated. Fetching this version should always succeed.", "type": "string" diff --git a/pkg/tests/apis/openapi_snapshots/dashboard.grafana.app-v2alpha1.json b/pkg/tests/apis/openapi_snapshots/dashboard.grafana.app-v2alpha1.json index ddc33887775..d8962a72a5d 100644 --- a/pkg/tests/apis/openapi_snapshots/dashboard.grafana.app-v2alpha1.json +++ b/pkg/tests/apis/openapi_snapshots/dashboard.grafana.app-v2alpha1.json @@ -1732,6 +1732,10 @@ "type": "boolean", "default": false }, + "source": { + "description": "The original value map[string]any", + "type": "object" + }, "storedVersion": { "description": "The version which was stored when the dashboard was created / updated. Fetching this version should always succeed.", "type": "string" From 6059ef3d83a235ed47f5d81e5663e662e7c9b340 Mon Sep 17 00:00:00 2001 From: Ihor Yeromin Date: Fri, 10 Oct 2025 14:51:45 +0200 Subject: [PATCH 067/112] Fix: Sorting with sparse arrays containing empty values (#112114) --- .../src/dataframe/processDataFrame.test.ts | 12 ++++++++++++ .../src/dataframe/processDataFrame.ts | 16 +++++++++------- 2 files changed, 21 insertions(+), 7 deletions(-) diff --git a/packages/grafana-data/src/dataframe/processDataFrame.test.ts b/packages/grafana-data/src/dataframe/processDataFrame.test.ts index 72b3041d87c..5c7593e91c0 100644 --- a/packages/grafana-data/src/dataframe/processDataFrame.test.ts +++ b/packages/grafana-data/src/dataframe/processDataFrame.test.ts @@ -365,6 +365,7 @@ describe('sorted DataFrame', () => { { name: 'fourth', type: FieldType.time, values: [1, 2, 3], nanos: [10, 20, 30] }, ], }); + it('Should sort numbers', () => { const sorted = sortDataFrame(frame, 0, true); expect(sorted.length).toEqual(3); @@ -386,6 +387,17 @@ describe('sorted DataFrame', () => { expect(sorted.fields[3].values).toEqual([3, 2, 1]); expect(sorted.fields[3].nanos).toEqual([30, 20, 10]); }); + + it('Should handle arrays with empty values correctly', () => { + // Create a sparse array with empty slots (undefined values) + const values = ['502', '502', , '500', '500', '200', '404']; // Note the empty slot at index 2 + const frame = toDataFrame({ + fields: [{ name: 'status', type: FieldType.string, values }], + }); + const sorted = sortDataFrame(frame, 0, false); + + expect(sorted.fields[0].values).toEqual(['200', '404', '500', '500', '502', '502', undefined]); + }); }); describe('sorted DataFrame by nanos', () => { diff --git a/packages/grafana-data/src/dataframe/processDataFrame.ts b/packages/grafana-data/src/dataframe/processDataFrame.ts index 4a028b6b4fd..14621547725 100644 --- a/packages/grafana-data/src/dataframe/processDataFrame.ts +++ b/packages/grafana-data/src/dataframe/processDataFrame.ts @@ -429,18 +429,20 @@ export function sortDataFrame(data: DataFrame, sortIndex?: number, reverse = fal return { ...data, - fields: data.fields.map((f) => { - const newF = { - ...f, - values: f.values.map((v, i) => f.values[index[i]]), + fields: data.fields.map((field) => { + const newValues = Array.from({ length: field.values.length }, (_, i) => field.values[index[i]]); + + const newField = { + ...field, + values: newValues, }; // only add .nanos if it exists - const { nanos } = f; + const { nanos } = field; if (nanos !== undefined) { - newF.nanos = nanos.map((n, i) => nanos[index[i]]); + newField.nanos = Array.from({ length: nanos.length }, (_, i) => nanos[index[i]]); } - return newF; + return newField; }), }; } From cb9118627693d41bcffdc4558a416c06a4ff0935 Mon Sep 17 00:00:00 2001 From: Bogdan Matei Date: Fri, 10 Oct 2025 16:20:48 +0300 Subject: [PATCH 068/112] GenAI: Fix uncaught error when panel title is missing (#112277) --- public/app/features/dashboard/components/GenAI/utils.ts | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/public/app/features/dashboard/components/GenAI/utils.ts b/public/app/features/dashboard/components/GenAI/utils.ts index ffc3740b57c..ea1a8fe425b 100644 --- a/public/app/features/dashboard/components/GenAI/utils.ts +++ b/public/app/features/dashboard/components/GenAI/utils.ts @@ -164,9 +164,7 @@ export const DASHBOARD_NEED_PANEL_TITLES_AND_DESCRIPTIONS_MESSAGE = export function getPanelStrings(dashboard: DashboardModel): string[] { const panelStrings = dashboard.panels .filter( - (panel) => - (panel.title.length > 0 && panel.title !== NEW_PANEL_TITLE) || - (panel.description && panel.description.length > 0) + (panel) => (panel.title && panel.title !== NEW_PANEL_TITLE) || (panel.description && panel.description.length > 0) ) .map(getPanelString); From f4e7dfe82727d1050a47cb522774a73d6d89d619 Mon Sep 17 00:00:00 2001 From: Bogdan Matei Date: Fri, 10 Oct 2025 16:36:37 +0300 Subject: [PATCH 069/112] Dashboard: Improve static options editors on variables (#110831) --- .../dashboards-edit-custom-variables.spec.ts | 212 ++++++++++++++++++ .../dashboards-edit-variables.spec.ts | 40 ---- eslint-suppressions.json | 13 -- package.json | 4 +- .../src/selectors/pages.ts | 41 ++-- .../components/CustomVariableForm.tsx | 47 +--- .../components/QueryVariableForm.test.tsx | 20 +- .../components/VariableOptionsInput.tsx | 106 --------- .../components/VariableStaticOptionsForm.tsx | 139 ++++++++++++ .../VariableStaticOptionsFormAddButton.tsx | 21 ++ .../VariableStaticOptionsFormItemEditor.tsx | 117 ++++++++++ .../VariableStaticOptionsFormItems.tsx | 117 ++++++++++ .../editors/CustomVariableEditor.tsx | 89 -------- .../CustomVariableEditor.test.tsx | 0 .../CustomVariableEditor.tsx | 83 +++++++ .../CustomVariableEditor/ModalEditor.tsx | 47 ++++ .../editors/CustomVariableEditor/PaneItem.tsx | 37 +++ .../CustomVariableEditor/ValuesBuilder.tsx | 52 +++++ .../CustomVariableEditor/ValuesPreview.tsx | 13 ++ .../getCustomVariableOptions.tsx | 20 ++ .../editors/QueryVariableEditor.test.tsx | 12 +- .../variables/editors/QueryVariableEditor.tsx | 32 ++- .../settings/variables/utils.test.ts | 2 +- .../settings/variables/utils.ts | 6 +- .../query/QueryVariableStaticOptions.tsx | 8 +- public/locales/en-US/grafana.json | 20 +- yarn.lock | 22 +- 27 files changed, 966 insertions(+), 354 deletions(-) create mode 100644 e2e-playwright/dashboard-new-layouts/dashboards-edit-custom-variables.spec.ts delete mode 100644 public/app/features/dashboard-scene/settings/variables/components/VariableOptionsInput.tsx create mode 100644 public/app/features/dashboard-scene/settings/variables/components/VariableStaticOptionsForm.tsx create mode 100644 public/app/features/dashboard-scene/settings/variables/components/VariableStaticOptionsFormAddButton.tsx create mode 100644 public/app/features/dashboard-scene/settings/variables/components/VariableStaticOptionsFormItemEditor.tsx create mode 100644 public/app/features/dashboard-scene/settings/variables/components/VariableStaticOptionsFormItems.tsx delete mode 100644 public/app/features/dashboard-scene/settings/variables/editors/CustomVariableEditor.tsx rename public/app/features/dashboard-scene/settings/variables/editors/{ => CustomVariableEditor}/CustomVariableEditor.test.tsx (100%) create mode 100644 public/app/features/dashboard-scene/settings/variables/editors/CustomVariableEditor/CustomVariableEditor.tsx create mode 100644 public/app/features/dashboard-scene/settings/variables/editors/CustomVariableEditor/ModalEditor.tsx create mode 100644 public/app/features/dashboard-scene/settings/variables/editors/CustomVariableEditor/PaneItem.tsx create mode 100644 public/app/features/dashboard-scene/settings/variables/editors/CustomVariableEditor/ValuesBuilder.tsx create mode 100644 public/app/features/dashboard-scene/settings/variables/editors/CustomVariableEditor/ValuesPreview.tsx create mode 100644 public/app/features/dashboard-scene/settings/variables/editors/CustomVariableEditor/getCustomVariableOptions.tsx diff --git a/e2e-playwright/dashboard-new-layouts/dashboards-edit-custom-variables.spec.ts b/e2e-playwright/dashboard-new-layouts/dashboards-edit-custom-variables.spec.ts new file mode 100644 index 00000000000..ce5465c1e33 --- /dev/null +++ b/e2e-playwright/dashboard-new-layouts/dashboards-edit-custom-variables.spec.ts @@ -0,0 +1,212 @@ +import { Locator } from '@playwright/test'; + +import { test, expect, DashboardPage, E2ESelectorGroups } from '@grafana/plugin-e2e'; + +import { flows } from './utils'; + +test.use({ + featureToggles: { + kubernetesDashboards: true, + dashboardNewLayouts: true, + dashboardUndoRedo: true, + groupByVariable: true, + }, +}); + +test.use({ + viewport: { width: 1920, height: 1080 }, +}); + +const PAGE_UNDER_TEST = 'kVi2Gex7z/test-variable-output'; +const DASHBOARD_NAME = 'Test variable output'; + +test.describe( + 'Dashboard edit - Custom variable', + { + tag: ['@dashboards'], + }, + () => { + let addButton: Locator | undefined; + let rows: Locator | undefined; + let valueInputs: Locator | undefined; + let labelInputs: Locator | undefined; + let deleteButtons: Locator | undefined; + + const getAddButton = async (dashboardPage: DashboardPage, selectors: E2ESelectorGroups) => { + addButton = dashboardPage.getByGrafanaSelector( + selectors.pages.Dashboard.Settings.Variables.Edit.StaticOptionsEditor.addButton + ); + await expect(addButton).toBeVisible(); + }; + + const refetchItems = (dashboardPage: DashboardPage, selectors: E2ESelectorGroups) => { + rows = dashboardPage.getByGrafanaSelector( + selectors.pages.Dashboard.Settings.Variables.Edit.StaticOptionsEditor.row + ); + + valueInputs = dashboardPage.getByGrafanaSelector( + selectors.pages.Dashboard.Settings.Variables.Edit.StaticOptionsEditor.valueInput + ); + + labelInputs = dashboardPage.getByGrafanaSelector( + selectors.pages.Dashboard.Settings.Variables.Edit.StaticOptionsEditor.labelInput + ); + + deleteButtons = dashboardPage.getByGrafanaSelector( + selectors.pages.Dashboard.Settings.Variables.Edit.StaticOptionsEditor.deleteButton + ); + }; + + const checkRows = async (length: number) => { + expect(await rows!.all()).toHaveLength(length); + }; + + const fillValue = async (text: string, index: number) => { + await valueInputs!.nth(index).fill(text); + }; + + const fillLabel = async (text: string, index: number) => { + await labelInputs!.nth(index).fill(text); + }; + + const fillLabelValue = async (value: string, label: string, index: number) => { + await fillValue(value, index); + await fillLabel(label, index); + }; + + const openModal = async (dashboardPage: DashboardPage, selectors: E2ESelectorGroups) => { + await dashboardPage + .getByGrafanaSelector(selectors.pages.Dashboard.Settings.Variables.Edit.CustomVariable.optionsOpenButton) + .click(); + + await getAddButton(dashboardPage, selectors); + + refetchItems(dashboardPage, selectors); + }; + + const closeModal = async (dashboardPage: DashboardPage, selectors: E2ESelectorGroups) => { + await dashboardPage + .getByGrafanaSelector(selectors.pages.Dashboard.Settings.Variables.Edit.CustomVariable.closeButton) + .click(); + }; + + const checkItems = async (items: Array<[string, string?]>) => { + for (let i = 0; i < items.length; i++) { + const [value, label] = items[i]; + await expect(valueInputs!.nth(i)).toHaveValue(value); + await expect(labelInputs!.nth(i)).toHaveValue(label ?? ''); + } + }; + + const checkPreview = async (dashboardPage: DashboardPage, selectors: E2ESelectorGroups, labels: string[]) => { + const previewOptions = dashboardPage.getByGrafanaSelector( + selectors.pages.Dashboard.Settings.Variables.Edit.General.previewOfValuesOption + ); + + for (let i = 0; i < labels.length; i++) { + expect(await previewOptions.nth(i).textContent()).toBe(labels[i]); + } + }; + + const addItem = async (dashboardPage: DashboardPage, selectors: E2ESelectorGroups, value = '', label = '') => { + await addButton!.click(); + refetchItems(dashboardPage, selectors); + await fillLabelValue(value ?? '', label ?? '', (await rows!.all()).length - 1); + }; + + const removeItem = async (dashboardPage: DashboardPage, selectors: E2ESelectorGroups, index: number) => { + await deleteButtons!.nth(index).click(); + refetchItems(dashboardPage, selectors); + }; + + test.beforeEach(() => { + valueInputs = undefined; + labelInputs = undefined; + deleteButtons = undefined; + }); + + test('can add a new custom variable', async ({ gotoDashboardPage, selectors, page }) => { + const dashboardPage = await gotoDashboardPage({ uid: PAGE_UNDER_TEST }); + await expect(page.getByText(DASHBOARD_NAME)).toBeVisible(); + + // common steps to add a new variable + await flows.newEditPaneVariableClick(dashboardPage, selectors); + await flows.newEditPanelCommonVariableInputs(dashboardPage, selectors, { + type: 'custom', + name: 'foo', + label: 'Foo', + value: '', + }); + + await openModal(dashboardPage, selectors); + await checkRows(1); + await addItem(dashboardPage, selectors); + await checkRows(2); + await fillValue('first value', 0); + await fillLabelValue('second value', 'second label', 1); + await addItem(dashboardPage, selectors, 'third value', 'third label'); + await addItem(dashboardPage, selectors, 'fourth value', 'fourth value'); + await removeItem(dashboardPage, selectors, 2); + await checkRows(3); + await checkPreview(dashboardPage, selectors, ['first value', 'second label', 'fourth value']); + await closeModal(dashboardPage, selectors); + + // assert variable is visible and has the correct values + const variableLabel = dashboardPage.getByGrafanaSelector( + selectors.pages.Dashboard.SubMenu.submenuItemLabels('Foo') + ); + await expect(variableLabel).toBeVisible(); + await expect(variableLabel).toContainText('Foo'); + await expect( + dashboardPage.getByGrafanaSelector( + selectors.pages.Dashboard.SubMenu.submenuItemValueDropDownValueLinkTexts('first value') + ) + ).toBeVisible(); + + // check that variable deletion works + await dashboardPage.getByGrafanaSelector(selectors.components.EditPaneHeader.deleteButton).click(); + await expect(variableLabel).toBeHidden(); + }); + + test('can edit a custom variable', async ({ gotoDashboardPage, selectors, page }) => { + const dashboardPage = await gotoDashboardPage({ + uid: PAGE_UNDER_TEST, + queryParams: new URLSearchParams({ orgId: '1', editview: 'variables' }), + }); + await expect(page.getByText(DASHBOARD_NAME)).toBeVisible(); + + // Create a custom variable in the dashboard settings page + await dashboardPage.getByGrafanaSelector(selectors.components.CallToActionCard.buttonV2('Add variable')).click(); + const typeSelect = dashboardPage + .getByGrafanaSelector(selectors.pages.Dashboard.Settings.Variables.Edit.General.generalTypeSelectV2) + .locator('input'); + await typeSelect.fill('Custom'); + await typeSelect.press('Enter'); + await dashboardPage + .getByGrafanaSelector(selectors.pages.Dashboard.Settings.Variables.Edit.General.generalNameInputV2) + .fill('foo'); + await dashboardPage + .getByGrafanaSelector(selectors.pages.Dashboard.Settings.Variables.Edit.General.generalLabelInputV2) + .fill('Foo'); + await dashboardPage + .getByGrafanaSelector(selectors.pages.Dashboard.Settings.Variables.Edit.CustomVariable.customValueInput) + .fill('first value, second label : second value, fourth value : fourth value'); + await dashboardPage + .getByGrafanaSelector(selectors.pages.Dashboard.Settings.Variables.Edit.General.applyButton) + .click(); + await dashboardPage + .getByGrafanaSelector(selectors.components.NavToolbar.editDashboard.backToDashboardButton) + .click(); + + // Open the modal editor in the side pane + await dashboardPage.getByGrafanaSelector(selectors.components.PanelEditor.Outline.section).click(); + await dashboardPage.getByGrafanaSelector(selectors.components.PanelEditor.Outline.node('Variables')).click(); + await dashboardPage.getByGrafanaSelector(selectors.components.PanelEditor.Outline.item('foo')).click(); + await openModal(dashboardPage, selectors); + + // Check the items + await checkItems([['first value'], ['second value', 'second label'], ['fourth value']]); + await checkPreview(dashboardPage, selectors, ['first value', 'second label', 'fourth value']); + }); + } +); diff --git a/e2e-playwright/dashboard-new-layouts/dashboards-edit-variables.spec.ts b/e2e-playwright/dashboard-new-layouts/dashboards-edit-variables.spec.ts index 54991dfd623..4b5ea9574f8 100644 --- a/e2e-playwright/dashboard-new-layouts/dashboards-edit-variables.spec.ts +++ b/e2e-playwright/dashboard-new-layouts/dashboards-edit-variables.spec.ts @@ -20,46 +20,6 @@ test.describe( tag: ['@dashboards'], }, () => { - test('can add a new custom variable', async ({ gotoDashboardPage, selectors, page }) => { - const dashboardPage = await gotoDashboardPage({ uid: PAGE_UNDER_TEST }); - await expect(page.getByText(DASHBOARD_NAME)).toBeVisible(); - - const variable: Variable = { - type: 'custom', - name: 'foo', - label: 'Foo', - value: 'one,two,three', - }; - - // common steps to add a new variable - await flows.newEditPaneVariableClick(dashboardPage, selectors); - await flows.newEditPanelCommonVariableInputs(dashboardPage, selectors, variable); - - // set the custom variable value - const customValueInput = dashboardPage.getByGrafanaSelector( - selectors.pages.Dashboard.Settings.Variables.Edit.CustomVariable.customValueInput - ); - await customValueInput.fill(variable.value); - await customValueInput.blur(); - - // assert the dropdown for the variable is visible and has the correct values - const variableLabel = dashboardPage.getByGrafanaSelector( - selectors.pages.Dashboard.SubMenu.submenuItemLabels(variable.label!) - ); - await expect(variableLabel).toBeVisible(); - await expect(variableLabel).toContainText(variable.label!); - - const values = variable.value.split(','); - const firstValueLink = dashboardPage.getByGrafanaSelector( - selectors.pages.Dashboard.SubMenu.submenuItemValueDropDownValueLinkTexts(values[0]) - ); - await expect(firstValueLink).toBeVisible(); - - // check that variable deletion works - await dashboardPage.getByGrafanaSelector(selectors.components.EditPaneHeader.deleteButton).click(); - await expect(variableLabel).toBeHidden(); - }); - test('can add a new constant variable', async ({ gotoDashboardPage, selectors, page }) => { const dashboardPage = await gotoDashboardPage({ uid: PAGE_UNDER_TEST }); await expect(page.getByText(DASHBOARD_NAME)).toBeVisible(); diff --git a/eslint-suppressions.json b/eslint-suppressions.json index 1d3a1535b9b..cb9a72a644b 100644 --- a/eslint-suppressions.json +++ b/eslint-suppressions.json @@ -2189,19 +2189,6 @@ "count": 1 } }, - "public/app/features/dashboard-scene/settings/variables/editors/QueryVariableEditor.tsx": { - "no-restricted-syntax": { - "count": 1 - } - }, - "public/app/features/dashboard-scene/settings/variables/utils.ts": { - "@typescript-eslint/consistent-type-assertions": { - "count": 1 - }, - "@typescript-eslint/no-explicit-any": { - "count": 1 - } - }, "public/app/features/dashboard-scene/sharing/ShareButton/share-externally/EmailShare/ConfigEmailSharing/ConfigEmailSharing.tsx": { "no-restricted-syntax": { "count": 1 diff --git a/package.json b/package.json index d8fe5451d99..bce42eab31e 100644 --- a/package.json +++ b/package.json @@ -296,8 +296,8 @@ "@grafana/plugin-ui": "^0.10.10", "@grafana/prometheus": "workspace:*", "@grafana/runtime": "workspace:*", - "@grafana/scenes": "6.39.3", - "@grafana/scenes-react": "6.39.3", + "@grafana/scenes": "6.39.4", + "@grafana/scenes-react": "6.39.4", "@grafana/schema": "workspace:*", "@grafana/sql": "workspace:*", "@grafana/ui": "workspace:*", diff --git a/packages/grafana-e2e-selectors/src/selectors/pages.ts b/packages/grafana-e2e-selectors/src/selectors/pages.ts index b45357f05f7..7716ccc1436 100644 --- a/packages/grafana-e2e-selectors/src/selectors/pages.ts +++ b/packages/grafana-e2e-selectors/src/selectors/pages.ts @@ -500,24 +500,9 @@ export const versionedPages = { queryOptionsQueryInput: { '10.4.0': 'data-testid Variable editor Form Default Variable Query Editor textarea', }, - queryOptionsStaticOptionsRow: { - [MIN_GRAFANA_VERSION]: 'Variable editor Form Query Static Options row', - }, queryOptionsStaticOptionsToggle: { [MIN_GRAFANA_VERSION]: 'Variable editor Form Query Static Options toggle', }, - queryOptionsStaticOptionsLabelInput: { - [MIN_GRAFANA_VERSION]: 'Variable editor Form Query Static Options Label input', - }, - queryOptionsStaticOptionsValueInput: { - [MIN_GRAFANA_VERSION]: 'Variable editor Form Query Static Options Value input', - }, - queryOptionsStaticOptionsDeleteButton: { - [MIN_GRAFANA_VERSION]: 'Variable editor Form Query Static Options Delete button', - }, - queryOptionsStaticOptionsAddButton: { - [MIN_GRAFANA_VERSION]: 'Variable editor Form Query Static Options Add button', - }, queryOptionsStaticOptionsOrderDropdown: { [MIN_GRAFANA_VERSION]: 'Variable editor Form Query Static Options Order dropdown', }, @@ -559,6 +544,12 @@ export const versionedPages = { customValueInput: { [MIN_GRAFANA_VERSION]: 'data-testid custom-variable-input', }, + optionsOpenButton: { + [MIN_GRAFANA_VERSION]: 'data-testid custom-variable-options-open-button', + }, + closeButton: { + [MIN_GRAFANA_VERSION]: 'data-testid custom-variable-close-button', + }, }, IntervalVariable: { intervalsValueInput: { @@ -607,6 +598,26 @@ export const versionedPages = { ['12.3.0']: 'data-testid switch variable disabled value input', }, }, + StaticOptionsEditor: { + addButton: { + [MIN_GRAFANA_VERSION]: 'data-testid Variable editor Form Static Options Add button', + }, + labelInput: { + [MIN_GRAFANA_VERSION]: 'data-testid Variable editor Form Static Options Label input', + }, + valueInput: { + [MIN_GRAFANA_VERSION]: 'data-testid Variable editor Form Static Options Value input', + }, + moveButton: { + [MIN_GRAFANA_VERSION]: 'data-testid Variable editor Form Static Options Move button', + }, + deleteButton: { + [MIN_GRAFANA_VERSION]: 'data-testid Variable editor Form Static Options Delete button', + }, + row: { + [MIN_GRAFANA_VERSION]: 'data-testid Variable editor Form Static Options Row', + }, + }, }, }, }, diff --git a/public/app/features/dashboard-scene/settings/variables/components/CustomVariableForm.tsx b/public/app/features/dashboard-scene/settings/variables/components/CustomVariableForm.tsx index f11037fb80c..b3c78330156 100644 --- a/public/app/features/dashboard-scene/settings/variables/components/CustomVariableForm.tsx +++ b/public/app/features/dashboard-scene/settings/variables/components/CustomVariableForm.tsx @@ -1,16 +1,11 @@ import { FormEvent } from 'react'; -import { lastValueFrom } from 'rxjs'; import { selectors } from '@grafana/e2e-selectors'; import { Trans, t } from '@grafana/i18n'; -import { CustomVariable, SceneVariable } from '@grafana/scenes'; -import { TextArea } from '@grafana/ui'; -import { OptionsPaneItemDescriptor } from 'app/features/dashboard/components/PanelEditor/OptionsPaneItemDescriptor'; - -import { VariableLegend } from '../components/VariableLegend'; -import { VariableTextAreaField } from '../components/VariableTextAreaField'; import { SelectionOptionsForm } from './SelectionOptionsForm'; +import { VariableLegend } from './VariableLegend'; +import { VariableTextAreaField } from './VariableTextAreaField'; interface CustomVariableFormProps { query: string; @@ -71,41 +66,3 @@ export function CustomVariableForm({ ); } - -export function getCustomVariableOptions(variable: SceneVariable): OptionsPaneItemDescriptor[] { - if (!(variable instanceof CustomVariable)) { - return []; - } - - return [ - new OptionsPaneItemDescriptor({ - title: t('dashboard.edit-pane.variable.custom-options.values', 'Values separated by comma'), - id: 'custom-variable-values', - render: (descriptor) => , - }), - ]; -} - -function ValuesTextField({ variable, id }: { variable: CustomVariable; id?: string }) { - const { query } = variable.useState(); - - const onBlur = async (event: FormEvent) => { - variable.setState({ query: event.currentTarget.value }); - await lastValueFrom(variable.validateAndUpdate!()); - }; - - return ( -
name